Skip to content

features.openid4vci

OpenID for Verifiable Credential Issuance 1.0

Specifies whether OpenID4VCI core capabilities shall be enabled. When enabled, the authorization server shall expose the Credential Issuer Metadata, Credential Endpoint, and Nonce Endpoint routes, and perform protocol validation for issuance requests. Supported proof types are jwt and attestation. The attestation proof type relies on Key Attestation JWTs signed by a Wallet Provider; use getKeyAttestationSignaturePublicKey to resolve the attester’s public key.

Credential Offer is an application-level concern outside the scope of the framework. The Issuer constructs the Credential Offer JSON object (containing credential_issuer, credential_configuration_ids, and grants) and delivers it to the Wallet via a custom URL scheme redirect (same-device) or QR code (cross-device). The issuer_state authorization parameter, included in the offer’s grants.authorization_code object and sent back by the Wallet in the authorization request, should be registered via extraParams with a validator callback. Once registered, it becomes available in ctx.oidc.params and is included in the interaction session details automatically. The Wallet’s credential_offer_endpoint client metadata can be supported via extraClientMetadata if needed. The metadata configuration property below can be used to add any additional Credential Issuer Metadata members.

Access to the Credential Endpoint requires an Access Token issued through a user-facing authorization grant (e.g. Authorization Code). The token MUST use the opaque format and its audience MUST equal the value returned by the credentialEndpointExpectedAudience helper. Deployments shall use the features.resourceIndicators mechanism to configure that same value as a resource indicator. Use the defaultResource helper to detect credential-requesting authorization requests and return it as the resource so that the client needs not to use the resource parameter. Use the useGrantedResource helper to return true so that the issued Access Token targets the Credential Endpoint rather than the UserInfo Endpoint.

default value:

{
ack: undefined,
credentialConfigurationPolicy: [Function: openid4vciCredentialConfigurationPolicy], // see expanded details below
credentialConfigurationsSupported: {},
credentialEndpointExpectedAudience: [Function: openid4vciCredentialEndpointExpectedAudience], // see expanded details below
enabled: false,
getKeyAttestationSignaturePublicKey: [AsyncFunction: openid4vciGetKeyAttestationSignaturePublicKey], // see expanded details below
issueCredential: [AsyncFunction: openid4vciIssueCredential], // see expanded details below
metadata: {},
nonceSecret: undefined,
preAuthorizedCodeGrant: false
}

Example: (Click to expand) Configuring resourceIndicators for the Credential Endpoint.

{
features: {
resourceIndicators: {
defaultResource(ctx) {
// detect a credential-requesting authorization request
// and return the credential endpoint URL as the default resource
return ctx.oidc.urlFor('credential');
},
useGrantedResource() {
// skip issuing an Access Token for the UserInfo Endpoint,
// use the granted resource instead
return true;
},
getResourceServerInfo(ctx, resourceIndicator) {
if (resourceIndicator === ctx.oidc.urlFor('credential')) {
return {
scope: 'credential_scope',
// accessTokenFormat is 'opaque' by default
};
}
throw new errors.InvalidTarget();
},
},
},
}
(Click to expand) features.openid4vci options details

Specifies a helper function that shall be invoked at runtime to decide whether a specific credential configuration is currently issuable for the current request context.

default value:

function openid4vciCredentialConfigurationPolicy(
ctx,
{
credentialConfigurationId,
credentialConfiguration,
credentialIdentifier,
client,
account,
grant,
accessToken,
},
) {
return true;
}

Example: (Click to expand) Allowing issuance only for a specific client and granted scope.

{
async credentialConfigurationPolicy(ctx, {
credentialConfigurationId,
credentialConfiguration,
client,
account,
grant,
accessToken,
}) {
return client.clientId === 'wallet-app'
&& accessToken.scopes.has('mdl_scope');
},
}

Specifies static Credential Issuer metadata values for credential_configurations_supported.

default value:

{}

Example: (Click to expand) Defining credential configurations.

{
credentialConfigurationsSupported: {
'org.iso.18013.5.1.mDL': {
format: 'mso_mdoc',
doctype: 'org.iso.18013.5.1.mDL',
scope: 'mdl_scope',
cryptographic_binding_methods_supported: ['jwk'],
proof_types_supported: {
jwt: {
proof_signing_alg_values_supported: ['ES256'],
key_attestations_required: {
key_storage: ['iso_18045_high', 'iso_18045_moderate'],
user_authentication: ['iso_18045_moderate'],
},
},
},
},
'org.iso.18013.5.1.mDL.attestation': {
format: 'mso_mdoc',
doctype: 'org.iso.18013.5.1.mDL',
scope: 'mdl_scope',
cryptographic_binding_methods_supported: ['jwk'],
proof_types_supported: {
attestation: {
proof_signing_alg_values_supported: ['ES256'],
key_attestations_required: {
key_storage: ['iso_18045_high', 'iso_18045_moderate'],
user_authentication: ['iso_18045_moderate'],
},
},
},
},
SD_JWT_VC_example_in_OpenID4VCI: {
format: 'dc+sd-jwt',
vct: 'SD_JWT_VC_example_in_OpenID4VCI',
scope: 'mdl_scope',
},
},
}

Specifies a helper function that shall be invoked to resolve the value the Access Token’s aud claim must equal in order to access the Credential Endpoint. It shall return a non-empty string.

