Saltar a contenido

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/me pide 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.

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:

  1. PUBLIC_URL en la API: la URL que ve el navegador, con su /api si 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.
  2. El realm preparado, que lo hace tai-api rbac push con la misma PUBLIC_URL:

    • el refresh token rota en cada uso, y presentar uno ya usado revoca la sesión;
    • las duraciones de auth.session;
    • el cliente api con 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_SECRET en 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.