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
- Crea el cliente:
POST https://oauth.pireon.dev/api/clients(con un token tuyo de Pireon, scopeaccount:write). - Registra como redirect URIs:
http://localhost:4200/auth/callback(desarrollo)http://localhost:8080/auth/callback(Docker)- la de producción
- Pon
PIREON_CLIENT_IDyPIREON_CLIENT_SECRETen.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 enlocalStoragey 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
statedel flujo PKCE protege contra CSRF; elcode_verifiervive ensessionStoragesolo 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é):
- Registra
https://<tu-api>/api/webhooks/pireoncon los eventosuser.deactivatedytoken.revoked(POST /api/clients/{client}/webhooks). - 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
usersde 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 scopeusers: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.