Autenticación, interceptores y permisos
Contraparte del backend: Auth, RBAC y Guards.
Sesión
POST <apiUrl>/auth/logindevuelve{ token }(JWT). La petición lleva el headerskippara que los interceptores no la traten como autenticada.GET <apiUrl>/users/get-medevuelve el perfil conpermissions(acrónimos),featureFlagsytenantPreferences.- La sesión
{ ...usuario, token }se guarda enStorageService.secureStoragebajo la claveUSER_SESSION(cifrada, ver Servicios). - Al recargar,
AppComponentdespachaloadUserSession: si hay token se vuelve a pedirget-me.
AuthService.isLoggedIn() compara exp del JWT (decodificado en StorageService.parseJwt) con la hora actual.
El token viaja en el header x-api-key (TOKEN_HEADER_KEY), no en Authorization.
Interceptores HTTP
Registrados en AppModule en este orden: AuthTokenInterceptor, ErrorInterceptor, DeviceTokenInterceptor. Detalle completo (orden de la cadena, headers, casos por código HTTP, specs) en Interceptores HTTP.
| Interceptor | Qué hace |
|---|---|
AuthTokenInterceptor (auth-token.interceptor.ts) | Añade Timezone-Offset y Timezone-Name a toda petición. Si la petición lleva el header skip, no añade token. En otro caso lee USER_SESSION, agrega x-api-key y, si no hay token, navega a /auth/login (salvo en iframe) |
ErrorInterceptor (http-error.interceptor.ts) | Ver tabla de abajo |
DeviceTokenInterceptor (device-token.interceptor.ts) | Añade X-Device-Token (UUID persistido en localStorage bajo backoffice_device_token por DeviceTokenService) y X-Lang (idioma de LanguageService) si no están ya en la petición |
Tratamiento de errores
| Caso | Comportamiento |
|---|---|
401 en una petición con skip (login, frame-login) | Deja pasar el error crudo para que los efectos lean el mensaje real |
| 401 en iframe | Emite CRM_AUTH_EXPIRED al padre y navega a /auth/reconnect |
| 401 fuera de iframe | Despacha userLogout y navega a /auth/login |
| 403 | Toast words.forbiddenActionTitle / words.forbiddenAction; no cierra sesión ni redirige |
Error con error.data.code (por ejemplo TICKET_LOCKED, o META_NOT_CONFIGURED en OAuth de Instagram) | Se propaga intacto (HttpErrorResponse) sin toast, para que el llamador ramifique por código |
| Cualquier otro | Toast con error.error.message (o words.unknownError) y relanza Error con el mensaje traducido |
RBAC en el frontend
El backend resuelve un conjunto plano de acrónimos de permiso (CONTACT_READ, CONNECTION_CREATE, ...) y lo devuelve en get-me. El frontend lo usa en tres puntos (detalle de guards en Guards de rutas y de directivas en Directivas):
- Rutas:
permissionGuardcondata: { permission: '<ACRÓNIMO>' }(ver Rutas). - Plantillas: directiva estructural
*appShowWithPermission(shared/directives/show-with-permission.directive.ts), que acepta un acrónimo o una lista (basta uno). Es deny-by-default: el contenido no se renderiza hasta que la verificación resuelve. - Menú:
Menu.permissionennav.constant.ts.
MyPermissionsService (core/services/ui/my-permissions.service.ts) guarda el Set de acrónimos, lo siembra desde GlobalService.profile y lo refresca llamando a getMeUser() cuando está obsoleto. Si el refresco falla, queda en conjunto vacío (todo denegado) para no bloquear la navegación. Métodos: hasPermission(acronym) y hasAnyPermission(acronyms).
:::caution TTL de permisos
La constante del código es PERMISSIONS_TTL_MS = 10_000 (10 s), pero el comentario del mismo archivo y el README.md del repo hablan de 60 s. Rige el valor del código.
:::
Acrónimos que aparecen en rutas (data.permission): ACCESS_CONTROL_READ, TICKET_READ, TICKET_UNLOCK_ANY, MESSAGE_READ, LABEL_READ, QUICK_RESPONSE_READ, CONTACT_READ, USER_READ, DEPARTMENT_READ, SETTINGS_READ, INTERNAL_CHAT_READ, CONNECTION_READ.
Acrónimos que aparecen en plantillas con *appShowWithPermission (acciones dentro de pantallas): *_CREATE, *_UPDATE y *_DELETE de ACCESS_CONTROL, CONNECTION, CONTACT (sin delete), DEPARTMENT, LABEL, QUICK_RESPONSE (sin delete), USER (sin delete); además MESSAGE_CREATE, MESSAGE_DELETE, SETTINGS_UPDATE, INTERNAL_CHAT_UPDATE, TICKET_UPDATE, TICKET_DELETE, TICKET_UNLOCK_ANY, TICKET_FILTER_BY_AGENT, TICKET_FILTER_SCOPE_ALL, TICKET_FILTER_SCOPE_MINE, TICKET_FILTER_SCOPE_UNASSIGNED, TICKET_VIEW_AGENT_AVATAR y LABELS_ASSIGN_PERMISSION.
El backend es siempre la autoridad: un 403 se muestra como toast aunque el frontend hubiera dejado pasar la acción por una caché desactualizada.
Tipos de usuario
TYPES_USER_PERMISSIONS (helpers/global/user-management.constant.ts): admin, agent, cashdesk, collaboratorAgent, collaboratorOperator. Solo admin se evalúa en el frontend (AdminGuard, usado en la ruta de settings). Ver Guards de rutas.