Skip to main content

OID4VCI and OID4VP Reference

This page is the technical reference for integrators building issuers or verifiers that interoperate with the ISBE Holder Wallet.

The wallet acts as a client in both protocols. It does not expose any public API of its own; it is the issuers and verifiers who must expose the endpoints described here.


OID4VCI — Credential reception

The wallet implements the pre-authorized flow of OpenID for Verifiable Credential Issuance 1.0.

Endpoints the issuer must expose

EndpointDescription
GET /.well-known/openid-credential-issuerIssuer metadata.
POST /tokenToken endpoint. Exchanges the pre-authorized_code for an access_token.
POST /credentialCredential endpoint. Returns the signed credential.
POST /deferred_credential(Optional) Deferred credential endpoint. For deferred issuance.

Issuer metadata (openid-credential-issuer)

The issuer must publish its metadata at /.well-known/openid-credential-issuer. The wallet fetches them when processing the credential_offer.

Minimum required fields:

{
"credential_issuer": "https://issuer.example.redisbe.com",
"credential_endpoint": "https://issuer.example.redisbe.com/credential",
"credentials_supported": [
{
"format": "jwt_vc_json",
"id": "CredentialTypeName",
"types": ["VerifiableCredential", "CredentialTypeName"],
"cryptographic_binding_methods_supported": ["did:key", "did:jwk"],
"cryptographic_suites_supported": ["ES256K", "EdDSA"]
}
]
}

credential_offer structure

The offer can be delivered directly in the QR/deep link or by reference via credential_offer_uri.

By value:

openid-credential-offer://?credential_offer=<URL-encoded JSON>

By reference:

openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fissuer.example.com%2Foffer%2Fabc123

credential_offer object:

{
"credential_issuer": "https://issuer.example.redisbe.com",
"credentials": ["CredentialTypeName"],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "SplxlOBeZQQYbYS6WxSbIA",
"tx_code": {
"length": 6,
"input_mode": "numeric",
"description": "Enter the code received via SMS"
}
}
}
}

The tx_code field is optional. If the issuer does not require a PIN, omit the field entirely.

Token endpoint

The wallet makes POST /token with the following parameters in the body (form-urlencoded):

ParameterValue
grant_typeurn:ietf:params:oauth:grant-type:pre-authorized_code
pre-authorized_codeThe code from the credential_offer.
tx_code(Conditional) The PIN entered by the holder, if the issuer required it.

Successful response:

{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 86400,
"c_nonce": "tZignsnFbp",
"c_nonce_expires_in": 86400
}

The c_nonce field is optional but required if the issuer demands proof of possession at the /credential endpoint.

Credential endpoint

The wallet makes POST /credential with:

  • Header: Authorization: Bearer <access_token>
  • JSON body:
{
"format": "jwt_vc_json",
"types": ["VerifiableCredential", "CredentialTypeName"],
"proof": {
"proof_type": "jwt",
"jwt": "<JWT signed with the holder's private key>"
}
}

proof.jwt structure:

The proof of possession JWT is signed by the holder and contains:

{
"alg": "ES256K",
"typ": "openid4vci-proof+jwt",
"kid": "did:key:z6Mk...#z6Mk..."
}
.
{
"iss": "did:key:z6MkszZtxCmA2Ce4vUV132PCuLQmwnaDD5mw2L23fGNnsiRn",
"aud": "https://issuer.example.redisbe.com",
"iat": 1700000000,
"nonce": "tZignsnFbp"
}

The nonce in the proof.jwt must match the c_nonce returned by the token endpoint.

Successful response (immediate issuance):

{
"format": "jwt_vc_json",
"credential": "eyJhbGciOiJFUzI1NiIs..."
}

Response for deferred issuance:

{
"acceptance_token": "eyJhbGciOiJSUzI1NiIs...",
"c_nonce": "wlbQc6pCJp",
"c_nonce_expires_in": 86400
}

Deferred credential endpoint

If the issuer returns an acceptance_token, the wallet will periodically query POST /deferred_credential:

