Referencia OID4VCI y OID4VP
Esta página es la referencia técnica para integradores que construyen emisores (issuers) o verificadores (verifiers) que interoperan con la ISBE Holder Wallet.
La wallet actúa como cliente en ambos protocolos. No expone ninguna API pública propia; son los emisores y verificadores quienes deben exponer los endpoints descritos aquí.
OID4VCI — Recepción de credenciales
La wallet implementa el flujo pre-autorizado de OpenID for Verifiable Credential Issuance 1.0.
Endpoints que debe exponer el emisor
| Endpoint | Descripción |
|---|---|
GET /.well-known/openid-credential-issuer | Metadatos del emisor (Issuer Metadata). |
POST /token | Token endpoint. Intercambia el pre-authorized_code por un access_token. |
POST /credential | Credential endpoint. Devuelve la credencial firmada. |
POST /deferred_credential | (Opcional) Deferred credential endpoint. Para emisión diferida. |
Metadatos del emisor (openid-credential-issuer)
El emisor debe publicar sus metadatos en /.well-known/openid-credential-issuer. La wallet los obtiene al procesar la credential_offer.
Campos mínimos requeridos:
{
"credential_issuer": "https://issuer.ejemplo.redisbe.com",
"credential_endpoint": "https://issuer.ejemplo.redisbe.com/credential",
"credentials_supported": [
{
"format": "jwt_vc_json",
"id": "NombreDelTipoDeCredencial",
"types": ["VerifiableCredential", "NombreDelTipoDeCredencial"],
"cryptographic_binding_methods_supported": ["did:key", "did:jwk"],
"cryptographic_suites_supported": ["ES256K", "EdDSA"]
}
]
}
Estructura del credential_offer
La oferta puede entregarse directamente en el QR/deep link o por referencia mediante credential_offer_uri.
Por valor:
openid-credential-offer://?credential_offer=<URL-encoded JSON>
Por referencia:
openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fissuer.ejemplo.com%2Foffer%2Fabc123
Objeto credential_offer:
{
"credential_issuer": "https://issuer.ejemplo.redisbe.com",
"credentials": ["NombreDelTipoDeCredencial"],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "SplxlOBeZQQYbYS6WxSbIA",
"tx_code": {
"length": 6,
"input_mode": "numeric",
"description": "Introduce el código recibido por SMS"
}
}
}
}
El campo tx_code es opcional. Si el emisor no requiere PIN, omite el campo completo.
Token endpoint
La wallet hace POST /token con los siguientes parámetros en el body (form-urlencoded):
| Parámetro | Valor |
|---|---|
grant_type | urn:ietf:params:oauth:grant-type:pre-authorized_code |
pre-authorized_code | El código del credential_offer. |
tx_code | (Condicional) El PIN introducido por el holder, si el emisor lo requirió. |
Respuesta exitosa:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 86400,
"c_nonce": "tZignsnFbp",
"c_nonce_expires_in": 86400
}
El campo c_nonce es opcional pero requerido si el emisor exige prueba de posesión en el endpoint /credential.
Credential endpoint
La wallet hace POST /credential con:
- Header:
Authorization: Bearer <access_token> - Body JSON:
{
"format": "jwt_vc_json",
"types": ["VerifiableCredential", "NombreDelTipoDeCredencial"],
"proof": {
"proof_type": "jwt",
"jwt": "<JWT firmado con la clave privada del holder>"
}
}
Estructura del proof.jwt:
El JWT de prueba de posesión es firmado por el holder y contiene:
{
"alg": "ES256K",
"typ": "openid4vci-proof+jwt",
"kid": "did:key:z6Mk...#z6Mk..."
}
.
{
"iss": "did:key:z6MkszZtxCmA2Ce4vUV132PCuLQmwnaDD5mw2L23fGNnsiRn",
"aud": "https://issuer.ejemplo.redisbe.com",
"iat": 1700000000,
"nonce": "tZignsnFbp"
}
El nonce en el proof.jwt debe coincidir con el c_nonce devuelto por el token endpoint.
Respuesta exitosa (emisión inmediata):
{
"format": "jwt_vc_json",
"credential": "eyJhbGciOiJFUzI1NiIs..."
}
Respuesta para emisión diferida:
{
"acceptance_token": "eyJhbGciOiJSUzI1NiIs...",
"c_nonce": "wlbQc6pCJp",
"c_nonce_expires_in": 86400
}
Deferred credential endpoint
Si el emisor devuelve un acceptance_token, la wallet consultará periódicamente POST /deferred_credential:
{
"acceptance_token": "eyJhbGciOiJSUzI1NiIs..."
}
Mientras la credencial no esté lista, el emisor responde con HTTP 202. Cuando está lista, devuelve la credencial con HTTP 200 en el mismo formato que el endpoint /credential.
OID4VP — Presentación de credenciales
La wallet implementa OpenID for Verifiable Presentations 1.0 con DCQL (Digital Credentials Query Language).
La wallet ISBE usa DCQL para los criterios de presentación. El objeto presentation_definition de DIF PEv2 no está soportado. Los verificadores deben usar el campo dcql_query en el authorization_request.
Flujo de presentación
Deep link de solicitud
Por valor:
openid4vp://?<parámetros URL-encoded del authorization_request>
Por referencia (recomendado):
openid4vp://?request_uri=https%3A%2F%2Fverifier.ejemplo.com%2Frequest%2Fabc123
Estructura del authorization_request
El verificador puede entregar el authorization_request como URL o como JWT firmado. Campos requeridos:
{
"response_type": "vp_token",
"response_mode": "direct_post",
"response_uri": "https://verifier.ejemplo.com/response",
"client_id": "https://verifier.ejemplo.com",
"nonce": "n-0S6_WzA2Mj",
"dcql_query": {
"credentials": [
{
"id": "credential_query_id",
"format": "jwt_vc_json",
"claims": [
{"path": ["$.vc.credentialSubject.nombre"]},
{"path": ["$.vc.credentialSubject.fechaNacimiento"]}
]
}
]
}
}
| Campo | Descripción |
|---|---|
response_type | Siempre vp_token. |
response_mode | Siempre direct_post. La wallet no soporta otros modos. |
response_uri | URL donde la wallet enviará la VP. |
client_id | Identificador del verificador. Debe coincidir con el aud de la VP. |
nonce | Valor aleatorio para prevenir ataques de replay. La wallet lo incluye en la VP firmada. |
dcql_query | Criterios de selección de credenciales en formato DCQL. |
Estructura del dcql_query
El campo credentials es un array de objetos, cada uno describiendo una credencial requerida:
{
"credentials": [
{
"id": "credential_query_id",
"format": "jwt_vc_json",
"meta": {
"vct_values": ["NombreDelTipoDeCredencial"]
},
"claims": [
{"path": ["$.vc.credentialSubject.nombre"]},
{"path": ["$.vc.credentialSubject.documentoIdentidad"]}
]
}
]
}
| Campo | Descripción |
|---|---|
id | Identificador de la query. Se referencia en el presentation_submission. |
format | Siempre jwt_vc_json. |
meta.vct_values | (Opcional) Tipos de credencial aceptados. |
claims | (Opcional) Atributos específicos requeridos. La wallet verifica que la credencial los contiene. |
Respuesta de la wallet al verificador
La wallet hace POST <response_uri> con body application/x-www-form-urlencoded:
vp_token=<JWT de la VP>&presentation_submission=<JSON URL-encoded>
Estructura del vp_token (VP JWT):
// Header
{
"alg": "ES256K",
"typ": "JWT",
"kid": "did:key:z6Mk...#z6Mk..."
}
// Payload
{
"iss": "did:key:z6MkszZtxCmA2Ce4vUV132PCuLQmwnaDD5mw2L23fGNnsiRn",
"aud": "https://verifier.ejemplo.com",
"nonce": "n-0S6_WzA2Mj",
"iat": 1700000000,
"exp": 1700003600,
"vp": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiablePresentation"],
"verifiableCredential": [
"eyJhbGciOiJFUzI1NiIs..."
]
}
}
El iss es el DID del holder. El aud es el client_id del verificador.
Estructura del presentation_submission:
{
"id": "random-id",
"definition_id": "dcql_query",
"descriptor_map": [
{
"id": "credential_query_id",
"format": "jwt_vp",
"path": "$",
"path_nested": {
"id": "credential_query_id",
"format": "jwt_vc",
"path": "$.vp.verifiableCredential[0]"
}
}
]
}
Respuesta del verificador
Tras recibir la VP, el verificador debe responder con HTTP 200. Puede incluir un redirect_uri en el body para redirigir al holder a una URL de continuación:
{
"redirect_uri": "https://verifier.ejemplo.com/success?session=abc123"
}
Si el verificador no necesita redirigir, puede responder con HTTP 200 y body vacío.
Curvas criptográficas soportadas
| Curva | Identificador JWA | Método DID |
|---|---|---|
| secp256k1 | ES256K | did:key (prefijo zQ3s), did:jwk |
| Ed25519 | EdDSA | did:key (prefijo z6Mk), did:jwk |
La wallet usa la curva configurada al crear la cuenta. Los emisores y verificadores deben declarar en sus metadatos las suites criptográficas que aceptan (cryptographic_suites_supported).
Fuentes de referencia
| Especificación | URL |
|---|---|
| OID4VCI 1.0 | https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html |
| OID4VP 1.0 | https://openid.net/specs/openid-4-verifiable-presentations-1_0.html |
| DCQL | https://identity.foundation/credential-query-language/ |
| W3C VC Data Model 2.0 | https://www.w3.org/TR/vc-data-model-2.0/ |