Guards
Ubicados en src/app/core/guards/.
AuthGuard — auth.guard.ts
Protege rutas que requieren sesión de usuario activa.
Cómo funciona:
- Extrae el token del header
x-api-key - Verifica y decodifica el JWT con
JwtService - Consulta el usuario en BD mediante
UserService - Valida que
user.isActive === truey que el tenant del usuario esté operativo - Inyecta el usuario en
request.user
Uso:
@UseGuards(AuthGuard)
// o con el decorador abreviado:
@Auth()
Errores que lanza:
401 Unauthorized— headerx-api-keyausente, token inválido o expirado, usuario no encontrado, usuario inactivo o tenant no operativo
ApiKeyGuard — api-key.guard.ts
Protege endpoints destinados a clientes externos (integraciones de terceros).
Cómo funciona:
- Extrae
apiKeydel body y lo elimina derequest.body(el resto del body sigue disponible) - Calcula el hash de la key (
hashApiKey) y la busca por ese código (ApiKeyService.findByCode) - Valida que
isActiveseatruey quedateExpirationno haya pasado - Busca el tenant de la key (
TenantService.findByMongoId) y valida que exista y queisActiveseatrue - Si
tenant.domainWhitelistno está vacío, toma el dominio del headerOrigin(o, si falta, deReferer) y exige que termine con alguno de los dominios de la lista; si la lista está vacía, no se valida el dominio - Adjunta
request.tenantyrequest.apiKey
Uso:
@UseGuards(ApiKeyGuard)
// o:
@ApiKeyAuth()
Errores que lanza:
401 Unauthorized—Missing apiKey in body,Invalid API key,API key is inactive,API key has expired,Tenant not foundoTenant is inactive403 Forbidden—Domain not allowed: la whitelist del tenant tiene dominios y el origen falta o no coincide
:::note Comparación de dominio
La coincidencia usa origin.endsWith(dominio) sobre el hostname, no una igualdad exacta: un dominio de la lista como example.com también admite miexample.com.
:::
Diferencia entre los dos guards
AuthGuard | ApiKeyGuard | |
|---|---|---|
| Usado por | Usuarios internos (agentes) | Clientes externos / integraciones |
| Credencial | JWT en header x-api-key | API Key en body |
| Validación extra | user.isActive y tenant operativo | Estado de la key, expiración, tenant activo y whitelist de dominios (si existe) |
PermissionsGuard — permissions.guard.ts
Autoriza el acceso según el permiso declarado con @RequirePermission. Es el guard de autorización fina del CRM: el AuthGuard solo dice quién es el usuario; este decide qué puede hacer.
Cómo funciona:
- Lee el acrónimo requerido con
Reflector.getAllAndOverridesobre el handler y la clase (el del handler tiene prioridad). - Si no hay
@RequirePermission, deja pasar (true): una ruta sin el decorador solo exige lo que apliquen los demás guards. - Toma
request.usery exige que tengatenantIdyroleId; si falta alguno lanza403. - Llama a
PermissionResolverService.resolve(tenantId, roleId), que devuelve el conjunto de acrónimos del rol (con caché de 10 segundos; ver RBAC). - Si el conjunto no contiene el acrónimo requerido, lanza
403.
Uso:
@UseGuards(PermissionsGuard)
@Auth()
@Controller(...)
export class UserController {
@RequirePermission('USER_READ')
@Get('get-all')
findAll() { ... }
}
Errores que lanza:
403 Forbidden(Forbidden resource) — el usuario no tienetenantIdoroleId, o su rol no incluye el permiso
:::note Orden de guards
PermissionsGuard depende de request.user, que solo establece AuthGuard. Por eso los controladores combinan @UseGuards(PermissionsGuard) con @Auth(). Si no hay usuario, el resultado sería 403 y no 401.
:::
El guard y el resolvedor se exportan desde CorePermissionsModule (src/app/core/permissions/), que cada módulo con rutas protegidas debe importar.
RolesGuard — roles.guard.ts
Compara request.user.userType con la lista de @Roles. Sin @Roles (o con lista vacía) permite el paso; si el usuario no tiene userType o no está en la lista, lanza 403.
PlatformAdminGuard — platform-admin.guard.ts
Permite el acceso solo si request.user.isPlatformAdmin === true; en otro caso lanza 403. Se aplica con @PlatformAdmin(), que lo combina con AuthGuard.