Ir al contenido

Autenticación

La API usa JWT Bearer con refresh token y validación de tenant. Hay dos formas de autenticarse según la configuración del tenant:

  • Credenciales (POST /auth/login) — usuario y contraseña de TwiinsHRM.
  • SSO con Keycloak (POST /auth/keycloak) — cuando el tenant tiene Login_SSO = 'S' en sus parámetros.

La regla de oro: tenant en header y en token

Sección titulada «La regla de oro: tenant en header y en token»

Toda petición autenticada lleva dos credenciales, y deben ser coherentes entre sí:

Authorization: Bearer <jwt>
x-tenant-id: <alias-del-tenant>

El JWT incluye un claim tenant. El backend valida que x-tenant-id coincida exactamente con ese claim; si no coinciden responde 401 Unauthorized: Invalid tenant ID. No se puede usar el token de un tenant contra otro.

sequenceDiagram
    participant App as App móvil
    participant API as th-core-api

    App->>API: POST /auth/login (x-tenant-id + username/password)
    API-->>App: { token: { bearer, refresh } }
    App->>App: Guardar tokens en Keychain/Keystore
    App->>API: GET /auth/session (Bearer + x-tenant-id)
    API-->>App: Perfil del usuario autenticado

    Note over App,API: ...uso normal de la API...

    App->>API: GET /me/... (Bearer expirado)
    API-->>App: 401 Unauthorized
    App->>API: POST /auth/refresh { token: { refresh } }
    API-->>App: { token: { bearer, refresh } } (nuevos)
    App->>API: Reintentar la petición original

    App->>API: POST /auth/refresh (refresh inválido/expirado)
    API-->>App: 401 → volver a la pantalla de login

Autentica con usuario y contraseña. Requiere x-tenant-id (aún sin Authorization).

Request

{
"username": "jperez",
"password": "********"
}

Response 200

{
"token": {
"bearer": "eyJhbGciOiJIUzI1NiIs...",
"refresh": "eyJhbGciOiJIUzI1NiIs..."
}
}
Campo Descripción
token.bearer JWT de acceso, de corta duración. Va en Authorization: Bearer en cada petición.
token.refresh JWT de renovación, expira a los 7 días. Solo se usa contra /auth/refresh.

Errores

Código Causa
401 Credenciales inválidas (Invalid credentials).
403 Usuario deshabilitado (User is disabled).
404 x-tenant-id ausente o tenant inexistente.

Renueva ambos tokens a partir del refresh token. No requiere Authorization.

Request

{
"token": {
"refresh": "eyJhbGciOiJIUzI1NiIs..."
}
}

Response 200 — misma forma que el login: { token: { bearer, refresh } }. Guarda ambos tokens nuevos (el refresh también rota).

Errores: 401 si el refresh token es inválido o expiró → la app debe cerrar sesión y volver al login.

Devuelve el perfil del usuario autenticado. Úsalo tras el login para poblar la pantalla de inicio/perfil.

Response 200 (campos principales)

Campo Tipo Descripción
username string Usuario autenticado.
identification string Cédula / documento.
name, lastName, fullName string Nombres.
mail string Correo.
avatar string URL de la foto de perfil.
gender enum Género (UserGenderEnum).
positionId string Cargo.
supervisorsupervisor4 string Cadena de supervisión.
cellPhone string Celular.
status string Estado del usuario.

Incluye además objetos anidados de departamento, oficina y posición.

Invalida las cachés de sesión y permisos del usuario en el servidor. Responde { "message": "Session closed successfully" }.

Después de llamarlo, la app debe borrar los tokens locales. Si el tenant usa SSO, además hay que cerrar la sesión en Keycloak (ver abajo).

Cambio de contraseña del usuario autenticado (requiere Bearer). El detalle del contrato se documenta junto al módulo Mis Datos.

SSO con Keycloak (tenants con Login_SSO = 'S')

Sección titulada «SSO con Keycloak (tenants con Login_SSO = 'S')»

Antes de mostrar la pantalla de login, consulta los parámetros del tenant (ver Tenants y ambientes). Si Login_SSO es 'S', el login es contra Keycloak con Authorization Code + PKCE:

  1. La app abre el navegador (Custom Tab / ASWebAuthenticationSession) hacia el servidor Keycloak: https://auth.twiinshrm.com/realms/{alias-del-tenant}/protocol/openid-connect/auth con el client_id del portal, redirect_uri propio de la app y el code_challenge PKCE.
  2. El usuario se autentica y Keycloak redirige al redirect_uri con un code.
  3. La app intercambia el código contra la API (no contra Keycloak directamente):
POST /auth/keycloak
x-tenant-id: <alias>
{
"code": "<authorization_code>",
"codeVerifier": "<pkce_code_verifier>",
"redirectUri": "<el mismo redirect_uri usado en el paso 1>"
}

La respuesta entrega los mismos tokens { token: { bearer, refresh } } que el login normal — de ahí en adelante todo el flujo es idéntico.

Logout con SSO: además de POST /auth/logout y de borrar los tokens locales, abre la URL de logout de Keycloak (.../realms/{alias}/protocol/openid-connect/logout) para cerrar la sesión del navegador.

  1. Guarda los tokens en almacenamiento seguro (iOS Keychain / Android Keystore). Nunca en preferencias planas.
  2. Ante un 401 en cualquier endpoint: intenta una vez POST /auth/refresh; si funciona, reintenta la petición original con el nuevo bearer.
  3. Si el refresh también devuelve 401: borra tokens y navega al login.
  4. Serializa el refresh: si varias peticiones reciben 401 a la vez, ejecuta un solo refresh y reintenta las demás con el token nuevo (el refresh rota en cada uso).