ValwispDocs
Desarrolladores

Autenticación

JWT + 3 guards globales (roles legacy y CASL dinámico) + el flujo real de login/refresh.

JWT

El payload es { sub: userId, tenantId, role } (sub, no userId — ojo si buscas el claim en el token). Se firma y valida en src/modules/auth/.

  • POST /api/v1/auth/login — público, throttle 5/min.
  • POST /api/v1/auth/refresh — público, throttle 10/min.
  • POST /api/v1/auth/accept-invite — público, crea contraseña desde invitación + login automático.
  • POST /api/v1/auth/logout — requiere JWT; invalida el refresh token en BD y agrega blacklist:{userId} en Redis (TTL 900s).
  • GET /api/v1/auth/me — requiere JWT.

JwtStrategy (src/modules/auth/strategies/jwt.strategy.ts) valida el Bearer token, chequea la blacklist de Redis, carga el User completo de Prisma (con role, roleId, tenantId, permissions, nodeAccess) y actualiza lastSeenAt (throttled a 1 escritura/2min) para presencia en tiempo real.

Los 3 guards globales

Registrados en src/app.module.ts vía APP_GUARD, en este orden de ejecución:

ThrottlerGuard → JwtAuthGuard → RolesGuard → PermissionsGuard → DemoReadOnlyGuard
GuardPathComportamiento
JwtAuthGuardsrc/common/guards/jwt-auth.guard.tsSi hay @Public(), deja pasar; si no, valida el JWT vía Passport.
RolesGuardsrc/common/guards/roles.guard.tsLegacy — lee @Roles(...UserRole[]). SUPER_ADMIN siempre pasa. Si no hay roles requeridos, pasa.
PermissionsGuardsrc/common/authorization/permissions.guard.tsDinámico (CASL) — lee @CheckPermission(action, subject), construye la Ability del usuario y verifica ability.can(...). Si no hay decorador, pasa (deja el control a RolesGuard).

@Roles y @CheckPermission pueden coexistir en la misma ruta durante la migración de legacy → dinámico — ambos deben pasar si ambos están presentes.

CaslAbilityFactory

src/common/authorization/casl-ability.factory.ts construye una PureAbility por usuario:

  1. Si tiene customPermissionCodes explícitos → los usa directo.
  2. Si tiene roleId → carga Role + RolePermission de Prisma, cacheado en Redis (ability:{roleId}, TTL 300s). SUPER_ADMINcan('manage', 'all').
  3. Si no hay roleId (legacy) → cae a DEFAULT_ROLE_PERMISSIONS por el string del enum role.

invalidateRole(roleId) limpia la caché al editar un rol — ver Implementadores → Roles y permisos para el catálogo completo y el impacto de la caché de 5 minutos.

Decoradores

@Roles(...roles), @CheckPermission(action, subject), @Public(), @CurrentUser(), @TenantId() — todos en src/common/decorators/.

On this page