Autenticación con Keycloak¶
Los usuarios viven en Keycloak. La API valida los tokens en local, contra las claves públicas del realm: en una petición normal no habla con Keycloak.
Montarla¶
pip install 'tai-api[rbac]'
tai-api auth init # elige Keycloak: configura el realm main
tai-api rbac push # crea el realm si no existe, y sus permisos y roles
Para levantar Keycloak en local o desplegarlo, tai-kc, de
tai-keycloak.
Lo que necesita¶
| Variable | Qué es | Quién la usa |
|---|---|---|
MAIN_KEYCLOAK_URL |
La URL de Keycloak, con credenciales de administrador si se van a administrar realms: http://admin:admin@localhost:8090 |
la API y el CLI |
KEYCLOAK_API_CLIENT_SECRET |
El secreto del cliente api del realm (Clients → api → Credentials) |
la API y el CLI |
KEYCLOAK_ADMIN_USERNAME · KEYCLOAK_ADMIN_PASSWORD |
El administrador de Keycloak, si no va en la URL | rbac push |
KEYCLOAK_PUBLIC_URL |
La URL de Keycloak que ven los navegadores, si no es la misma | la API |
PUBLIC_URL |
La URL de la API que ve el navegador, con su /api |
la sesión con cookie |
tai-api no lee ningún .env: las variables tienen que estar en el entorno.
Los realms¶
La API solo acepta tokens de los realms del .taiapiproject, cada uno con su cliente y su
audiencia:
"auth": { "type": "keycloak", "config": { "realms": [ { "name": "main", "client_name": "api", "audience": "api" } ] } }
tai-api rbac push crea en Keycloak los realms que tienen rbac/<realm>.py y no existen —con la
plantilla segura de tai-kc— y los añade al .taiapiproject. La API los acepta después de
tai-api generate, porque la lista va en el código generado.
Cómo se comporta¶
- Los permisos son los roles del cliente de la API del realm y los del cliente
app, nunca los roles de realm.GET /auth/mepide a Keycloak los efectivos, incluidos los heredados de los grupos. - Un token caducado es
TOKEN_EXPIRED, como en el modo Database: es lo que le dice al cliente que refresque en vez de volver al login. - Cambiar la contraseña cierra las sesiones del usuario en Keycloak. Sus access tokens siguen valiendo hasta que caducan: son autosuficientes.
- Keycloak caído es un 503, no un 500 genérico.
La sesión con cookie¶
Con auth.session.transport: "cookie" hay además login por redirección, con la API de
intermediaria:
navegador ──▶ GET /auth/main/authorize?return_to=/inicio ──▶ Keycloak (SSO, MFA, login social)
◀── GET /auth/main/callback ◀──────────────────────┘
la API canjea el código, deja la sesión en la cookie y vuelve a /inicio
El navegador no ve nunca un token de Keycloak. return_to solo admite una ruta del propio sitio,
para que el login no sea un redirector abierto. El login con usuario y contraseña sigue
funcionando, también con la cookie.
Necesita:
PUBLIC_URLen la API: la URL que ve el navegador, con su/apisi va detrás de un proxy (https://app.cliente.com/api). No se deduce de las cabeceras, que detrás de un proxy son lo primero que se falsifica.-
El realm preparado, que lo hace
tai-api rbac pushcon la mismaPUBLIC_URL:- el refresh token rota en cada uso, y presentar uno ya usado revoca la sesión;
- las duraciones de
auth.session; - el cliente
apicon el flujo estándar y PKCE, y su redirección a<PUBLIC_URL>/auth/<realm>/callback; - una sesión por usuario: iniciar sesión en otro dispositivo termina la anterior.
Es idempotente: aplicarlo dos veces no cambia nada, y dice lo que cambió. 3.
KEYCLOAK_API_CLIENT_SECRETen la API: firma la cookie temporal del login por redirección.
La plantilla de los realms nuevos no trae nada de esto: rotar el refresh rompería los frontends que lo refrescan en paralelo, así que solo se aplica a los proyectos que declaran la sesión con cookie.