Skip to main content

Autenticación, interceptores y permisos

Contraparte del backend: Auth, RBAC y Guards.

Sesión​

  1. POST <apiUrl>/auth/login devuelve { token } (JWT). La petición lleva el header skip para que los interceptores no la traten como autenticada.
  2. GET <apiUrl>/users/get-me devuelve el perfil con permissions (acrónimos), featureFlags y tenantPreferences.
  3. La sesión { ...usuario, token } se guarda en StorageService.secureStorage bajo la clave USER_SESSION (cifrada, ver Servicios).
  4. Al recargar, AppComponent despacha loadUserSession: si hay token se vuelve a pedir get-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.

InterceptorQué 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​

CasoComportamiento
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 iframeEmite CRM_AUTH_EXPIRED al padre y navega a /auth/reconnect
401 fuera de iframeDespacha userLogout y navega a /auth/login
403Toast 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 otroToast 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):

  1. Rutas: permissionGuard con data: { permission: '<ACRÓNIMO>' } (ver Rutas).
  2. 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.
  3. Menú: Menu.permission en nav.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.