Puntos de Entrada
/login via NextAuth signIn()./dashboard/**.Flujo de Ejecución — Login Credentials
LoginForm.tsx llama signIn('credentials', {email, password}). Validación Zod client-side antes del POST.[...nextauth]/route.ts ejecuta el callback authorize() del provider credentials.users en PostgreSQL → compara hash bcrypt → si falla, retorna null (NextAuth devuelve 401).jwt.ts: access token (15 min, HS256, payload: {userId, orgId, role, permissions[]}). Refresh token (7 días) guardado en tabla refresh_tokens.jwt() añade accessToken, role y orgId al objeto session. El token JWT de NextAuth envuelve el access token propio.middleware.ts usa getToken() de NextAuth. Si el token expira → redirige a /login?callbackUrl=.... ⚠️ No verifica la firma del access token propio, solo el de NextAuth.withRBAC(handler, ['org_admin', 'superadmin']) decodifica el access token propio → verifica rol. La lógica de permisos está duplicada: aquí y en usePermissions() del cliente.AuthProvider.tsx monta un setInterval cada 840 s → llama /api/auth/refresh → endpoint valida refresh token en DB y emite nuevo par.Capas de Arquitectura
/dashboard/**, /api/v1/**getToken() — sin DBmfa_verified del tokenjwt() y session() enriquecen el tokenmfa_enabled y mfa_verified al tokensignIn() puede bloquear el flujo si MFA requeridorbac.ts define matriz roles → permisoswithRBAC() en server, usePermissions() en clientusers: credenciales + rol + orgIdrefresh_tokens: token, userId, expiresAtmfa_secret, mfa_enabled, mfa_backup_codesmfa_challenges para flujo de verificaciónPatrones Identificados
Archivos Clave
| Archivo | Rol | Importancia |
|---|---|---|
| src/app/api/auth/[...nextauth]/route.ts | Orquestador del flujo auth (credentials + OAuth) | Crítico |
| src/middleware.ts | Guard de edge runtime — protección de rutas | Crítico |
| src/lib/auth/jwt.ts | Utilidades: sign, verify, decode del access token propio | Crítico |
| src/lib/auth/rbac.ts | Definición de roles, permisos y HOF withRBAC() |
Alto |
| src/components/AuthProvider.tsx | Provider React + lógica de refresh silencioso | Alto |
| src/hooks/usePermissions.ts | Exposición de permisos a componentes UI | Medio |
Dependencias
■ Externas
next-auth@5jsonwebtokenbcryptjszod@prisma/client■ Internas
src/lib/dbsrc/lib/auth/jwt.tssrc/lib/auth/rbac.tssrc/types/auth.tssrc/lib/emailRecomendaciones para nuevo desarrollo (MFA TOTP)
signIn() de NextAuthfalse en signIn() si el usuario tiene mfa_enabled=true pero aún no ha verificado el TOTP. Redirigir a /auth/mfa-challenge con un token de estado temporal.withRBAC() para el guard MFAwithMFA(handler) siguiendo el mismo patrón HOF de withRBAC(). Verificar el campo mfa_verified del JWT antes de continuar al handler.jwt.ts para incluir mfa_verified en el payloaduserId, orgId, role, permissions[]. Añadir mfa_verified: boolean y mfa_at: timestamp. Actualizar el tipo JWTPayload en src/types/auth.ts.withRBAC() (server) y usePermissions() (client) está duplicada. Crear un módulo src/lib/auth/permissions.ts como fuente única e importarlo en ambos lados.jsonwebtoken (Node.js). Confiar en el token de NextAuth para el middleware y usar el access token propio solo en los API handlers (Node.js runtime).mfa_secret (semilla TOTP) debe quedar solo en la columna users.mfa_secret de PostgreSQL, cifrado en reposo. En el JWT solo va mfa_verified: true/false.🔑 Plan de implementación MFA sugerido
1. Migración DB — Añadir mfa_secret TEXT, mfa_enabled BOOLEAN DEFAULT false, mfa_backup_codes TEXT[] a la tabla users. Crear tabla mfa_challenges(id, user_id, expires_at) para el estado temporal entre pasos 1 y 2 del login.
2. Setup flow — Nueva ruta /settings/security/mfa: generar secret con otplib, mostrar QR con qrcode, verificar primer código, persistir mfa_enabled=true.
3. Login flow — Modificar callback signIn() en [...nextauth]/route.ts: si mfa_enabled, redirigir a /auth/mfa-challenge?challenge_id=.... Crear handler POST /api/auth/mfa/verify que valide el TOTP, actualice el token JWT con mfa_verified: true y complete la sesión.
4. Guards — Añadir withMFA() a handlers de operaciones críticas (transferencias de crédito, cambio de plan). Actualizar middleware.ts para redirigir a challenge si el token tiene mfa_verified: false y la ruta lo requiere.