The default derives the Credential Endpoint URL from the incoming request, which only resolves consistently when the Credential Endpoint and the Token Endpoint are served on the same host. Deployments serving the Credential Endpoint on another host, such as a mutual-TLS host, shall return a fixed absolute URL from this helper.

Whatever this helper returns MUST equal the resource indicator the Access Token was issued for; this helper and the features.resourceIndicators configuration are two halves of the same contract. Note that OpenID4VCI recommends the Credential Issuer Identifier (ctx.oidc.issuer) as the resource parameter value, which is another value that does not vary with the host serving the request.

default value:

function openid4vciCredentialEndpointExpectedAudience(ctx) {
return ctx.oidc.urlFor('credential');
}

Specifies a helper function used to resolve the public key for verifying Key Attestation JWT (typ key-attestation+jwt) signatures when the attestation proof type is used. At the point of invocation the JWT format and iss claim presence have been validated; no cryptographic or further claims verification has occurred yet.

The function receives the Koa request context, the iss (Wallet Provider identifier) from the Key Attestation JWT, the decoded JOSE protected header, and the Client instance. It MUST return a public key as a CryptoKey, KeyObject, or JWK object. If the function throws, the credential endpoint responds with invalid_proof.

default value:

async function openid4vciGetKeyAttestationSignaturePublicKey(ctx, iss, header, client) {
// @param ctx - koa request context
// @param iss - Issuer Identifier from the Key Attestation JWT
// @param header - Protected Header of the Key Attestation JWT
// @param client - the Client instance
throw new Error('features.openid4vci.getKeyAttestationSignaturePublicKey not implemented');
}

Example: (Click to expand) Fetching attester public keys from the attester’s hosted JWKS

import * as jose from 'jose';
const attesters = new Map(Object.entries({
'https://attester.example.com': jose.createRemoteJWKSet(new URL('https://attester.example.com/jwks')),
}));
function getKeyAttestationSignaturePublicKey(ctx, iss, header, client) {
if (attesters.has(iss)) return attesters.get(iss)(header);
throw new Error('unsupported key attestation issuer');
}

Specifies a helper function that shall be invoked to perform actual credential issuance and return credential response payloads. By the time this function is called all proof signatures, algorithms, types, required claims (iat, nonce, aud), and c_nonce challenges have already been validated.

When proofs is present it contains a single key whose name is the proof type:

  • jwt The value is the original array of compact JWS strings. When the JWT proof(s) contain a key_attestation JOSE header parameter, the pre-parsed key attestation data is available as proofs.key_attestation with:

    • jwt {string} The Key Attestation JWT compact serialization.
    • attestedKeys {Object[]} The attested_keys claim (array of JWK objects).
    • payload {Object} The full Key Attestation JWT payload, including optional claims such as key_storage, user_authentication, and certification.
  • attestation The value is a pre-parsed object with:

    • jwt {string} The original Key Attestation JWT compact serialization.
    • attestedKeys {Object[]} The attested_keys claim (array of JWK objects).
    • payload {Object} The full Key Attestation JWT payload, including optional claims such as key_storage, user_authentication, and certification when present. If key_attestations_required is configured for the credential configuration, the required claims have been validated to contain at least one matching value before this function is called.

default value:

async function openid4vciIssueCredential(
ctx,
{
credentialConfigurationId,
credentialConfiguration,
credentialIdentifier,
body,
proofs,
client,
account,
grant,
accessToken,
},
) {
throw new Error('features.openid4vci.issueCredential not implemented');
}

Example: (Click to expand) Returning one credential in the response.

{
async issueCredential(ctx, {
credentialConfigurationId,
credentialConfiguration,
body,
proofs,
client,
account,
grant,
accessToken,
}) {
return {
credentials: [
{ credential: '...serialized-credential...' },
],
};
},
}

Free-form object with additional top-level members to be merged into the Credential Issuer Metadata response.

default value:

{}

Specifies the cryptographic secret used to generate and validate OpenID4VCI c_nonce challenges exposed by the nonce endpoint. This value MUST be a 32-byte Buffer instance. c_nonce values are derived from this secret rather than stored; the same value MUST be configured on all instances of a deployment and kept stable across restarts.

Specifies whether the OpenID4VCI Pre-Authorized Code Flow shall be enabled. When enabled, the authorization server shall accept urn:ietf:params:oauth:grant-type:pre-authorized_code grant type exchanges at the token endpoint. Clients using this grant type must have it registered in their grant_types client metadata.

Pre-authorized codes represent issuance authorization obtained through means outside of the protocol exchanges defined by this framework. Creating them, together with their underlying Grant, is an application-level concern, as is delivering them to the Wallet inside a Credential Offer.

Pre-authorized codes are single-use. An optional Transaction Code (txCode property, a string) may be attached to a pre-authorized code, in which case the Wallet-provided tx_code parameter presence is validated before the code is consumed and its value is then compared in constant time, a failed comparison revokes the pre-authorized code and its underlying Grant.

No ID Token is issued as part of this grant type’s token exchange.

default value:

false

Example: (Click to expand) Minting a pre-authorized code.

const grant = new provider.Grant({
accountId,
clientId,
});
grant.addResourceScope(resource, 'credential_scope');
const grantId = await grant.save();
const code = new provider.PreAuthorizedCode({
accountId,
clientId,
grantId,
resource,
scope: 'credential_scope',
txCode: '493536', // optional
});
// deliver as grants['urn:ietf:params:oauth:grant-type:pre-authorized_code']['pre-authorized_code']
// in a Credential Offer
const preAuthorizedCode = await code.save();