{
"acceptance_token": "eyJhbGciOiJSUzI1NiIs..."
}

While the credential is not ready, the issuer responds with HTTP 202. When it is ready, it returns the credential with HTTP 200 in the same format as the /credential endpoint.


OID4VP — Credential presentation

The wallet implements OpenID for Verifiable Presentations 1.0 with DCQL (Digital Credentials Query Language).

Only DCQL — not DIF Presentation Exchange

The ISBE wallet uses DCQL for presentation criteria. The presentation_definition object from DIF PEv2 is not supported. Verifiers must use the dcql_query field in the authorization_request.

Presentation flow

By value:

openid4vp://?<URL-encoded authorization_request parameters>

By reference (recommended):

openid4vp://?request_uri=https%3A%2F%2Fverifier.example.com%2Frequest%2Fabc123

authorization_request structure

The verifier can deliver the authorization_request as a URL or as a signed JWT. Required fields:

{
"response_type": "vp_token",
"response_mode": "direct_post",
"response_uri": "https://verifier.example.com/response",
"client_id": "https://verifier.example.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"]}
]
}
]
}
}
FieldDescription
response_typeAlways vp_token.
response_modeAlways direct_post. The wallet does not support other modes.
response_uriURL where the wallet will send the VP.
client_idVerifier identifier. Must match the aud of the VP.
nonceRandom value to prevent replay attacks. The wallet includes it in the signed VP.
dcql_queryCredential selection criteria in DCQL format.

dcql_query structure

The credentials field is an array of objects, each describing a required credential:

{
"credentials": [
{
"id": "credential_query_id",
"format": "jwt_vc_json",
"meta": {
"vct_values": ["CredentialTypeName"]
},
"claims": [
{"path": ["$.vc.credentialSubject.nombre"]},
{"path": ["$.vc.credentialSubject.documentoIdentidad"]}
]
}
]
}
FieldDescription
idQuery identifier. Referenced in the presentation_submission.
formatAlways jwt_vc_json.
meta.vct_values(Optional) Accepted credential types.
claims(Optional) Specific required attributes. The wallet checks the credential contains them.

Wallet response to the verifier

The wallet makes POST <response_uri> with body application/x-www-form-urlencoded:

vp_token=<VP JWT>&presentation_submission=<URL-encoded JSON>

vp_token structure (VP JWT):

// Header
{
"alg": "ES256K",
"typ": "JWT",
"kid": "did:key:z6Mk...#z6Mk..."
}
// Payload
{
"iss": "did:key:z6MkszZtxCmA2Ce4vUV132PCuLQmwnaDD5mw2L23fGNnsiRn",
"aud": "https://verifier.example.com",
"nonce": "n-0S6_WzA2Mj",
"iat": 1700000000,
"exp": 1700003600,
"vp": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiablePresentation"],
"verifiableCredential": [
"eyJhbGciOiJFUzI1NiIs..."
]
}
}

The iss is the holder's DID. The aud is the verifier's client_id.

presentation_submission structure:

{
"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]"
}
}
]
}

Verifier response

After receiving the VP, the verifier must respond with HTTP 200. It can include a redirect_uri in the body to redirect the holder to a continuation URL:

{
"redirect_uri": "https://verifier.example.com/success?session=abc123"
}

If the verifier does not need to redirect, it can respond with HTTP 200 and an empty body.


Supported cryptographic curves

CurveJWA identifierDID method
secp256k1ES256Kdid:key (prefix zQ3s), did:jwk
Ed25519EdDSAdid:key (prefix z6Mk), did:jwk

The wallet uses the curve configured when creating the account. Issuers and verifiers must declare the cryptographic suites they accept in their metadata (cryptographic_suites_supported).


Reference sources

SpecificationURL
OID4VCI 1.0https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
OID4VP 1.0https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
DCQLhttps://identity.foundation/credential-query-language/
W3C VC Data Model 2.0https://www.w3.org/TR/vc-data-model-2.0/