Autenticación nativa en Streamlit 1.41+
Desde Streamlit 1.41, la plataforma incluye soporte nativo de autenticación OAuth sin necesidad de librerías externas como streamlit-authenticator. Los componentes principales son:
st.login(provider)- botón/flujo de inicio de sesión con un proveedor OAuthst.logout()- cierra la sesión del usuario actualst.experimental_user- objeto con los datos del usuario autenticado
sequenceDiagram
participant U as Usuario
participant App as App Streamlit
participant Auth as Provider OAuth Google GitHub
participant Cookie as Cookie firmada
U->>App: Visita página protegida
App->>App: st.experimental_user.is_logged_in?
alt No autenticado
App->>U: Muestra st.login provider
U->>Auth: Autoriza con credenciales
Auth->>App: redirect_uri con token
App->>Cookie: Crea cookie firmada
Cookie->>U: Guarda en navegador
else Autenticado
App->>U: Sirve contenido protegido
U->>App: Click st.logout
App->>Cookie: Elimina cookie
end
Note over App,Cookie: cookie_secret en secrets.toml
Configurar el proveedor OAuth en secrets.toml
Antes de usar st.login, configura las credenciales del proveedor en .streamlit/secrets.toml:
# .streamlit/secrets.toml
[auth]
redirect_uri = "http://localhost:8501/oauth2callback"
cookie_secret = "una_clave_secreta_larga_y_aleatoria_min_32_chars"
[auth.google]
client_id = "TU_CLIENT_ID.apps.googleusercontent.com"
client_secret = "TU_CLIENT_SECRET"
server_metadata_url = "https://accounts.google.com/.well-known/openid-configuration"
[auth.github]
client_id = "TU_GITHUB_CLIENT_ID"
client_secret = "TU_GITHUB_CLIENT_SECRET"
server_metadata_url = "https://token.actions.githubusercontent.com/.well-known/openid-configuration"
En Streamlit Community Cloud, estos secretos se añaden en el panel Secrets del proyecto (ver tutorial de despliegue).
st.login: botón de inicio de sesión
import streamlit as st
st.title("Bienvenido a la aplicación")
if not st.experimental_user.is_logged_in:
st.info("Inicia sesión para acceder al contenido.")
col1, col2 = st.columns(2)
with col1:
if st.button("Entrar con Google", icon="🔑", type="primary"):
st.login("google")
with col2:
if st.button("Entrar con GitHub", icon="🐙"):
st.login("github")
else:
st.success(f"¡Hola, {st.experimental_user.name}!")
También puedes llamar a st.login() directamente sin botón para redirigir de inmediato al proveedor:
import streamlit as st
if not st.experimental_user.is_logged_in:
st.login("google") # Redirige automáticamente al flujo OAuth de Google
st.experimental_user: datos del usuario autenticado
Una vez autenticado, st.experimental_user expone los atributos del usuario devueltos por el proveedor:
import streamlit as st
usuario = st.experimental_user
if usuario.is_logged_in:
col_avatar, col_info = st.columns([1, 4])
with col_avatar:
if usuario.picture:
st.image(usuario.picture, width=80)
with col_info:
st.subheader(usuario.name or "Usuario")
st.caption(usuario.email or "Sin email")
st.write("**Datos del proveedor OAuth:**")
st.json({
"name": usuario.name,
"email": usuario.email,
"email_verified": usuario.email_verified,
"picture": usuario.picture,
"sub": usuario.sub, # Identificador único del proveedor
})
Atributos estándar de st.experimental_user:
| Atributo | Descripción |
|----------|-------------|
| is_logged_in | True si el usuario está autenticado |
| name | Nombre completo del usuario |
| email | Dirección de correo |
| email_verified | Si el email fue verificado por el proveedor |
| picture | URL del avatar del usuario |
| sub | ID único del usuario en el proveedor OAuth |
st.logout: cerrar sesión
import streamlit as st
if st.experimental_user.is_logged_in:
st.write(f"Sesión activa: **{st.experimental_user.email}**")
if st.button("Cerrar sesión", icon="🚪"):
st.logout()
st.logout() invalida la sesión y redirige al usuario a la página de inicio de la aplicación.
Proteger páginas completas con guard pattern
El patrón habitual es verificar is_logged_in al inicio de cada página protegida:
# pages/dashboard.py
import streamlit as st
# Guard de autenticación
if not st.experimental_user.is_logged_in:
st.warning("Debes iniciar sesión para acceder a esta página.")
if st.button("Iniciar sesión"):
st.login("google")
st.stop() # Detener la ejecución del resto de la página
# Contenido protegido
st.title("📊 Dashboard privado")
st.write(f"Bienvenido, {st.experimental_user.name}")
Integración con st.navigation
# app.py
import streamlit as st
login_page = st.Page("pages/login.py", title="Iniciar sesión", icon="🔑")
inicio = st.Page("pages/inicio.py", title="Inicio", icon="🏠", default=True)
dashboard = st.Page("pages/dashboard.py", title="Dashboard", icon="📊")
perfil = st.Page("pages/perfil.py", title="Mi perfil", icon="👤")
admin = st.Page("pages/admin.py", title="Admin", icon="🔧")
# Controlar qué páginas son visibles según el estado de autenticación
if not st.experimental_user.is_logged_in:
pg = st.navigation([login_page])
elif st.experimental_user.email in ["admin@empresa.com", "cto@empresa.com"]:
# Usuarios administradores: acceso completo
pg = st.navigation({
"Principal": [inicio, dashboard],
"Cuenta": [perfil],
"Sistema": [admin]
})
else:
# Usuarios estándar: sin acceso a admin
pg = st.navigation({
"Principal": [inicio, dashboard],
"Cuenta": [perfil]
})
pg.run()
# pages/login.py
import streamlit as st
st.title("🔑 Acceso a la aplicación")
st.write("Inicia sesión con tu cuenta corporativa para continuar.")
if st.button("Entrar con Google", type="primary", use_container_width=True):
st.login("google")
Aplicación completa con autenticación OAuth
# app.py
import streamlit as st
st.set_page_config(page_title="Mi App Segura", page_icon="🔐", layout="wide")
# Definir páginas
login_page = st.Page("pages/login.py", title="Inicio de sesión", icon="🔑")
home = st.Page("pages/home.py", title="Inicio", icon="🏠", default=True)
analytics = st.Page("pages/analytics.py", title="Analítica", icon="📈")
settings = st.Page("pages/settings.py", title="Ajustes", icon="⚙️")
# Navegación condicional
if st.experimental_user.is_logged_in:
with st.sidebar:
st.image(st.experimental_user.picture or "https://via.placeholder.com/50", width=50)
st.caption(st.experimental_user.email)
if st.button("Cerrar sesión", icon="🚪"):
st.logout()
pg = st.navigation({
"Aplicación": [home, analytics],
"Sistema": [settings]
})
else:
pg = st.navigation([login_page])
pg.run()
# pages/home.py
import streamlit as st
if not st.experimental_user.is_logged_in:
st.switch_page("pages/login.py")
st.title(f"Hola, {st.experimental_user.name} 👋")
st.write("Selecciona una sección en el menú lateral para comenzar.")
col1, col2 = st.columns(2)
with col1:
with st.container(border=True):
st.metric("Sesiones hoy", "1.204", "+12%")
with col2:
with st.container(border=True):
st.metric("Usuarios activos", "342", "+5%")
Contexto: el antes y el después de la autenticación nativa
Durante años, la forma habitual de añadir autenticación a una app Streamlit fue integrar streamlit-authenticator, una biblioteca de terceros que gestionaba usuarios, hashes de contraseñas y cookies de sesión. Ese enfoque funcionaba, pero tenía varios inconvenientes: había que mantener una base de datos propia de usuarios, gestionar el hashing de contraseñas manualmente y confiar en una dependencia no oficial para un aspecto crítico como la seguridad.
Desde Streamlit 1.41, el equipo oficial proporciona st.login y st.logout como primitivas nativas integradas con el framework OAuth 2.0 / OpenID Connect. Esto permite delegar la autenticación a proveedores establecidos (Google, GitHub, Microsoft, Auth0...) sin necesidad de gestionar contraseñas. La ventaja es doble: menos código propio y una superficie de ataque mucho menor, ya que no almacenas credenciales de usuario en tu infraestructura.
Explicación línea por línea del flujo OAuth
- El usuario llega a la app y Streamlit comprueba
st.experimental_user.is_logged_in. Si esFalse, muestra el botón de login. - Al pulsar el botón,
st.login("google")redirige al navegador a la URL de autorización de Google con elclient_idconfigurado ensecrets.toml. - El usuario se autentica en Google y concede permisos. Google redirige al
redirect_uriconfigurado (por ejemplo,http://localhost:8501/oauth2callback) con un código de autorización. - Streamlit intercambia ese código por un token de acceso usando el
client_secrety obtiene los datos del usuario (nombre, email, avatar). - Estos datos se almacenan en una cookie firmada con
cookie_secretpara mantener la sesión entre recargas. - A partir de ahí,
st.experimental_user.is_logged_indevuelveTruey el resto del script puede acceder a los atributos del usuario.
Tabla de configuración de secrets.toml
| Sección | Clave | Descripción |
|---------|-------|-------------|
| [auth] | redirect_uri | URL de callback de OAuth (debe coincidir con la configurada en el proveedor) |
| [auth] | cookie_secret | Clave secreta aleatoria de al menos 32 caracteres para firmar cookies |
| [auth.google] | client_id | ID de cliente OAuth de Google Cloud Console |
| [auth.google] | client_secret | Secreto del cliente OAuth |
| [auth.google] | server_metadata_url | URL del documento de configuración OIDC |
| [auth.github] | client_id | ID de la OAuth App de GitHub |
Errores comunes
redirect_uri no coincide. El proveedor OAuth rechazará la petición si la URL de callback configurada en su panel no coincide exactamente con la de secrets.toml. Asegúrate de incluir el puerto (:8501) en local y la URL completa en producción.
cookie_secret débil o reutilizada. Esta clave firma las cookies de sesión. Si es corta o está hardcodeada en un repositorio público, cualquiera puede falsificar sesiones. Genera una clave aleatoria con python -c "import secrets; print(secrets.token_urlsafe(32))".
Olvido del st.stop() tras el guard. Si compruebas is_logged_in al principio de una página pero no llamas a st.stop(), el código posterior se seguirá ejecutando y el usuario no autenticado verá contenido protegido.
Atributos de usuario distintos según el proveedor. Google devuelve picture pero GitHub devuelve avatar_url. Si combinas varios proveedores, normaliza los atributos con una función helper antes de usarlos en la UI.
Localhost vs. HTTPS en producción. Muchos proveedores OAuth exigen HTTPS en URLs de callback de producción. Si despliegas en Community Cloud o un dominio personalizado, recuerda actualizar redirect_uri en ambos sitios (el panel del proveedor y secrets.toml).
Mejores prácticas
- Usa un proveedor OAuth establecido (Google, GitHub, Microsoft) en lugar de gestionar contraseñas propias.
- Implementa el patrón "guard" al principio de cada página protegida: comprueba
is_logged_iny usast.stop()si no lo está. - Controla la autorización (qué usuarios pueden acceder a qué páginas) con listas blancas basadas en email o dominio, no solo con el hecho de estar autenticado.
- Muestra el avatar y el email del usuario en el sidebar con un botón de logout claramente visible.
- Para aplicaciones B2B, considera restringir el acceso al dominio corporativo comprobando
usuario.email.endswith("@empresa.com"). - No guardes datos sensibles del usuario en
st.session_statemás allá de lo necesario: Streamlit los mantiene en memoria del servidor y podrían filtrarse si hay un bug de aislamiento entre sesiones.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Streamlit es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Streamlit
Explora más contenido relacionado con Streamlit y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Configurar un proveedor OAuth en secrets.toml para autenticación con Google o GitHub. Mostrar el botón de inicio de sesión con st.login y cerrar sesión con st.logout. Acceder a los datos del usuario autenticado mediante st.experimental_user. Proteger páginas verificando st.experimental_user.is_logged_in. Combinar la autenticación nativa con st.navigation para controlar el acceso por página.