Saltar a contenido

Autenticación y roles

Dos mecanismos, para públicos distintos

Este servicio tiene dos formas de autenticación independientes. No se reemplazan entre sí — sirven a públicos distintos y conviven al mismo tiempo.

Mecanismo Para quién Endpoint de login Tabla Restricción de proceso
clientId / clientSecret Un sistema externo llamando a la API sin humano de por medio (machine-to-machine) POST /api/v1/auth/token clientes_api Ninguna — accede a cualquier proceso
username / password Una persona (rol ADMIN o FIRMANTE) POST /api/v1/usuarios/login usuarios Sí, si es FIRMANTE

Ambos devuelven el mismo formato de token: un JWT Bearer firmado con HS512, con expiración configurable (1 hora por defecto). Se usa igual en ambos casos, en el header:

Authorization: Bearer <accessToken>

La diferencia está en qué endpoints puede llamar cada uno y si hay restricción de proceso — no en el formato del token en sí.

Login de cliente API (machine-to-machine)

POST /api/v1/auth/token
Content-Type: application/json

{
  "clientId": "saeta-prod-01",
  "clientSecret": "7s_vtHa6pHH5OLNk80u9hpRn6xQW3ZJiMblJiPxz4sM"
}

Respuesta 200:

{
  "accessToken": "eyJhbGciOiJIUzUxMiJ9...",
  "tokenType": "Bearer",
  "expiresIn": 3600
}

Este mecanismo no tiene restricción de proceso — con un token de cliente API se puede enviar un documento a cualquier idProcesoFirma que exista, sin necesitar que un ADMIN se lo asigne explícitamente. Está pensado para integraciones de sistema a sistema (por ejemplo, un ERP que envía documentos automáticamente).

Login de persona (humano)

POST /api/v1/usuarios/login
Content-Type: application/json

{
  "username": "sfirmante",
  "password": "cambiar123"
}

Respuesta 200: mismo formato que el anterior (accessToken, tokenType, expiresIn). La diferencia es lo que viaja dentro del JWT: el rol de esa persona (ADMIN o FIRMANTE), que determina qué endpoints puede llamar después.

Roles

ADMIN — único y global (cliente_id es NULL en la tabla usuarios). Gestiona todo: - Crea clientes (organizaciones). - Crea firmantes — la API nunca permite crear otro ADMIN, solo puede crear usuarios con rol FIRMANTE, por diseño. - Asigna procesos a firmantes. - Puede listar todos los procesos configurados en el sistema, para saber qué id asignarle a cada firmante.

FIRMANTE — pertenece a un cliente específico. Solo puede: - Ver sus propios procesos asignados (GET /api/v1/mis-procesos), con el certificado correspondiente a cada uno. - Enviar, consultar y descargar documentos — pero solo contra los procesos que el ADMIN le asignó explícitamente. Si intenta usar un idProcesoFirma que no le pertenece, recibe 403 PROCESO_NO_AUTORIZADO.

Autorización por endpoint

Endpoint Rol requerido
POST /api/v1/auth/token Público
POST /api/v1/usuarios/login Público
POST /api/v1/clientes ADMIN
GET /api/v1/clientes ADMIN
POST /api/v1/usuarios ADMIN
GET /api/v1/usuarios ADMIN
POST /api/v1/usuarios/{id}/procesos ADMIN
GET /api/v1/procesos ADMIN
GET /api/v1/mis-procesos FIRMANTE
POST /api/v1/documentos Cualquiera autenticado*
GET /api/v1/documentos/{trackingId} Cualquiera autenticado*
GET /api/v1/documentos/{trackingId}/archivo Cualquiera autenticado*

* Los endpoints de documentos no exigen un rol específico — cualquier token válido (cliente API o usuario) puede llamarlos. La restricción real ahí es distinta: si el token es de un FIRMANTE, se verifica además que el proceso pedido esté en su lista de procesos asignados. Un cliente API o un ADMIN no tienen esa restricción.

Cómo se diferencian los errores de autenticación/autorización

Situación HTTP Código
Sin token, token expirado o mal formado 401 TOKEN_INVALIDO
Token válido, pero el rol no alcanza para ese endpoint 403 ACCESO_DENEGADO
Token de FIRMANTE válido, pero pide un proceso que no le asignaron 403 PROCESO_NO_AUTORIZADO
Login con credenciales incorrectas (cualquiera de los dos mecanismos) 401 CREDENCIALES_INVALIDAS

Credenciales de referencia (entorno de producción)

Estas son credenciales reales del entorno de producción. Si este repositorio deja de ser privado en algún momento, hay que rotarlas antes.

Cliente API (para probar el flujo machine-to-machine):

clientId:     saeta-prod-01
clientSecret: 7s_vtHa6pHH5OLNk80u9hpRn6xQW3ZJiMblJiPxz4sM

Admin único (para el panel/login humano):

username: admin-ssigner-server
password: saet@2026

Firmante de prueba (pertenece a un cliente, con procesos ya asignados):

username: sfirmante
password: cambiar123

Base URL de producción: https://agente.plussigner.com

Cómo se protegen las contraseñas y secretos

  • usuarios.password_hash y clientes_api.client_secret_hash se guardan siempre hasheados con BCrypt — nunca en texto plano, ni siquiera en la base de datos.
  • El admin único se crea automáticamente al arrancar el servicio por primera vez (AdminBootstrap), sin secretos hardcodeados en el código: si hay variables de entorno ADMIN_USERNAME/ADMIN_PASSWORD configuradas, las usa; si no, genera un password aleatorio seguro y lo imprime una única vez en el log de arranque.
  • No existe ningún endpoint para crear otro admin — la única forma es un INSERT directo en la base de datos.

Cómo agregar más credenciales

Otro cliente API (INSERT directo, con el secreto ya hasheado en BCrypt):

INSERT INTO clientes_api (nombre, client_id, client_secret_hash, activo, fecha_creacion)
VALUES ('nombre-del-sistema', 'client-id-nuevo', '<hash-bcrypt>', 1, datetime('now'));

Un firmante nuevo: por la API, no por SQL — POST /api/v1/usuarios (requiere token de ADMIN), con {"nombre", "username", "password", "clienteId"}. Ver el detalle completo en Referencia de API.