Flujo de emisión de credenciales
El Credential Issuer de ISBE es el servicio encargado de emitir IsbePortalLearCredential a los empleados y representantes de las organizaciones participantes en la red. El flujo se basa en el protocolo OID4VCI (OpenID for Verifiable Credential Issuance) con preauthorization code.
Arquitectura del Credential Issuer
Portal ISBE
│ (token Keycloak)
▼
Credential Issuer API ◄──── TMF API (datos de la organización)
│ ◄──── Management API (roles y poderes autorizados)
▼
Wallet del usuario (escaneado QR)
│
▼
Credential Issuer API ──► Email al solicitante (QR de oferta de credencial)
El Issuer está implementado en Django + Django REST Framework. El Issuer valida permisos, registra el estado, coordina la generación de la credencial y entrega el QR al solicitante.
Tipos de emisión
El Issuer soporta dos tipos de emisión de credenciales:
| Tipo | Endpoint | Descripción |
|---|---|---|
| Representante | POST /issuance/representative | Emite una VC al propio representante autenticado (el solicitante y el portador son la misma persona). El QR se devuelve en la respuesta HTTP. |
| Empleado | POST /issuance/employee | Emite una VC a un empleado de la organización. El QR se envía por email al empleado. |
Flujo paso a paso: emisión de credencial de representante
1. Autenticación del representante
El representante se autentica en el portal ISBE. Keycloak devuelve un token JWT (access token) con los siguientes claims relevantes:
organization_identifier— Identificador fiscal de la organización (p. ej.ES-B12345678).organization— Nombre de la organización.user_identifier— ID del empleado en el sistema de la organización.email— Email del representante.power— Array de poderes autorizados por ISBE para esa organización.
2. Solicitud de credencial (POST /issuance/representative)
El portal llama al Issuer con el token de Keycloak en la cabecera Authorization: Bearer <token>:
{
"power": [
{
"type": "domain",
"domain": "ISBE",
"function": "onboarding",
"action": ["execute"]
}
]
}
El Issuer realiza las siguientes comprobaciones antes de emitir:
- Valida el token de Keycloak verificando la firma contra el JWKS endpoint de Keycloak (
KEYCLOAK_JWKS_URI). - Verifica los poderes solicitados consultando la Management API: los poderes incluidos en el body del POST deben ser un subconjunto de los poderes autorizados por ISBE para
organization_identifier. - Determina el tipo de VC (
vc_type) a partir de la tablaConfigurationde la base de datos (key = VC_TYPES, tag = "representative"). - Determina el perfil de emisión a usar (tabla
Configuration,key = PROFILE).
3. Registro del preauth code
El servicio de emisión genera un preauth code con los siguientes parámetros:
{
"preauth_code": "52c520b0-b0b6-40c7-8c62-d17b1cce920f",
"expires_in": 300
}
4. Generación del QR
El servicio de emisión genera una imagen PNG con el QR de la oferta de credencial.
5. Persistencia del estado
El Issuer crea un registro IssuedCredential en la base de datos con:
| Campo | Valor |
|---|---|
vc_type | Tipo de VC (p. ej. IsbePortalLearCredential) |
subject_id | <org_identifier>_<timestamp> |
organization_identifier | Identificador fiscal de la organización |
preauth_code | UUID del preauth code |
preauth_code_expires_in | now() + expires_in |
status | pending |
token_data | Claims del token de Keycloak |
body_data | Body del POST (poderes solicitados) |
credential_type | "representative" |
employee_id | user_identifier del token |
6. Entrega del QR al solicitante
- Si el token incluye un campo
email, el Issuer también envía un email al representante con el QR embebido. - La respuesta HTTP al portal devuelve la imagen PNG directamente (
Content-Type: image/png).
7. El usuario escanea el QR con su wallet
El representante escanea el QR con su wallet digital compatible con OID4VCI. El wallet inicia el proceso de reclamación de la credencial; el Issuer recibe la solicitud y responde con los claims:
POST /issuance/claims
{
"preauth_code": "52c520b0-b0b6-40c7-8c62-d17b1cce920f"
}
El Issuer busca el IssuedCredential por preauth_code, lee los datos de organización del TMF API y responde con los claims que debe incluir la VC en el credentialSubject.
8. Notificación de éxito
Cuando el wallet recibe la VC correctamente, el Issuer es notificado:
POST /issuance/notifications
{
"preauth_code": "...",
"credential_id": "urn:uuid:...",
"status": "issued"
}
El Issuer actualiza el registro IssuedCredential a status = "issued" y guarda el credential_id.
Flujo de emisión de credencial de empleado
El flujo de emisión de empleado (POST /issuance/employee) es similar al de representante con estas diferencias:
- El body del POST incluye el email del empleado destinatario y los poderes a asignarle.
- El QR no se devuelve en la respuesta HTTP; en su lugar se envía por email al empleado directamente.
- La respuesta al portal es un JSON con un mensaje de confirmación de envío.
Para emitir credenciales a empleados, el representante autenticado debe tener en su token de Keycloak un poder que incluya la función de emisión delegada. La lógica de validación de poderes es la misma que en la emisión de representante.
Estado de una credencial emitida
El ciclo de vida de un IssuedCredential sigue estos estados:
| Estado | Descripción |
|---|---|
pending | El preauth code ha sido creado, el wallet aún no ha reclamado la credencial. |
issued | El wallet reclamó la credencial y el Connector emisión confirmada. |
revoked | La credencial fue revocada mediante POST /issuance/credential/revoke. |
Consultar y revocar credenciales
| Endpoint | Descripción |
|---|---|
POST /issuance/credential | Recupera el listado de credenciales emitidas para una o varias organizaciones. Protegido con token Keycloak. |
POST /issuance/credential/revoke | Revoca una credencial a partir de su credential_id. Invalida la VC en el registro de estado. |
POST /issuance/identifiers | Devuelve los vc_types para los que el subject_id indicado tiene permisos de emisión. |
Una vez revocada una credencial, no puede volver a activarse. Es necesario emitir una nueva credencial si el empleado o representante necesita seguir accediendo.