Files
task-stream/docs/05-autenticacion.md
2026-10-03 13:40:26 +02:00

4.9 KiB

5. Autenticación (Pireon)

Los usuarios pertenecen a Pireon (https://oauth.pireon.dev, OAuth2 con Laravel Passport). TaskStream no guarda contraseñas: solo valida tokens y guarda una copia local de nombre y email para mostrarlos.

5.1 Cómo valida la API un token

sequenceDiagram
  participant C as Cliente
  participant G as AuthGuard (presenters)
  participant Q as ResolveSessionQuery
  participant R as Redis (caché)
  participant P as Pireon

  C->>G: Authorization: Bearer <token>
  G->>Q: resolveSession(token)
  Q->>R: GET auth:session:sha256(token)
  alt en caché
    R-->>Q: usuario
  else no está
    Q->>P: GET /api/user
    P-->>Q: usuario + roles
    Q->>R: SET (AUTH_SESSION_TTL_SECONDS)
  end
  Q-->>G: User (o 401)
  • Se cachea el hash SHA-256 del token, nunca el token en claro.
  • Si Pireon marca al usuario como inactivo, el token se rechaza.
  • El guard es global: todo requiere token salvo lo marcado con @Public() (health, configuración de login, proxy de tokens y webhook).
  • En subscriptions, el token se valida una vez al abrir el WebSocket (connection_init).
  • @RequireAppRoles('…') permite exigir roles del cliente OAuth en Pireon (los admins globales de Pireon los tienen todos).

5.2 Iniciar sesión desde el navegador

Pireon no permite CORS en /oauth/token y el client_secret no debe llegar al navegador, así que la web pasa por la API: POST /api/auth/token hace de proxy hacia Pireon.

Método Necesita Flujo
Entrar con Pireon (Authorization Code + PKCE) PIREON_CLIENT_ID, PIREON_CLIENT_SECRET y la redirect URI registrada Web → /oauth/authorize de Pireon → /auth/callback → POST /api/auth/token (authorization_code + code_verifier)
Email y contraseña El mismo cliente, con PIREON_PASSWORD_GRANT=true POST /api/auth/token (password)

Las dos opciones necesitan el cliente OAuth: sin PIREON_CLIENT_ID la API responde 503 y el login avisa de que no está configurado. El email y la contraseña son los de la cuenta de Pireon; TaskStream no los guarda, solo los reenvía. (El login con token de acceso personal se ha retirado de la web por ahora; la API sigue aceptando esos tokens como Bearer.)

Registrar TaskStream como cliente en Pireon

  1. Crea el cliente: POST https://oauth.pireon.dev/api/clients (con un token tuyo de Pireon, scope account:write).
  2. Registra como redirect URIs:
    • http://localhost:4200/auth/callback (desarrollo)
    • http://localhost:8080/auth/callback (Docker)
    • la de producción
  3. Pon PIREON_CLIENT_ID y PIREON_CLIENT_SECRET en .env (o en el entorno del contenedor).

GET /api/auth/config devuelve a la web lo necesario para iniciar el flujo (authorizeUrl, clientId, scopes, passwordGrant), o null si no hay cliente configurado; en ese caso el login muestra el aviso y no deja entrar.

Sesión en la web

  • La sesión (accessToken, refreshToken, caducidad) se guarda en localStorage y se restaura al arrancar.
  • Un minuto antes de caducar se renueva con refresh_token. Si llegan varias peticiones a la vez, se renueva una sola vez.
  • Si la API responde UNAUTHENTICATED, se cierra la sesión y se vuelve al login.
  • El parámetro state del flujo PKCE protege contra CSRF; el code_verifier vive en sessionStorage solo durante la redirección.

5.3 Webhooks de Pireon (opcional)

Para que la desactivación de un usuario o la revocación de un token surtan efecto al instante (sin esperar a que caduque la caché):

  1. Registra https://<tu-api>/api/webhooks/pireon con los eventos user.deactivated y token.revoked (POST /api/clients/{client}/webhooks).
  2. Pon el secret que devuelve en PIREON_WEBHOOK_SECRET.

La API verifica la firma X-Webhook-Signature (HMAC-SHA256 del cuerpo), marca al usuario como inactivo y borra sus sesiones cacheadas. Sin PIREON_WEBHOOK_SECRET, el endpoint rechaza todas las llamadas.

5.4 Usuarios en TaskStream

  • Al validar un token, el usuario se guarda (nombre, email, activo) en la colección users de MongoDB.
  • users(search) sincroniza antes el directorio de Pireon con el token del usuario, así que se puede añadir a gente que aún no ha entrado en TaskStream (requiere el scope users:read).
  • Los read models guardan el nombre de los usuarios desnormalizado: si alguien cambia su nombre en Pireon, las entradas antiguas mantienen el nombre anterior.