OpenID for Verifiable Credential Issuance

1. Introduction

This document explains the “OpenID for Verifiable Credential Issuance” (OID4VCI) specification and how Authlete supports the specification.

This document elaborately explains overviews and details of various concepts, using well over 100 diagrams. All the explanations carefully avoid assuming prior knowledge of concepts not yet explained, allowing readers to understand the content without the need to navigate back and forth within the document. Additionally, the document refrains from delving into excessive details while presenting the overall picture to prevent readers from getting disoriented. These considerations make this document significantly more readable than the specification itself. Therefore, reading this document beforehand will be a great help when you read the specification.

Revision History
Date Changes
2023-10-22 The initial version was released.
2023-10-27 Some subsections were added to the "OID4VCI Implementation" section.
2023-11-15 Some explanations and diagrams were updated to align with the specification changes below.

  • The type of the credentials_supported credential issuer metadata has been changed from a JSON array to a JSON object.
  • The type of elements in the credentials array in a credential offer has been restricted to "string" only. JSON objects are no longer accepted.
  • The elements in the credentials array in a credential offer reference the "keys" in the credentials_supported object, not the values of the scope property of elements in the credentials_supported object.
2023-11-24 The "OID4VCI Demo" section was added.

Some explanations and diagrams were updated to align with the specification change below.

  • The draft 01 of the SD-JWT VC specification has renamed the type claim to vct, which means "verifiable credential type".

2. OID4VCI Specification

The OID4VCI specification defines rules for issuance of verifiable credentials.

2.1. Core Technical Terms

2.1.1. Verifiable Credential

Verifiable credential” is a key technical term in the OID4VCI specification.

“Credential” in the term represents a collection of data about a user or users (or any identifiable entities). Given name, family name, and birthdate are examples of data about a user.

“Verifiable” in the term indicates that it is possible to verify that the data collection has not been tampered with. Technically speaking, it means that the data collection is digitally signed.

Digital driving licenses or health insurance cards stored on a mobile device are examples of verifiable credentials.


2.1.2. Credential Issuer

Verifiable credentials are issued by a “credential issuer”. Credential issuer is also a technical term. The specification describes behaviors of a credential issuer.

2.1.3. Access Token

To obtain a verifiable credential from a credential issuer, the requester of the issuance must present an “access token” to the credential issuer. The access token here is the one defined in RFC 6749, which is the core specification of OAuth 2.0.

2.1.4. Authorization Server

Access tokens are issued by an “authorization server”. The fundamental behaviors of an authorization server are defined in RFC 6749, and there are many other standard specifications around RFC 6749 that add extra functionalities to an authorization server. The OID4VCI specification also defines additional requirements for an authorization server so that an authorization server can issue access tokens that can be used for the issuance of verifiable credentials.

2.1.5. Wallet

In the OID4VCI specification, a software application that communicates with an authorization server and a credential issuer to obtain a verifiable credential is referred to as a “wallet”. Technically speaking, within the context of issuing verifiable credentials, a wallet acts as a “client application” of OAuth 2.0. Thus, from a technical perspective, the terms wallet and client application are interchangeable in the context of the OID4VCI specification.

2.1.6. Relationship

The following diagram illustrates the relationship among the core technical terms.

2.2. Access Token Issuance Overview

2.2.1. Pre-Authorized Code Flow

The specification defines multiple methods for issuing access tokens that are usable for the issuance of verifiable credentials.

One of these methods is entirely new. The new one is referred to as the “pre-authorized code flow”. In the flow, as the first step, a wallet obtains a “pre-authorized code” from a credential issuer.

Then, the wallet presents the pre-authorized code at the “token endpoint” (RFC 6749, 3.2. Token Endpoint) of an authorization server.

In return, the wallet receives an access token.

As explained in the previous section, the wallet presents the access token to the credential issuer. To be specific, the wallet presents the access token at the “credential endpoint” of the credential issuer.

In return, the wallet receives a verifiable credential.

The diagram below is an overview of the pre-authorized code flow.

2.2.2. Authorization Code Flow

The other methods than the pre-authorized code flow are extensions of the traditional “authorization code flow” (RFC 6749, 4.1. Authorization Code Grant).

Let’s review the flow.

In the authorization code flow, as the first step, a client application (which is a wallet in the OID4VCI context) sends an “authorization request” to the “authorization endpoint” (RFC 6749, 3.1. Authorization Endpoint) of an authorization server via a web browser.

On receiving the authorization request, the authorization server begins communicating with a user via the web browser. After obtaining consent from the user, the authorization server issues an “authorization code” to the client application.

Then, the client application sends a “token request” including the authorization code to the token endpoint of the authorization server.

In return, the client application receives an access token.

The process after getting an access token is the same as the one of the pre-authorized code flow. The client application presents the access token at the credential endpoint of the credential issuer.

In return, the client application receives a verifiable credential.

The diagram below illustrates the authorization code flow followed by the credential issuance.

The OID4VCI specification extends the authorization request in the authorization code flow. To be specific, the specification utilizes the following request parameters of an authorization request.

  1. The issuer_state request parameter.
  2. The authorization_details request parameter.
  3. The scope request parameter.

The issuer_state request parameter is a new one defined by the OID4VCI specification.

The authorization_details request parameter is defined in “RFC 9396 OAuth 2.0 Rich Authorization Requests”, a.k.a. “RAR”.

The scope request parameter is a traditional one defined in “RFC 6749 The OAuth 2.0 Authorization Framework”.

2.2.3. Authorization Code Flow + issuer_state

The issuer_state request parameter is defined in the OID4VCI specification. To use the request parameter, a wallet needs to obtain an “issuer state” from a credential issuer before making an authorization request.

Then, the wallet makes an authorization request with the issuer state included as the value of the issuer_state request parameter.

The remaining part after the authorization request is the same as that of the normal authorization code flow.

The diagram below illustrates the authorization code flow with an issuer state.

2.2.4. Authorization Code Flow + authorization_details

The RAR specification (RFC 9396) defines the authorization_details parameter as a general-purpose parameter that conveys detailed information about authorization. It is up to deployments how to use the parameter.

The value of the parameter is a JSON array, and each element of the array is a JSON object. We call the object “RAR object”.

The RAR object is flexible. Any properties can be put in the object. However, the RAR specification predefines several properties that are expected to be commonly used across the foreseeable use cases.

Among such predefined properties, the "type" property is the only mandatory property. The property indicates what the RAR object represents.

And, the OID4VCI specification defines a special value, "openid_credential", for the "type" property in order to indicate that the RAR object conveys information about the verifiable credential that the wallet wants.

2.2.5. Authorization Code Flow + scope

The scope request parameter is one of the traditional ones defined in the core specification of OAuth 2.0 (RFC 6749). Its original usage is to list permissions that the client application wants. If the user approves the request, the authorization server issues an access token that has the requested permissions.

Historically, the scope request parameter has been used for purposes beyond its original intent, and the OID4VCI specification has similarly extended the use of the scope request parameter.

A credential issuer manages the types of verifiable credentials it can issue as “supported credentials”, and publishes the list of the supported credentials at a certain place. Each supported credential may have a "scope" property.

A wallet may include values of the "scope" property in the scope request parameter to indicate which type of verifiable credentials it wants.

Multiple supported credentials may have the same value for the "scope" property.

2.3. Credential Issuance Overview

Once a wallet obtains an access token from an authorization server, the wallet can request a credential issuer to issue a verifiable credential by presenting the access token.

In the foundational procedure, the wallet sends a credential request with an access token to the credential endpoint of the credential issuer.

The credential issuer issues the requested verifiable credential as a response.

2.3.1. Deferred Credential Issuance

However, it is possible that the verifiable credential is not yet available when requested. For example, there might be time-consuming offline processes happening in the background.

In such a case, the credential issuer issues a “transaction ID” instead.

In this case, the wallet waits until the verifiable credential issuance is ready. Then, it presents the previously received transaction ID and access token to the “deferred credential endpoint”.

The credential issuer issues the requested verifiable credential as a response.

If the verifiable credential is still not ready, the deferred credential endpoint will return an error indicating it (e.g., "error":"issuance_pending"). In this case, the wallet will make a “deferred credential request” again later.


2.3.2. Batch Credential Issuance

The wallet may want to obtain multiple verifiable credentials at a time. For such use cases, there is a “batch credential endpoint”.

The wallet sends a “batch credential request” with an access token to the batch credential endpoint.

The endpoint returns multiple verifiable credentials and/or transaction IDs.

Each transaction ID can be used to obtain a verifiable credential from the deferred credential endpoint.

2.4. Access Token Issuance Details

In the previous sections, we’ve provided an overview of access token issuance and credential issuance. In this section, we will delve into the technical details of access token issuance.

However, even at the time of writing this document, there are still open issues and pull requests, some of which may involve breaking changes. Therefore, please keep in mind that technical details written from here are subject to breaking changes.


2.4.1. Credential Offer

When a credential issuer issues a pre-authorized code, it provides a “credential offer” that includes the pre-authorized code instead of issuing it directly.

Likewise, an issuer state is also included as part of a credential offer. A credential offer may contain either a pre-authorized code, an issuer state, or both.

A credential offer contains other information, too. It always contains the identifier of the credential issuer.

Also, a credential offer contains information about the verifiable credentials that the credential issuer offers.

2.4.1.1. Credential Offer Issuance by Value

To transmit the credential offer to the wallet, a URL is employed. This URL is a “credential offer endpoint” with a query parameter credential_offer. The value of the query parameter is the content of the credential offer.

If the URL is accessed in some way and the access can be processed by the wallet, the wallet can receive the credential offer.

However, a key issue here is how to trigger the access. The OID4VCI specification anticipates an HTTP GET request or HTTP redirection initiated by the credential issuer, but it does not define how the credential issuer and the wallet should agree upon the method of triggering the access.

Another issue is that the credential issuer will not be able to know the value of the credential offer endpoint when providing a credential offer. The specification defines a client metadata called credential_offer_endpoint, which represents the wallet’s credential offer endpoint. However, especially in cases where a QR code representing the URL is used as suggested by the specification, the credential issuer do not have access to the wallet’s metadata because the credential issuer cannot know for which wallet it is going to provide a credential offer. For such cases, openid-credential-offer:// is defined as the fallback credential offer endpoint.


2.4.1.2. Credential Offer Issuance by Reference

A credential offer may be passed to the wallet by reference. To be specific, the URL may contain the location of the issued credential offer instead of its content. In that case, a credential_offer_uri query parameter is used to point to the location.

The value of the credential_offer_uri query parameter points to an endpoint that returns the content of the issued credential offer.

By accessing the URI,

the wallet can obtain the content of the issued credential offer.

2.4.1.3. Credential Offer Content

The actual content of a credential offer is a JSON object.

The identifier of the credential issuer is put as the value of the "credential_issuer" property.

The information about the verifiable credentials that the credential issuer offers is put in the "credentials" array. The specific details of the array elements are discussed later.

When issued, an issuer state is placed at a somewhat nested location.

There is a "grants" property as a top-level property in a credential offer. The value of the "grants" property is a JSON object. The keys within the "grants" JSON object are identifiers of grant types, such as authorization_code.

The value of each entry in the "grants" JSON object is another JSON object containing properties related to the grant type represented by the corresponding key.

In the case of the issuer state, the value of the issued issuer state is placed as the value of the "issuer_state" property within the "authorization_code" JSON object, which is within the "grants" JSON object.

Similarly, in the case of the pre-authorized code, the value of the issued pre-authorized code is placed as the value of the "pre-authorized_code" property within the "urn:ietf:params:oauth:grant-type:pre-authorized_code" JSON object, which is within the "grants" JSON object. The string "urn:ietf:params:oauth:grant-type:pre-authorized_code" here is the new identifier assigned to the pre-authorized code flow.

The "urn:ietf:params:oauth:grant-type:pre-authorized_code" JSON object may contain a "user_pin_required" property, with a value of true or false. When the value is true, the token request using the pre-authorized code will have to include a PIN code. Further details about this are described later.

The diagram below illustrates an overview of the structure of the content of a credential offer.

2.4.1.4. “credentials” in Credential Offer

The "credentials" property in a credential offer holds information about the verifiable credentials that the credential issuer offers. The value of the property is a JSON array. The elements in the array are strings.

The values of the elements are the identifiers of the supported credentials.

2.4.2. Pre-Authorized Code Flow Details

Once a wallet obtains a pre-authorized code, it can make a token request with the pre-authorized code.

The table below lists the request parameters required for a token request to comply with the pre-authorized code flow.

Parameter Description
grant_type The value must be "urn:ietf:params:oauth:grant-type:pre-authorized_code".
pre-authorized_code A pre-authorized code.
user_pin A user-supplied PIN. This parameter is required if the pre-authorized code has been issued with "user_pin_required":true. In that case, it is expected that the user has already received a PIN code corresponding to the pre-authorized code through some out-of-band mechanism.

Also, additional request parameters related to client authentication may be required. For example, when the private_key_jwt client authentication is employed, the client_assertion and client_assertion_type request parameters are required.

The following is an example of a token request without client authentication using the pre-authorized code flow, excerpted from the OID4VCI specification.

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code
&pre-authorized_code=SplxlOBeZQQYbYS6WxSbIA
&user_pin=493536

The token endpoint will return a response including an access token as usual. The following is an example of token response excerpted from the specification.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ",
  "token_type": "bearer",
  "expires_in": 86400,
  "c_nonce": "tZignsnFbp",
  "c_nonce_expires_in": 86400
}

As the example above shows, when the issued access token can be used for the issuance of verifiable credentials, the token response may contain the c_nonce and c_nonce_expires_in response parameters, in addition to the traditional response parameters (access_token, token_type and expires_in) defined in the core specification of OAuth 2.0 (RFC 6749). Further details regarding these response parameters will be explained later.


2.4.3. Issuable Credentials

While there are multiple methods available to issue access tokens for verifiable credentials, from the perspective of access token implementation, they all converge on one common goal. That is to associate information about the types of verifiable credentials that can be issued with the access token.

In this document, we call such information “issuable credential”. But, please keep in mind that “issuable credential” is not an official term.

Elements in the "credentials" array in a credential offer are JSON strings. They indirectly specify issuable credentials by referencing supported credentials.

The number of elements in the "credentials" array in this example is one, but the array may contain multiple elements. Such elements compose the set of issuable credentials represented by the credential offer.

Therefore, it can be said that a token request using the pre-authorized code flow requests an access token associated with the issuable credentials specified in the credential offer that contains the pre-authorized code.

Similarly, it can be said that an authorization request with the issuer_state request parameter requests an access token associated with the issuable credentials specified in the credential offer that contains the issuer state.

Values in the scope request parameter may indirectly specify one or more issuable credentials via the "scope" property of supported credentials.

RAR objects with "type":"openid_credential" directly specify issuable credentials.

Although the current draft of the OID4VCI specification does not explicitly address the cases where these mechanisms to specify issuable credentials are used simultaneously, in our interpretation and implementation, all the issuable credentials specified by these mechanisms are combined into a single set.

The diagram below illustrates the overall picture of the mechanisms to specify issuable credentials.

You might be curious about the credential information in the diagram.

However, before delving into those specifics, we need to walk through the formats of verifiable credentials.

2.5. Credential Verification

In order to discuss the formats of verifiable credentials, we need to understand their intended purpose. Let’s examine the verification steps of a verifiable credential, one by one.

First, the credential issuer prepares data to include in a verifiable credential. In this example, we use name, birthdate, and address.

To sign the data, the credential issuer prepares a pair of a private key and a public key.

Then, the credential issuer signs the data with the private key. As a result, a signature is generated.

The set of the data and the signature is a verifiable credential.

The credential issuer passes the verifiable credential to the wallet.

If the wallet wants to verify the signature of the verifiable credential, it obtains the public key from the credential issuer through some means or other, and uses it to verify the signature. If the verification succeeds, it can be ensured that the verifiable credential has been issued by the legitimate credential issuer and the content has not been tampered with.

The key distinction between the use of a verifiable credential and an ID token is that the “holder” of a verifiable credential may present it to others. Before presented, a verifiable credential is transformed into a “verifiable presentation”.

The wallet presents the verifiable presentation to others. The external entities receiving the verifiable presentation are referred to as “verifiers” because they are supposed to verify the verifiable presentation before providing services to the holder.

If the verifier wants to verify the signature of the verifiable presentation, it obtains the public key from the credential issuer through some means or other, and uses it to verify the signature.

2.5.1. Key Binding

By the way, how can a verifier confirm the following?

  1. The verifiable presentation has been presented by the holder.
  2. The verifiable credential underlying the verifiable presentation has been issued by the credential issuer to the holder.

As for the first point, it can be achieved by having the holder provide a pair of a private key and a public key,

and including a signature made with the holder’s private key in the verifiable presentation. The target data for the signature can be arbitrary as long as the data is presented together with the signature.

As for the second point, it can be achieved by including the holder’s public key in the verifiable credential’s data and having the credential issuer sign the entire data with its private key.

The cryptographic association between the verifiable credential and the holder in this manner is referred to as “key binding”.

When the wallet requests a verifiable credential with cryptographic key binding, it includes the public key in the credential request. However, the credential issuer should not unconditionally accept the presented public key, as a malicious wallet could present an irrelevant public key.

Therefore, the wallet must demonstrate the legitimate ownership of the public key. To achieve this, the wallet generates a signature using the private key and presents it alongside the public key. This combination of the signature and the public key is commonly known as a “key proof”.

If the credential issuer can validate the presented public key, it can create a verifiable credential with key binding.

The verifiable credential is then passed to the wallet,

which uses it to create a verifiable presentation. The wallet includes a signature created using the private key in the verifiable presentation.

The verifiable presentation is then passed to the verifier.

The verifier can verify the signature added by the wallet by using the public key embedded in the verifiable presentation.

The following diagram illustrates the overall picture of the credential verification explained so far.

2.6. Selective Disclosure

When presenting a verifiable presentation, the holder may choose to disclose only certain parts of the verifiable credential’s content. For example, if the verifiable credential contains name, birthdate, and address, the holder may opt to disclose only the name and birthdate while omitting the address information.

We call the act of selectively disclosing chosen information like this “selective disclosure”.

However, omitting information without special consideration will result in the failure of the signature verification. This is because the dataset targeted for signing differs from the dataset received by the verifier.

There are several methods to achieve selective disclosure without invalidating the signature. BBS+ (Boneh-Lynn-Shacham signature plus) and CL Signatures (Camenisch-Lysyanskaya Signatures) are examples and may seem promising. However, in the real world, the adoption of these methods depends on various factors, as outlined below, and it is not always the case that solutions based on academically elegant theories become widespread:

  1. The complexity of the theory.
  2. The ease of implementation.
  3. Monetary costs for licensing.
  4. Legal restrictions on usage.
  5. Support from Hardware Security Module (HSM) products.
  6. How much the industry believes the algorithm is robust/secure.

After thorough comparison of credential profiles and hackathons, the industry has decided to create a new format called “Selective Disclosure for JWTs (SD-JWT)”.


2.6.1. SD-JWT

SD-JWT is a format that utilizes JWT (RFC 7519 JSON Web Token (JWT)) to achieve selective disclosure.

The payload part of a normal JWT contains pairs of a claim name and its value.

To make such a claim “selectively-disclosable” using the SD-JWT format, you first extract the claim.

Then, you add an arbitrary salt to it,

and create a JSON array including the salt, the claim name and the claim value.

The next step is to encode that JSON array in base64url. In the SD-JWT specification, the resulting string is referred to as “disclosure”.

The original claim is replaced with the digest value of the disclosure. The digest value is base64url-encoded and placed in the "_sd" array, which is inserted where the original claim was located.

The same process is applied to other claims that need to be made selectively-disclosable.

By concatenating the “issuer-signed JWT” with the disclosures using tildes (~), a single string is formed.

This resulting string is an SD-JWT.

The next step is optional, but if you want to perform key binding, please prepare a key pair.

Then, embed the public key into the issuer-signed JWT,

sign a specific dataset defined in the specification, and place the resulting JWT at the end of the previously created SD-JWT. The JWT is called “key binding JWT”.

The following diagram illustrates the overall process of generating SD-JWT that we have explained so far.

The key point is that if the recipient of an SD-JWT doesn’t receive all the disclosures, they can only reconstruct claims corresponding to the received disclosures. Importantly, even in the case, the signature of the issuer-signed JWT remains valid.

For more detailed information, please refer to the SD-JWT specification itself. Additionally, you can find useful information in the README of the open-source SD-JWT library for the Java programming language, authlete/sd-jwt.

2.7. Verifiable Credential Formats

2.7.1. Confusion Surrounding Verifiable Credential Formats

The confusion surrounding verifiable credential formats stems from the existence of multiple competing specifications, each with its own set of challenges, and many of which are still in development. Furthermore, the fact that organizations from various countries, regions, and industries are promoting different formats is also complicating the situation.

When it comes to verifiable credentials, many people associate them with the “W3C Verifiable Credentials Data Model” (W3C VCDM). This is primarily because the document is often seen as the primary source defining the three-party Issuer-Holder-Verifier model. However, W3C VCDM itself is not flawless, and discussions are indeed ongoing. While version 1.1 was released on March 3, 2022, version 2.0 is currently under discussion.

When observed from an external perspective, what further complicates the situation is the specification titled “Securing Verifiable Credentials using JOSE and COSE” (w3c/vc-jose-cose). The specification states in the “Abstract” section that it “defines how to secure credentials and presentations conforming to the VC-DATA-MODEL”, but it conflicts with certain requirements of W3C VCDM. For instance, W3C VCDM mandates that the value of the "typ" header parameter must be "JWT", but this requirement is not followed by w3c/vc-jose-cose. Additionally, W3C VCDM introduces the "vc" and "vp" claims as the designated places to embed verifiable credentials and verifiable presentations. However, w3c/vc-jose-cose does not utilize these "vc" and "vp" claims.

Furthermore, what adds to the confusion for newcomers is that the OID4VCI specification defines jwt_vc_json, jwt_vc_json-ld, and ldp_vc as credential format profiles based on W3C VCDM, but most people in the OpenID industry contributing to the specification do not seem inclined to support these credential format profiles. They are currently dedicating their efforts to the specification development and implementation of verifiable credential formats based on SD-JWT and “ISO/IEC 18013-5” (Personal identification - ISO-compliant driving licence - Part 5: Mobile driving licence (mDL) application).

For those discussing OAuth and OpenID Connect, ISO/IEC 18013-5 is challenging to approach because its format is based on the less familiar binary format, “Concise Binary Object Representation” (CBOR) (RFC 8949) and “CBOR Object Signing and Encryption” (COSE) (RFC 9052, RFC 9053). Additionally, detailed technical articles about it are not widely available online because ISO standards must be purchased.

The public key distribution method for verifying verifiable credentials is also a challenging issue. When a verifier receives a verifiable credential, they cannot determine whether it was issued according to the OID4VCI specification. Therefore, it is not ideal to force verifiers to search for public keys starting from the metadata of credential issuers (/.well-known/openid-credential-issuer).

As an alternative starting point, the well-known path /.well-known/jwt-issuer is proposed in the specification called “SD-JWT-based Verifiable Credentials (SD-JWT VC)”. However, this path name is so generic that it can easily clash with other JWT issuer-related specifications. Additionally, whether the verifiable credential format is JWT or not is not essential. Therefore, some people are not favorable to the solution. In fact, the Italian ecosystem that leverages OpenID Federation has opted not to use /.well-known/jwt-issuer. Instead, they have chosen to define a new entity type identifier called openid_credential_issuer and embed public keys for verifiable credential verification in the “metadata”.“openid_credential_issuer” object of the entity configuration.

As a related topic, a new client authentication method called “OAuth 2.0 Attestation-Based Client Authentication” is currently under development. For the method, a wallet must obtain a “wallet attestation” from an “attester” in advance because the wallet needs to include this attestation when performing the client authentication method. The recipient (e.g., an authorization server) of the attestation must obtain the public key for verifying the attestation’s signature from the attester. Here, the distribution of public keys for attestations is an issue similar to that described for verifiable credentials above. And, here again, /.well-known/jwt-issuer is proposed as a possible option. This is the very predicted concern, which makes it technically impossible to run an attester and a credential issuer on the same server (but whether running both on the same server is conceptually suitable or not is a different matter). Additionally, whether the attestation format is JWT or not is not essential.


2.7.2. Essential Functions of Verifiable Credential Formats

As mentioned in the previous section, there are many challenges related to verifiable credential formats. However, we believe that the essential functions expected from verifiable credential formats can be summarized as follows:

  1. Verifiability
  2. Key Binding
  3. Selective Disclosure

In the following section, we will explain a verifiable credential format based on SD-JWT that can meet these requirements.


2.7.3. SD-JWT VC

SD-JWT is a general-purpose data format and not a verifiable credential format in itself. However, by adding certain requirements, it is possible to define a verifiable credential format based on SD-JWT. “SD-JWT-based Verifiable Credentials (SD-JWT VC)” is a specification designed for this purpose.

Since an overview of SD-JWT has already been provided, we will only briefly introduce the key points of SD-JWT VC in the following table. Please refer to the SD-JWT VC specification for more details.

Media Type application/vc+sd-jwt
Issuer-signed JWT Place Name Presence Description
Header alg REQUIRED As required by the JWT specification (RFC 7519).
typ REQUIRED vc+sd-jwt
Payload iss REQUIRED The identifier of the credential issuer.
iat REQUIRED The issuance time.
nbf OPTIONAL The time before which the verifiable credential must not be accepted.
exp OPTIONAL The expiry time.
cnf CONDITIONALLY REQUIRED Required when cryptographic key binding is to be supported. The "jwk" property representing the public key should be included. (cf. RFC 7800)
vct REQUIRED The identifier of the type of the verifiable credential.
status OPTIONAL The information on how to read the status of the verifiable credential.
sub OPTIONAL The identifier of the subject of the verifiable credential.
Key Binding JWT Place Name Presence Description
Header alg REQUIRED As required by the JWT specification (RFC 7519).
typ REQUIRED kb+jwt (as required by the SD-JWT specification)
Payload iat REQUIRED The issuance time.
aud REQUIRED The intended recipient of the key binding JWT, which is typically the verifier.
nonce REQUIRED A string ensuring the freshness of the signature.
_sd_hash REQUIRED The base64url-encoded hash digest over the issuer-signed JWT and the selected disclosures.

The actual value of the type claim and additional claims specific to the credential type in the issuer-signed JWT are determined by respective deployments, and they fall outside the scope of the SD-JWT VC specification.


2.7.4. Other Verifiable Credential Formats

This document does not describe other verifiable credential formats such as jwt_vc_json.

2.8. Credential Information for Access Token

Since we have covered verifiable credential formats, we can revisit the topic of credential information for access tokens.

The current draft of the OID4VCI specification has defined how to store credential information at respective locations, but some reported issues remain unresolved. Unfortunately, reaching a satisfactory resolution in the near term is unlikely due to conflicting opinions among participants.

Therefore, please keep in mind that the way credential information is expressed, which we will explain next, is particularly unstable and ambiguous within the OID4VCI specification.


2.8.1. Credential Information in Credential Offer

Before the breaking change of the OID4VCI specification, the "credentials" array in a credential offer might contain JSON objects. Such objects could have complex structures.

However, after the breaking change, the type of the elements in the "credentials" array in a credential offer is restricted to “string” only. JSON objects are no longer accepted. Therefore, explanation about the complex structures is no longer needed to be provided here.

The string elements in the "credentials" array are the identifiers of the supported credentials.


2.8.2. Credential Information in RAR Object

When the type of a RAR object is "openid_credential", the RAR object contains information about an issuable credential.

Such RAR object must contain the "format" property. It represents the format of the issuable credential. For instance, the identifier "jwt_vc_json" is used for verifiable credentials based on W3C VCDM without JSON-LD.

The presence of other properties depends on the value of the "format" property. For example, according to Appendix E of the OID4VCI specification, the jwt_vc_json format and some others require a "credential_definition" property, while the mso_mdoc format requires a "doctype" property.

{
    "type": "openid_credential",
    "format": "jwt_vc_json",
    "credential_definition": {
        "type": [
            "VerifiableCredential",
            "UniversityDegreeCredential"
        ],
        "credentialSubject": {
            "given_name": {},
            "family_name": {},
            "degree": {}
        }
    }
}
{
    "type": "openid_credential",
    "format": "ldp_vc",
    "credential_definition": {
        "@context": [
            "https://www.w3.org/2018/credentials/v1",
            "https://www.w3.org/2018/credentials/examples/v1"
        ],
        "type": [
            "VerifiableCredential",
            "UniversityDegreeCredential"
        ],
        "credentialSubject": {
            "given_name": {},
            "family_name": {},
            "degree": {}
        }
    }
}
{
    "type": "openid_credential",
    "format": "mso_doc",
    "doctype": "org.iso.18013.5.1.mDL",
    "claims": {
        "org.iso.18013.5.1": {
            "given_name": {},
            "family_name": {},
            "birth_date": {}
        },
        "org.iso.18013.5.1.aamva": {
            "organ_donor": {}
        }
    }
}

As the OID4VCI specification does not provide definitions for SD-JWT-based verifiable credentials, supplementary documents are required. One such document is the “OpenID4VC High Assurance Interoperability Profile with SD-JWT VC” (HAIP).

For SD-JWT-based verifiable credentials, HAIP introduces vc+sd-jwt as a valid value for the "format" property. Furthermore, it provides specific guidelines on how credential information for vc+sd-jwt should be structured. HAIP mandates the inclusion of a "credential_definition" JSON object, which must contain a "vct" property. Below is an example.

{
    "type": "openid_credential",
    "format": "vc+sd-jwt",
    "credential_definition": {
        "vct": "https://credentials.example.com/identity_credential"
    }
}

2.8.3. Credential Information in Supported Credential

Information about supported credentials is described as a part of “credential issuer metadata”.

The metadata is a JSON object. It contains a "credentials_supported" JSON object. Each of the entries in the object represents credential information about a verifiable credential supported by the credential issuer.

The properties within a supported credential object are divided into (1) those that may appear common to all supported credential objects and (2) those specific to the respective format. For example, the "format" property always exists in every supported credential object, while the "credential_definition" property is available for some formats only.


2.8.4. Credential Information Processing

Due to the lack of consistency and identifiability among pieces of credential information, the current feasible, lowest common denominator implementation will result in that credential issuers issue verifiable credentials with fixed structures, ignoring finer conditions specified at runtime.

Specifically, it is likely that credential issuer implementations will check the value of the "format" property in credential information and a few others only (e.g., "credential_definition" / "vct" in the case of "vc+sd-jwt"), and issue a verifiable credential with a fixed structure.

And, if verifiable credential formats that support selective disclosure become widespread, such behavior by credential issuers might suffice. This is because the functionality to narrow down the content of a verifiable credential at issuance time is almost unnecessary.

2.9. Credential Issuance Details

The previous sections have covered the details about access token issuance. Next, we will delve into the details about credential issuance.


2.9.1. Key Proof

As explained in the “Credential Verification” section, the wallet is expected to provide a key proof if it wishes to obtain a verifiable credential capable of key binding.

In the OID4VCI specification, two specific formats for key proof are defined. These formats are based on JWT (RFC 7519) and CWT (RFC 8392), respectively. Additional key proof formats may be introduced in the future when the need arises.


2.9.1.1. Key Proof JWT

By definition, a key proof includes a public key or a reference to the key. In the case of the key proof based on JWT, several methods are employed to include this key information as listed below. A key proof JWT must use one and only one of the methods.

  1. The jwk header parameter (RFC 7515, 4.1.3)
  2. The x5c header parameter (RFC 7515, 4.1.6)
  3. The kid header parameter (RFC 7515, 4.1.4)

All of these methods embed the key information in the header of a key proof JWT.

In the case of using the jwk header parameter, the public key is embedded in the format of “JWK” (RFC 7517 JSON Web Key (JWK)).

The value of the jwk header parameter is a JSON object representing the public key.

The key proof JWT itself must be signed with the private key that corresponds to the public key.

In the case of using the x5c header parameter, an X.509 certificate for the public key needs to be prepared. The base64 representation of the DER representation of the certificate must be included in the x5c JSON array as the first element. If the certificate chain of the certificate is available, the chain can be included along with the certificate. See RFC 7515, 4.1.6. “x5c” (X.509 Certificate Chain) Header Parameter for the details of the format that the x5c parameter expects.

In the case of using the kid header parameter, its value should be a DID URL that can be resolved to the public key.

Next, let’s take a look at the payload part of a key proof JWT.

The following table lists the claims that must or may appear in the payload part of a key proof JWT.

Name Presence Description
iss CONDITIONALLY REQUIRED The identifier of the client application (wallet).
aud REQUIRED The identifier of the credential issuer.
iat REQUIRED The issuance time.
nonce CONDITIONALLY REQUIERD The server-provided c_nonce.
  • The iss claim represents the identifier of the client application (wallet) and is required in most cases. The only exception is when the access token is issued using the pre-authorized code flow, and the token request for the access token doesn’t include any information to identify the client application. Such token requests are allowed only if the authorization server permits anonymous access in the pre-authorized code flow. The authorization server’s support for this is indicated by the boolean server metadata, pre-authorized_grant_anonymous_access_supported.

  • The aud claim represents the identifier of the credential issuer and is always required.

  • The iat claim represents the issuance time of the key proof JWT, as defined in RFC 7519, 4.1.6. “iat” (Issued At) Claim. This claim is always required.

  • The nonce claim corresponds to the c_nonce included in the token response and/or the credential response. It is required when the token response contains the c_nonce parameter. Additionally, the credential issuer may mandate the nonce claim, even when the token response doesn’t contain the c_nonce parameter. More information about c_nonce will be provided later.

The following table summarizes the requirements for a key proof JWT.

Key Proof JWT Place Name Presence Description
Header alg REQUIRED As required by the JWT specification (RFC 7519).
typ REQUIRED openid4vci-proof+jwt
jwk CONDITIONALLY REQUIRED Exactly one of these header parameters must be included, representing a public key or the reference to a public key.
x5c
kid
Payload iss CONDITIONALLY REQUIRED The identifier of the client application (wallet).
aud REQUIRED The identifier of the credential issuer.
iat REQUIRED The issuance time.
nonce CONDITIONALLY REQUIRED The server-provided c_nonce.

The following is an example of key proof JWT.

eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJraWQiOiJHVURvZFB1SURJYllocmdmMHZsT3RNd1otczNiaVpFT3hWMFRTRjBKN3R3IiwieCI6InJjdU1FT1BYbVBJRlotc0Jvbkxyb1VvaTVYdGZ4NktWeFlFR09YMi1UbGsiLCJ5IjoiNUw1SUZrUFpNT0doTVpsNHRaSk9ISjdtckZQbnJSeV9RSURUOXRWZF9obyIsImFsZyI6IkVTMjU2In19.eyJpc3MiOiJodHRwczovL3dhbGxldC5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNjk3MjM0NzcwLCJub25jZSI6IjZhMzA3YjU1LWM4ZTEtNDg4YS05NjFlLTI1MzQ4ZmYzZTlkYSJ9.Vvo_X_fZyanUZ-y5X0yYtY7d70bbjMKUKqAoDiCBmP3NT4xNfTEpuYl9eu7vxc2fLf67ZdbSfw4rwEp8qvvWpA

The header and payload of the key proof JWT example are decoded into the following JSONs, respectively.

{
  "alg": "ES256",
  "typ": "openid4vci-proof+jwt",
  "jwk": {
    "kty": "EC",
    "alg": "ES256",
    "crv": "P-256",
    "x":   "rcuMEOPXmPIFZ-sBonLroUoi5Xtfx6KVxYEGOX2-Tlk",
    "y":   "5L5IFkPZMOGhMZl4tZJOHJ7mrFPnrRy_QIDT9tVd_ho",
    "kid": "GUDodPuIDIbYhrgf0vlOtMwZ-s3biZEOxV0TSF0J7tw"
  }
}
{
  "iss": "https://wallet.example.com",
  "aud": "https://issuer.example.com",
  "iat": 1697234770,
  "nonce": "6a307b55-c8e1-488a-961e-25348ff3e9da"
}
2.9.1.2. Key Proof CWT

This document does not explain key proofs based on CWT. Please refer to the OID4VCI specification for details.


2.9.2. c_nonce

As the primary countermeasure against key proof replay, the credential issuer may require the inclusion of the nonce claim in the key proof. The value of this claim is provided as a c_nonce response parameter from the authorization server or the credential issuer.

A token response from the authorization server may include the c_nonce response parameter along with the c_nonce_expires_in response parameter, which indicates the lifetime of the c_nonce in seconds.

The wallet uses the value of the c_nonce response parameter as the value of the nonce claim in a key proof JWT.

The wallet includes the key proof JWT in a credential request.

If the nonce claim is missing, although the credential issuer requires it, or if the specified nonce value has expired, the credential endpoint will return an error response. This error response includes either the expected c_nonce value or a fresh c_nonce value. Additionally, even when a valid nonce value is provided, the credential response may still include c_nonce for future use. In either case, c_nonce is included in a credential response if the credential issuer requires key proofs include the nonce claim.

If necessary, the wallet can regenerate a new key proof using the c_nonce value provided by the credential endpoint and make a credential request again with the fresh key proof.

The diagram below provides an overview of c_nonce.

2.9.3. Credential Request

A credential request is an HTTP POST request with an access token and a JSON-formatted payload. This payload contains credential information and may include an optional key proof.

2.9.3.1. Credential Information in Credential Request

Credential information in a credential request includes a mandatory "format" property and additional format-specific properties. For example, when the value of the "format" property is "jwt_vc_json", an accompanying "credential_definition" property is expected.

The credential information is a description about the verifiable credential that the wallet wants to obtain. As implied by the example in the diagram above, taken from the OID4VCI specification, this description goes beyond merely identifying an issuable credential from among the issuable credentials associated with the access token. For instance, the intention of the example credential request is to request a verifiable credential in the format of jwt_vc_json that includes the given_name, family_name and degree claims only.

However, there are the following issues here:

  1. It’s not easy to determine which of the issuable credentials meet the specified conditions.

  2. There’s a possibility that multiple issuable credentials may satisfy the conditions.

  3. Minor differences in conditions can lead to the selection of a different issuable credential.

  4. It’s not easy to confirm whether the presented access token has the permission to request verifiable credentials that meet the specified conditions.

Simply put, this specification lacks considerations for implementations. Therefore, as mentioned in the “Credential Information Processing” section previously, unless the specification is improved, it is likely that credential issuer implementations will issue verifiable credentials with fixed structures, ignoring finer conditions specified at runtime (in access token requests or credential requests).


2.9.3.2. Key Proof Information in Credential Request

Key proof information in a credential request is represented by a "proof" property. The value of the property is a JSON object.

The "proof" object contains a mandatory "proof_type" property that indicates the format of the key proof.

When the value of the "proof_type" property is "jwt", a JWT is used as a key proof. In this case, the "proof" object contains a "jwt" property. The value of the "jwt" property is a JWT that conforms to the specification of the key proof JWT.

The diagram below is an overview of a credential request.

2.9.4. Credential Response

A credential response is an HTTP response containing JSON.


2.9.4.1. Credential Response with Verifiable Credential

When a verifiable credential is successfully issued, it is placed in the JSON as the value of the "credential" property. The format of the value of the "credential" property depends on the format of the verifiable credential, which is indicated by the "format" property.

For instance, in the case of SD-JWT-based verifiable credentials conforming to SD-JWT VC, the "format" property holds "vc+sd-jwt", and the "credential" property is a JSON string in the format of SD-JWT.

In addition, the credential response may contain the c_nonce and c_nonce_expires_in response parameters, as explained previously.

Let’s dive into some details of an SD-JWT-based verifiable credential.

An SD-JWT consists of an issuer-signed JWT, zero or more disclosures, and an optional key binding JWT. Tildes (~) are used as delimiters between the components. Note that because a key binding JWT is generated by a wallet, verifiable credentials do not have a key binding JWT when they are issued by a credential issuer.

<Issuer-Signed-JWT>~<Disclosure-1>...<Disclosure-N>~

The first component in an SD-JWT is an issuer-signed JWT. As a standard JWT, the header and payload of the issuer-signed JWT can be base64url-decoded.

The following are important points to note.

  1. The value of the typ header parameter is "vc+sd-jwt".
  2. The payload contains "cnf"."jwk" for key binding.
  3. The payload contains the "_sd_alg" property, which indicates the hash algorithm used for disclosures.
  4. The payload does not contain user claims like "given_name". Instead, it contains the "_sd" array, which holds digest values of disclosures for user claims.

The example of SD-JWT-based verifiable credential contains four disclosures.

By base64url-decoding the disclosures, the original JSON arrays will appear.

The digest values of the disclosures are computed using the hash algorithm indicated by the "_sd_alg" property and are listed in the "_sd" array. The order of the digest values in the array must be independent of the order of the disclosures in the SD-JWT. In this example, the digest values are listed in ASCII-code order.

The diagram below is an overview of a credential response with an SD-JWT-based verifiable credential.

2.9.4.2. Credential Response with Transaction ID

When the requested verifiable credential is not ready, the credential endpoint returns a transaction ID instead of a verifiable credential. The transaction ID is included in the credential response as the value of the "transaction_id" response parameter. The following is an example from the OID4VCI specification.

HTTP/1.1 202 Accepted
Content-Type: application/json
Cache-Control: no-store

{
  "transaction_id": "8xLOxBtZp8",
  "c_nonce": "wlbQc6pCJp",
  "c_nonce_expires_in": 86400
}

The transaction ID is intended to be used later when the wallet sends a deferred credential request to the deferred credential endpoint of the credential issuer.


2.9.4.3. Credential Response with Error

If the credential request cannot be processed successfully, the credential endpoint will return an error response, with the type of error reflected in the value of the "error" response parameter. Below is a sample error excerpt from the specification.

HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store

{
   "error": "invalid_request"
}

If a required key proof is missing or incorrect due to reasons like the nonce claim’s absence or expiration, the error code "invalid_proof" is used. Here is an example from the specification in such a case.

HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store

{
  "error": "invalid_proof"
  "error_description":
       "Credential Issuer requires key proof to be bound to a Credential Issuer provided nonce.",
  "c_nonce": "8YE9hCnyV2",
  "c_nonce_expires_in": 86400
}

2.9.5. Deferred Credential Request

The wallet can send a request to the deferred credential endpoint using a transaction ID. This request should be an HTTP POST request containing JSON with a "transaction_id" property holding the transaction ID.

POST /deferred_credential HTTP/1.1
Host: issuer.example.com
Content-Type: application/json
Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW

{
   "transaction_id": "8xLOxBtZp8"
}

2.9.6. Deferred Credential Response

The deferred credential endpoint will respond with an HTTP response containing JSON. If a verifiable credential has been issued successfully, this JSON includes the "credential" and "format" response parameters, representing the verifiable credential and its format, respectively.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
  "format": "vc+sd-jwt",
  "credential": "eyJ......CJd~"
}

In the event of an unsuccessful issuance, an error response will be returned with the "error" parameter. Particularly, when the requested verifiable credential is not yet ready, the error code "issuance_pending" is used.

HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store

{
   "error": "issuance_pending"
}

2.9.7. Batch Credential Request

A wallet can request multiple verifiable credentials at a time by sending a batch credential request to the batch credential endpoint of the credential issuer.

A batch credential request is an HTTP POST request containing JSON, which includes a "credential_requests" JSON array. The array is a list of JSON objects, each of which represents a credential request.

Each credential request contains credential information and may contain an optional key proof.

It is allowed for credential requests in a batch credential request to specify different credential formats and have different key proofs. The opposite is also true. The credential requests may specify the same credential format and have the same key proof.

The diagram below is an overview of a batch credential request.

2.9.8. Batch Credential Response

A batch credential response is an HTTP response containing JSON, which includes a "credential_responses" JSON array. The array is a list of JSON objects, each of which represents a credential response. The elements in the array correspond to the elements in the "credential_requests" array in the preceding batch credential request.

Each credential response contains either a verifiable credential or a transaction ID.

In addition, a batch credential response may contain c_nonce and c_nonce_expires_in as top-level properties for cases where the wallet sends a credential request or a batch credential request with a key proof in the future.

The diagram below is an overview of a batch credential response.

2.10. Public Key Distribution

To verify the signature of a verifiable credential or a verifiable presentation, verifiers need to obtain the public key that corresponds to the private key which the credential issuer used to sign the verifiable credential.

How to distribute public keys for verifying the signatures of verifiable credentials is outside the scope of the OID4VCI specification. However, here we describe a few proposed methods.


2.10.1. Embedding X.509 Certificate

One method for public key distribution is to embed an X.509 certificate for the public key within the verifiable credential.

In the case of JWT-based verifiable credentials, it is likely that the "x5c" header parameter (RFC 7515, 4.1.6) will be used for that purpose.


2.10.2. Embedding within Entity Configuration

Another method utilizes the OpenID Federation specification and embeds the public key within the entity configuration of the credential issuer.

The Italian ecosystem has defined openid_credential_issuer as a new entity type identifier, which represents a credential issuer, and the ecosystem uses the "jwks" metadata to place the credential issuer’s public keys within.

2.10.3. jwt-issuer

Another proposal for public key distribution is /.well-known/jwt-issuer. The new well-known path is intended to serve as the starting point for searching for the public key.

The well-known path returns JSON containing the JWT issuer’s metadata. The "jwks_uri" property in the JSON points to the location of the JWK Set of the issuer. Verifiers can find the target public key in the JWK Set.

2.11. Specification Summary

The OID4VCI specification defines rules for the issuance of verifiable credentials. The two major topics in the specification are “access token issuance” and “credential issuance”.

For access token issuance, the specification defines several methods for specifying issuable credentials, which include, (1) using the pre-authorized code in a credential offer, (2) using the issuer state in a credential offer, (3) using RAR objects with "type":"openid_credential", and (4) using scope values referencing entries in the "credentials_supported" metadata.

For credential issuance, the specification introduces three endpoints, namely, (1) the credential endpoint, (2) the batch credential endpoint, and (3) the deferred credential endpoint.

Pieces of credential information appear at some locations such as (1) the "credentials_supported" issuer metadata, (2) RAR objects, (3) credential requests, and (4) batch credential requests. Due to the lack of consistency and identifiability among them, the specification may not fully achieve its intended goal. However, in exchange for sacrificing full interoperability, real-world ecosystems will be able to issue verifiable credential for their specific needs based on the specification along with their supplementary specifications.

The specification does not go into the specifics of verifiable credential formats, but it does establish rules related to the jwt_vc_json, jwt_vc_json-ld, ldp_vc, and mso_mdoc formats. However, the format that has recently been garnering the most attention is “SD-JWT VC”, which is based on SD-JWT. eIDAS 2.0 mandates support for SD-JWT-based and mdoc-based formats.

Public key distribution is also not covered in the specification. Some recognized proposals for public key distribution include (1) embedding an X.509 certificate in the verifiable credential itself, (2) using “openid_credential_issuer”.“jwks” in the entity configuration of the credential issuer, and (3) using /.well-known/jwt-issuer.

The OID4VCI specification may not achieve its ideal goal in the foreseeable future, but real-world ecosystems will be able to use it for their specific needs with practical compromises and local supplementary specifications.

3. OID4VCI Implementation

3.1. Authlete Overview

Developers can build their own credential issuers and authorization servers / OpenID providers conforming to the OID4VCI specification by utilizing Authlete.

While most vendors directly provide implementations of frontend servers such as an authorization server, Authlete takes a different approach. Authlete provides a set of Web APIs with which developers themselves can implement their own frontend servers. Authlete sits behind such frontend servers and is invisible from end users.

The Authlete architecture inevitably requires developers to build frontend servers, but in return, developers receive the following benefits.

  1. Any technical components of developer’s choice
    • User authentication method
    • User management system
    • API gateway
    • Programming language
    • Web framework
    • Cloud service
  2. Full control over user data
    • No need to upload user data to the OAuth/OIDC vendor’s server.
    • Manageable compliance with various regulations for the protection of user data.
  3. Full control over end-user facing frontend servers
    • Corporate brand management across all aspects of UI/UX.
  4. Enforced proper layer separation in system design
    • API authorization is separated from user management and user authentication.
    • OAuth/OIDC protocol processing is separated from API gateway and frontend servers.

3.2. Authlete Configuration

3.2.1. Authlete Version

The OID4VCI specification is supported from Authlete 3.0, which is scheduled to be released around April 2024. Until then, a trial server is available for customers and business partners. If you are interested in trying OID4VCI, please contact us.

3.2.2. Authlete Server Configuration

The “Verifiable Credentials” feature must be enabled on the Authlete server. If you are using the on-premises version of Authlete, please confirm that the configuration file (authlete-server.properties) includes the following line to enable this feature.

feature.verifiable_credentials.enabled=true

3.2.3. Authlete Service Configuration

Property Type Description
verifiableCredentialsEnabled boolean This flag controls availability of features related to Verifiable Credentials such as support of the OID4VCI specification.
credentialIssuerMetadata credentialIssuer string The identifier of the credential issuer when this service acts as a credential issuer. This property corresponds to the credential_issuer metadata defined in the OID4VCI specification.

The value must be a valid URL with the https scheme and without the query part and the fragment part. In addition, Authlete limits the value to ASCII only and a maximum length of 200 characters.

To act as a credential issuer, this property must be set.
authorizationServers string
array
The identifiers of the authorization servers the credential issuer relies on for authorization when this service acts as a credential issuer. This property corresponds to the authorization_servers metadata defined in the OID4VCI specification.

The values must be HTTP-accessible URLs.
credentialEndpoint string The URL of the credential endpoint when this service acts as a credential issuer. This property corresponds to the credential_endpoint metadata defined in the OID4VCI specification.

The value must be a valid URL with the https scheme and without the fragment part. In addition, Authlete limits the value to ASCII only and a maximum length of 200 characters.

To act as a credential issuer, this property must be set.
batchCredentialEndpoint string The URL of the batch credential endpoint when this service acts as a credential issuer. This property corresponds to the batch_credential_endpoint metadata defined in the OID4VCI specification.

The value must be a valid URL with the https scheme and without the fragment part. In addition, Authlete limits the value to ASCII only and a maximum length of 200 characters.

It is optional whether to implement the batch credential endpoint.
deferredCredentialEndpoint string The URL of the deferred credential endpoint when this service acts as a credential issuer. This property corresponds to the deferred_credential_endpoint metadata defined in the OID4VCI specification.

The value must be a valid URL with the https scheme and without the fragment part. In addition, Authlete limits the value to ASCII only and a maximum length of 200 characters.

If the credential endpoint and/or the batch credential endpoint of your credential issuer may issue transaction IDs, you must implement the deferred credential endpoint.
credentialResponseEncryptionAlgValuesSupported string
array
The JWE alg algorithms supported for credential response encryption. This property corresponds to the credential_response_encryption_alg_values_supported metadata defined in the OID4VCI specification.

The valid values are the names of JWEAlg enum entries such as "ECDH_ES". Only asymmetric algorithms are accepted.
credentialResponseEncryptionEncValuesSupported string
array
The JWE enc algorithms supported for credential response encryption. This property corresponds to the credential_response_encryption_enc_values_supported metadata defined in the OID4VCI specification.

The valid values are the names of JWEEnc enum entries such as "A256GCM". "A256GCM" should be included because it will be used as the default value when the credential_response_encryption_enc parameter is missing.
requireCredentialEncryptionResponse boolean The flag indicating whether to always encrypt credential responses. This property corresponds to the require_credential_response_encryption metadata defined in the OID4VCI specification.

If this property is set to true, every credential request is required to include encryption-related parameters such as credential_response_encryption_alg.
credentialsSupported string Credentials supported by the credential issuer when this service acts as a credential issuer. This property corresponds to the credentials_supported metadata defined in the OID4VCI specification.

The value must be a JSON object. Non-ASCII characters may be contained, but Authlete limits the maximum number of characters to 16383.

To act as a credential issuer, this property must be set.
credentialOfferDuration integer The default duration of credential offers in seconds.

When an API to the /vci/offer/create API does not contain the duration request parameter or the value of the parameter is 0 or negative, the value of this property is used as the default value.

If the value of this property is 0 or negative, the default value per Authlete server is used as the default value.
userPinLength integer The default length of user PINs that may be included in a credential offer.

When an API to the /vci/offer/create API does not contain the userPinLength request parameter or the value of the parameter is 0 or negative, the value of this property is used as the default value.

If the value of this property is 0 or negative, the default value per Authlete server is used as the default value.
preAuthorizedGrantAnonymousAccessSupported boolean This property indicates whether token requests using the pre-authorized code flow by unidentifiable client applications are allowed.

This property corresponds to the pre-authorized_grant_anonymous_access_supported metadata defined in the OID4VCI specification.
cNonceDuration integer The duration of c_nonce in seconds.

When the token endpoint of the authorization server issues an access token usable for verifiable credential issuance, it also issues a c_nonce alongside the access token. In addition, the credential endpoint and the batch credential endpoint of the credential issuer issue a new c_nonce when the presented c_nonce has already expired. This property is used as the lifetime of such c_nonces.

If the value of this property is 0 or negative, the default value per Authlete server is used.
credentialTransactionDuration integer The default duration of transaction IDs in seconds that may be issued as a result of a credential request or a batch credential request.

If the value of this property is 0 or negative, the default value per Authlete server is used.
credentialDuration integer The default duration of verifiable credentials in seconds.

Some Authlete APIs such as the /vci/single/issue API and the /vci/batch/issue API may issue one or more verifiable credentials. The value of this property specifies the default duration of such verifiable credentials.

The value 0 indicates that verifiable credentials will not expire. In the case, verifiable credentials will not have a property that indicates the expiration time. For example, JWT-based verifiable credentials will not contain the exp claim (RFC 7519, Section 4.1.4).

Authlete APIs that may issue verifiable credentials recognize a request parameter that can override the duration. For example, a request to the /vci/single/issue API contains an order object that has a credentialDuration parameter that can override the default duration.
credentialJwks string The JWK Set document containing private keys that are used to sign verifiable credentials.

Some Authlete APIs such as the /vci/single/issue API and the /vci/batch/issue API may issue one or more verifiable credentials. The content of this property is referred to by such APIs.

Authlete APIs that may issue verifiable credentials recognize a request parameter that can specify the key ID of a private key that should be used for signing. For example, a request to the /vci/single/issue API contains an order object that has a signingKeyId parameter that can specify the key ID of a private key to be used for signing. When a key ID is not specified, Authlete will select a private key automatically.

If JWKs in the JWK Set do not contain the kid property (RFC 7517, Section 4.5) when this credentialJwks property is updated, Authlete will automatically insert the kid property into such JWKs. The JWK thumbprint (RFC 7638) computed with the SHA-256 hash algorithm is used as the value of the kid property.
credentialJwksUri string The URL at which the JWK Set document of the credential issuer is exposed.

This URL is used as the value of the jwks_uri property in the JWT issuer metadata. The metadata itself is published at /.well-known/jwt-issuer. See SD-JWT-based Verifiable Credentials (SD-JWT VC) for details about the JWT issuer metadata.

3.3. Authlete APIs

3.3.1. Overall Picture of Authlete APIs for OID4VCI

The following diagram illustrates the relationship between the endpoints of the frontend servers (the credential issuer and the authorization server) and Authlete APIs. The details of the Authlete APIs are explained in the following sections.

3.3.2. Authlete API Call

A significant difference between Authlete 2.x and Authlete 3.0 is how to call Authlete APIs.

In Authlete 2.x and older versions, developers call Authlete APIs using a pair of an API key and an API secret (e.g., a service API key and a service API secret). In Authlete 3.0, on the other hand, developers call Authlete APIs with an access token.

Developers can obtain access tokens for Authlete APIs using the new Web console, which is significantly different from the previous ones. In Authlete 2.x and older versions, there are two separate Web consoles: the service owner console (for managing services corresponding to authorization servers and OpenID providers) and the developer console (for managing client applications). In Authlete 3.0, however, a single Web console is provided, and its appearance and functionality change based to on the privileges of the presented access token.

Authlete 2.x Authlete 3.0
Protection API key & API secret Access token
Web Console The service owner console and the developer console A single console

Another difference is found in the path component of Authlete APIs. In Authlete 3.0, most Authlete APIs include a service ID as part of the path, such as /api/{ServiceID}/auth/authorization, where {ServiceID} represents the identifier of a service (i.e., the service API key in Authlete 2.x).

Authlete Version API Path Example
Authlete 2.x /api/auth/authorization
Authlete 3.0 /api/{ServiceID}/auth/authorization

These changes are not insignificant, but their impact on programs can be minimized by absorbing the differences at the library layer. For instance, developers using the sample authorization server written in Java (authlete/java-oauth-server) and switching from Authlete 2.x to Authlete 3.0 only need to modify the content of the configuration file (authlete.properties) from:

# For Authlete 2.x
base_url = ...
service.api_key = ..
service.api_secret = ...

to:

# For Authlete 3.0
api_version = V3
base_url = ...
service.api_key = ...
service.access_token = ...

3.4. Credential Offer Issuance

As mentioned before, the process of issuing credential offers varies among credential issuers.

For example, after interacting with a user via a web browser, the credential issuer may display a QR code like below:

that represents “openid-credential-offer://?credential_offer={CredentialOffer}” where {CredentialOffer} holds the following credential offer.

{
  "credential_issuer": "https://trial.authlete.net",
  "credentials": [
    "IdentityCredential"
  ],
  "grants": {
    "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
      "pre-authorized_code": "oCwfpo-Bxj0dP2QstEEG1hTXQzKgd7e48Mnj6gfefVs"
    }
  }
}

The credential issuer may instead show a hyperlink like below:

  • openid-credential-offer://?credential_offer_uri={CredentialOfferUri}

where {CredentialOfferUri} holds a URL-encoded URL like https%3A%2F%2Ftrial.authlete.net%2Fapi%2Foffer%2FTctoiNm9lYASTBT6XRGb8RQsrClKczCxDtqLY1jLvpk.


3.4.1. The /vci/offer/create API

Regardless, credential issuers supporting credential offers must be able to create them. For the functionality, Authlete provides the /vci/offer/create API. The following table summarizes the API.

Request to the /vci/offer/create API
HTTP Method and
Content-Type
GET (query parameters)
POST application/json
POST application/x-www-form-urlencoded
Request Parameters credentials A string array, which will be used as the value of the "credentials" property of a credential offer. This request parameter is mandatory.
authorizationCodeGrantIncluded A boolean value (true or false) indicating whether to include the "authorization_code" object in the "grants" object.
issuerStateIncluded A boolean value (true or false) indicating whether to include the "issuer_state" property in the "authorization_code" object in the "grants" object.

When this parameter is true, Authlete generates an issuer state and puts it in the "authorization_code" object as the value of the "issuer_state" property.
preAuthorizedCodeGrantIncluded A boolean value (true or false) indicating whether to include the "urn:ietf:params:oauth:grant-type:pre-authorized_code" object in the "grants" object.

When this parameter is true, Authlete generates a pre-authorized code and puts it in the "urn:ietf:params:oauth:grant-type:pre-authorized_code" object as the value of the "pre-authorized_code" property.
userPinRequired A boolean value (true or false) indicating whether to include the "user_pin_required" property with the value true in the "urn:ietf:params:oauth:grant-type:pre-authorized_code" object.
userPinLength The length of the user PIN associated with the credential offer.

Authlete generates a user PIN of the specified length when necessary. The maximum length that can be specified is 8 as the OID4VCI specification requires so. When this property is omitted or its value is 0 or negative, the value of the userPinLength property of the service is used.
subject The subject (the unique identifier) of the user associated with the credential offer.

This parameter is mandatory.
duration The duration of the credential offer in seconds.

If this parameter holds a positive integer, the value is used as the duration of the credential offer being issued. Otherwise, the value of the credentialOfferDuration property of the service is used.
context The general-purpose arbitrary string associated with the credential offer.

Developers can utilize this parameter as they like. Authlete does not care about the content of this parameter.
properties The extra properties associated with the credential offer, which are general-purpose key-value pairs.

The extra properties will be eventually associated with an access token which will be created based on the credential offer.
jwtAtClaims The additional claims in JSON object format that are added to the payload part of the JWT access token.

This parameter has a meaning only when the format of access tokens issued by the service is JWT. In other words, it has a meaning only when the accessTokenSignAlg property of the service holds a non-null value.

The additional claims will be eventually associated with an access token which will be created based on the credential offer.
authTime The time when the user authentication was performed during the course of issuing the credential offer.

The time is represented as seconds since the Unix epoch.
acr The Authentication Context Class Reference of the user authentication performed during the course of issuing the credential offer.

For example, the following command lines create a credential offer.

$ BASE_URL=https://nextdev-api.authlete.net
$ SERVICE_ID=986126671
$ ACCESS_TOKEN=${YOUR_ACCESS_TOKEN}
$ curl -s ${BASE_URL}/api/${SERVICE_ID}/vci/offer/create \
    -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    -H "Content-Type: application/json" \
    --data '
{
  "credentials": [ "IdentityCredential" ],
  "preAuthorizedCodeGrantIncluded": true,
  "userPinRequired": true,
  "subject": "1001"
}
'

The /vci/offer/create API returns JSON like below.

{
  "type": "credentialOfferCreateResponse",
  "resultCode": "A366001",
  "resultMessage": "[A366001] A credential offer was created successfully.",
  "action": "CREATED",
  "info": {
    "authTime": 0,
    "authorizationCodeGrantIncluded": false,
    "credentialIssuer": "https://trial.authlete.net",
    "credentialOffer": "{\"credential_issuer\":\"https://trial.authlete.net\",\"credentials\":[\"IdentityCredential\"],\"grants\":{\"urn:ietf:params:oauth:grant-type:pre-authorized_code\":{\"pre-authorized_code\":\"AN96gmLgCvws7y1GDKVto64ED4CpU_D_8R-lCi8r9sA\",\"user_pin_required\":true}}}",
    "credentials": [
      "IdentityCredential"
    ],
    "expiresAt": 1700074172201,
    "identifier": "wJROwXd1q04FolIvQcH00CCZ91KAzXmynH-kmclTpMA",
    "issuerStateIncluded": false,
    "preAuthorizedCode": "AN96gmLgCvws7y1GDKVto64ED4CpU_D_8R-lCi8r9sA",
    "preAuthorizedCodeGrantIncluded": true,
    "subject": "1001",
    "userPin": "286724",
    "userPinRequired": true
  }
}

The "info" object in the API response contains information about the created credential offer. The "credentialOffer" property in the "info" object is a string representing the created credential offer. The value of the "credentialOffer" property in the above example looks like the following when formatted in a human-readable manner.

{
  "credential_issuer": "https://trial.authlete.net",
  "credentials": [
    "IdentityCredential"
  ],
  "grants": {
    "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
      "pre-authorized_code": "AN96gmLgCvws7y1GDKVto64ED4CpU_D_8R-lCi8r9sA",
      "user_pin_required": true
    }
  }
}

With the value of the "credentialOffer" property, you can construct a URL by concatenating the following components:

  1. A credential offer endpoint. For example, openid-credential-offer://.
  2. ?credential_offer=.
  3. URL-encoded "credentialOffer" value.
openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftrial.authlete.net%22%2C%22credentials%22%3A%5B%22IdentityCredential%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22AN96gmLgCvws7y1GDKVto64ED4CpU_D_8R-lCi8r9sA%22%2C%22user_pin_required%22%3Atrue%7D%7D%7D

The request to and the response from the /vci/offer/create API are represented by the CredentialOfferCreateRequest and CredentialOfferCreateResponse Java classes in the authlete-java-common library, respectively. Please refer to the library’s JavaDoc for details.


3.4.2. The /vci/offer/info API

The /vci/offer/info API returns information about a credential offer. This API accepts the identifier request parameter that specifies the identifier of a credential offer.

Request to the /vci/offer/info API
HTTP Method and
Content-Type
GET (path parameters)
POST application/json
POST application/x-www-form-urlencoded
Request Parameters identifier The identifier of a credential offer. When the API call is an HTTP GET request, the identifier is specified as the last path component like /vci/offer/info/{identifier}.

The identifier is included in the response from the /vci/offer/create API. The value of the "identifier" property in the "info" object is the identifier. In the example in the previous section, its value is wJROwXd1q04FolIvQcH00CCZ91KAzXmynH-kmclTpMA.

The following command lines query information about the credential offer created in the previous section.

$ CREDENTIAL_OFFER_IDENTIFIER=wJROwXd1q04FolIvQcH00CCZ91KAzXmynH-kmclTpMA
$ curl -s ${BASE_URL}/api/${SERVICE_ID}/vci/offer/info/${CREDENTIAL_OFFER_IDENTIFIER} \
    -H "Authorization: Bearer ${ACCESS_TOKEN}"

The /vci/offer/info API returns JSON like below, which is almost the same as the response from the /vci/offer/create API.

{
  "type": "credentialOfferInfoResponse",
  "resultCode": "A368001",
  "resultMessage": "[A368001] Information about the credential offer was obtained successfully.",
  "action": "OK",
  "info": {
    "authTime": 0,
    "authorizationCodeGrantIncluded": false,
    "credentialIssuer": "https://trial.authlete.net",
    "credentialOffer": "{\"credential_issuer\":\"https://trial.authlete.net\",\"credentials\":[\"IdentityCredential\"],\"grants\":{\"urn:ietf:params:oauth:grant-type:pre-authorized_code\":{\"pre-authorized_code\":\"AN96gmLgCvws7y1GDKVto64ED4CpU_D_8R-lCi8r9sA\",\"user_pin_required\":true}}}",
    "credentials": [
      "IdentityCredential"
    ],
    "expiresAt": 1700074172000,
    "identifier": "wJROwXd1q04FolIvQcH00CCZ91KAzXmynH-kmclTpMA",
    "issuerStateIncluded": false,
    "preAuthorizedCode": "AN96gmLgCvws7y1GDKVto64ED4CpU_D_8R-lCi8r9sA",
    "preAuthorizedCodeGrantIncluded": true,
    "subject": "1001",
    "userPin": "286724",
    "userPinRequired": true
  }
}

The main purpose of the /vci/offer/info API is to assist developers in implementing an endpoint on their credential issuer that provides information about a credential offer when queried by a wallet.

If such endpoint is available, you can construct a URL by concatenating the following components:

  1. A credential offer endpoint. For example, openid-credential-offer://.
  2. ?credential_offer_uri=.
  3. URL-encoded URL of the endpoint including the identifier of the credential offer. For example, https://trial.authlete.net/api/offer/wJROwXd1q04FolIvQcH00CCZ91KAzXmynH-kmclTpMA.
openid-credential-offer://?credential_offer_uri=https%3A%2F%2Ftrial.authlete.net%2Fapi%2Foffer%2FwJROwXd1q04FolIvQcH00CCZ91KAzXmynH-kmclTpMA

3.4.3. Credential Offer Issuance Example

The sample authorization server implementation written in Java, authlete/java-oauth-server, can function as a credential issuer. Its /api/offer/issue endpoint provides an HTML page for developers to create custom credential offers. A java-oauth-server instance using Authlete 3.0 is currently running at https://trial.authlete.net, and the endpoint is active for trial purposes at https://trial.authlete.net/api/offer/issue.

The HTML page requires user authentication. The test accounts embedded in java-oauth-server can be used.

Subject Login ID Password
1001 john john
1002 jane jane
1003 max max
1004 inga inga

3.5. Credential Endpoint Implementation

The credential endpoint can be implemented using the following Authlete APIs.

Authlete API Description
1 /auth/introspection validates the presented access token, and returns the information about the access token.
2 /vci/single/parse parses and validates the received credential request, and returns the information about the credential request.
3 /vci/single/issue issues a verifiable credential or a transaction ID, and prepares the credential response.

Let’s go through the processing steps within a credential endpoint implementation.

As the first step, the implementation of the credential endpoint receives a credential request from a wallet.

The implementation extracts the access token from the credential request and passes it to Authlete’s /auth/introspection API.

The /auth/introspection API validates the access token, and returns information about the access token.

If the access token is valid, the endpoint implementation sends the access token and the message body of the credential request to the /vci/single/parse API.

The /vci/single/parse API parses and validates the credential request, and returns the information about the credential request.

The endpoint implementation prepares a “credential issuance order”, which contains necessary information for Authlete to issue a verifiable credential. The details about this preparation will be discussed later.

The endpoint implementation sends the credential issuance order and the access token to the /vci/single/issue API.

The /vci/single/issue API issues a verifiable credential or a transaction ID according to the credential issuance order, and prepares the content of the credential response.

The endpoint implementation builds an HTTP response that represents the credential response from the endpoint to the wallet. The response content prepared by the /vci/single/issue API can be used as the message body of the credential response.

Finally, the credential endpoint returns the credential response to the wallet.

The following diagram illustrates the processing steps within a credential endpoint implementation.

3.5.1. Credential Issuance Order

The steps to prepare a credential issuance order are as follows.


3.5.1.1. Credential Issuance Order Step 1

Get the subject (= unique identifier) of the user associated with the access token from the access token information. The "subject" property in the response from the /auth/introspection API (cf. IntrospectionResponse) holds the value of the subject.


3.5.1.2. Credential Issuance Order Step 2

Retrieve information about the user identified by the subject from the user database.


3.5.1.3. Credential Issuance Order Step 3

Get the information about the issuable credentials associated with the access token from the access token information. The "issuableCredentials" property in the response from the /auth/introspection API holds the information as a string. This string needs to be parsed as a JSON array.


3.5.1.4. Credential Issuance Order Step 4

Get the credential information included in the credential request from the credential request information. The "info" object in the response from the vci/single/parse API (cf. CredentialSingleParseResponse) holds various information about the credential request. The combination of the "format" property and the "details" property in the "info" object represent the credential information.

The value of the "details" property is a string. The string needs to be parsed as a JSON object. The content of the JSON object is almost the same as the credential request except that it does not contain the "format" parameter and the "proof" parameter.


3.5.1.5. Credential Issuance Order Step 5

Confirm that the access token has the necessary permissions for the credential request by checking if the credential information is a subset of any issuable credentials.

However, if you are a programmer, you can understand that the current OID4VCI specification makes it challenging to implement this step. Furthermore, in the case of SD-JWT VC, there is a proposal to make credential_definition.type determine the set of claims and eliminate the need to specify individual claims one by one (Note: type was renamed to vct later). The proposal makes it impossible to check the access token’s permissions only by mechanically seeing the inclusion relationship between JSON objects. These are also reasons why it is said that the OID4VCI specification does not consider implementation details much.

Therefore, the confirmation of whether the access token has sufficient permissions is left to be implemented by each credential issuer according to their respective policies. While permission checks based on inclusion relationships are implemented in Authlete, they have been disabled.


3.5.1.6. Credential Issuance Order Step 6

Determine the set of user claims to embed in the VC being issued based on the credential information, and get the values of the user claims from the dataset retrieved from the user database.


3.5.1.7. Credential Issuance Order Step 7

Build a credential issuance order using the collected data.

A credential issuance order is a JSON object that has the properties listed in the following table.

Property Type Description
requestIdentifier string The identifier of the credential request which has been assigned by Authlete. The info.identifier property in the response from the /vci/single/parse API is the identifier. This property is mandatory.
credentialPayload string The additional payload added to the VC being issued. The format of this string must be a JSON object. The set of the user claims should be converted into JSON and set to this property. This parameter is optional.
issuanceDeferred boolean The flag indicating whether to defer the credential issuance. When this property is true, the /vci/single/issue API issues a transaction ID instead of a VC.
credentialDuration integer The duration of the VC in seconds. If the value of this property is a positive number, the value is used as the duration. If the value is 0, the default duration of the service is used. If the value is a negative number, the VC will not have an expiration time.
signingKeyId string The key ID of the private key that should be used for signing the VC being issued. If omitted, Authlete will select a key automatically.

3.5.1.8. Credential Issuance Order Step 8

Prepare a request to the /vci/single/issue API (cf. CredentialSingleIssueRequest).

Request to the /vci/single/issue API
HTTP Method and Content-Type POST application/json
Request Parameters accessToken The access token presented at the credential endpoint.
order A credential issuance order that provides an instruction for issuing a verifiable credential or a transaction ID.

3.5.1.9. Credential Issuance Order Step 9

Send the prepared request to the /vci/single/issue API.


3.5.1.10. Credential Issuance Order Steps Summary

The following diagram is a summary of the steps for preparing a credential issuance order.


3.6. Batch Credential Endpoint Implementation

To be written.

4. OID4VCI Demo

4.1. Pre-Authorized Code Flow + Key Proof + SD-JWT VC


4.1.1. Setup

Download the resources used in this demo.

git clone https://github.com/authlete/oid4vci-demo
cd oid4vci-demo

Set up some shell variables for this demo.

CLIENT_ID=218232426
TOKEN_ENDPOINT=https://trial.authlete.net/api/token
CREDENTIAL_ISSUER=https://trial.authlete.net
CREDENTIAL_ENDPOINT=https://trial.authlete.net/api/credential

4.1.2. Pre-Authorized Code

Access https://trial.authlete.net/api/offer/issue to generate a “credential offer” that contains a “pre-authorized code”.

The page displayed at the URL provides a form to create an arbitrary credential offer for demo purposes. If “Pre-authorized code grant included” in the form is checked, a pre-authorized code will be included in the credential offer being issued.

Input inga and inga in the “Login ID” field and the “Password” field, confirm that “Pre-authorized code grant included” is checked, and press the “Submit” button. You will see a result page displayed.

The result page will show a QR code which represents a URL including a credential offer. The content of the credential offer is shown in the JSON placed under the QR code. The value of the pre-authorized_code property in the JSON is the issued pre-authorized code.

Set the issued pre-authorized code to shell variable PRE_AUTHORIZED_CODE to use it in the next step.

PRE_AUTHORIZED_CODE=HL4jsCAjW63bYZzmM51A-gZeRG9Os3gK7ogwg6UBIFQ

4.1.2. Access Token

Send a token request using the pre-authorized code flow. The client for this demo is a public client, so client authentication is not required. That is, it’s not necessary to add request parameters related to client authentication.

curl -s $TOKEN_ENDPOINT \
     -d client_id=$CLIENT_ID \
     -d grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code \
     -d pre-authorized_code=$PRE_AUTHORIZED_CODE

The token endpoint will return a response like below.

{
  "access_token": "_JJ7PPQ9hxb1la6OVxdPbDlbPbjd9jcHCAPS8k_S-Eg",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": null,
  "refresh_token": "kR0_yn3R2zfX-5Sex_6CVIXMcJj0RLlVQsNMcNSs2uY",
  "c_nonce": "a52hIZJFBU2J-Mo3c4ZLzvQNjxznwcXhgU90JcHjRjk",
  "c_nonce_expires_in": 86400
}

The response will contain the access_token parameter and the c_nonce parameter. Set the values of the response parameters to shell variables for later use.

ACCESS_TOKEN=_JJ7PPQ9hxb1la6OVxdPbDlbPbjd9jcHCAPS8k_S-Eg
C_NONCE=a52hIZJFBU2J-Mo3c4ZLzvQNjxznwcXhgU90JcHjRjk

4.1.3. Key Proof

Generate a “key proof JWT” using the holder key holder.jwk and the generate-key-proof script. The JWK file and the script are contained in the oid4vci-demo repository.

./generate-key-proof \
    -i $CREDENTIAL_ISSUER \
    -k holder.jwk \
    -c $CLIENT_ID \
    -n $C_NONCE

The generate-key-proof script will generate a key proof JWT like below.

eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiUFN4UXJEMnpsMF9tWGNBcXoxbWdxU2VCb0Jobm14Mnl4QkVwckJZOEYyMCIsInkiOiJ4VjhmYmkxRlNvc1V1bkxldUxOdUxrSmlxbVk2VEtpTW51ci1HbjJ3UjEwIn19.eyJpc3MiOiIyMTgyMzI0MjYiLCJhdWQiOiJodHRwczovL3RyaWFsLmF1dGhsZXRlLm5ldCIsImlhdCI6MTcwMDc2ODc0Niwibm9uY2UiOiJhNTJoSVpKRkJVMkotTW8zYzRaTHp2UU5qeHpud2NYaGdVOTBKY0hqUmprIn0.gcW_OM1JbOeKymahAWuU-zJxle31VeSV23EcR2oyqxeO7GuripWnbU2JkcbVPpxmfstAJrsNogKL_JO55qxiSg

Decoding the header and the payload of the key proof JWT by base64url will show the following JSONs.

{
  "typ": "openid4vci-proof+jwt",
  "alg": "ES256",
  "jwk": {
    "crv": "P-256",
    "kty": "EC",
    "x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
    "y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
  }
}
{
  "iss": "218232426",
  "aud": "https://trial.authlete.net",
  "iat": 1700768746,
  "nonce": "a52hIZJFBU2J-Mo3c4ZLzvQNjxznwcXhgU90JcHjRjk"
}

The result of executing the generate-key-proof script can be directly set to the shell variable KEY_PROOF_JWT by doing the following.

KEY_PROOF_JWT=`./generate-key-proof -i $CREDENTIAL_ISSUER -k holder.jwk -c $CLIENT_ID -n $C_NONCE`

4.1.4. SD-JWT VC

Send a “credential request” with the generated key proof JWT to the “credential endpoint”.

curl -s $CREDENTIAL_ENDPOINT \
       -H "Authorization: Bearer $ACCESS_TOKEN" \
       -H "Content-Type: application/json" \
       --data '{
  "format": "vc+sd-jwt",
  "credential_definition": {
    "vct": "https://credentials.example.com/identity_credential"
  },
  "proof": {
    "proof_type": "jwt",
    "jwt":"'${KEY_PROOF_JWT}'"
  }
}'

The credential endpoint will return a response like below.

{
  "format": "vc+sd-jwt",
  "credential": "eyJraWQiOiJaYkFBS3doeW5ycUJuWWxIZEVrQkl2TkpGWlpIX2JSZzFLSW9wS2ZaNk84IiwidHlwIjoidmMrc2Qtand0IiwiYWxnIjoiRVMyNTYifQ.eyJfc2QiOlsiMUFFdmEzNUZhNjdFMVRZY3VDZkN5enRJdGZId2E2UU5PY3VDZ01Famd2WSIsIjl4SWVuZHlyRVpRUVdpcWg0Xzd1d0RLWVBSTDQwTm5OV0o2YjAxNmlyNmMiLCJGQktJNXR3elEtSmFRcnJrTVBLYUJfSmhnbXc4anVYUVBNMXdzNUFGcEFNIiwiTVpsTno4NDl2REw2YWVlSnlqVWJSVDROTVFXQUdkbDFnS1o1SXhyTnA5USIsIk1kYUFvUnlmal9qVVRZTVdqdDJ3SnhOUjY1Y2VnMUxPaTV6VDQ5dFVRYlkiLCJYMVRRTjVjZjJ1UlZXT1AxZzBXOS1PY085a3VLZlRQT0ZaeTdOVmpnbDJBIiwiWEwxdnhLcjdXVE5yNnRSb3hKQk55SWJPaXNhTlZKREV6OW5LRm1ONWRuOCIsImdhN2h3WkN6bDg3a2otSXdUZUVoMHJ1NkNBcmc0S0FpcWFrdmhLUU1tcXMiLCJuSkNrc0VfNUFtcmkzX3o3TE0zeTJoSXQtT05Gc1dNYTc2eHlVUHdWQmpJIl0sInZjdCI6Imh0dHBzOi8vY3JlZGVudGlhbHMuZXhhbXBsZS5jb20vaWRlbnRpdHlfY3JlZGVudGlhbCIsIl9zZF9hbGciOiJzaGEtMjU2IiwiaXNzIjoiaHR0cHM6Ly90cmlhbC5hdXRobGV0ZS5uZXQiLCJjbmYiOnsiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJraWQiOiI0TTlrSXJCOVdZenQxR1FnTDEybHpkQlpzR3llVjNsZ1BLb3YyOG9UNUw0IiwieCI6IlBTeFFyRDJ6bDBfbVhjQXF6MW1ncVNlQm9CaG5teDJ5eEJFcHJCWThGMjAiLCJ5IjoieFY4ZmJpMUZTb3NVdW5MZXVMTnVMa0ppcW1ZNlRLaU1udXItR24yd1IxMCJ9fSwiaWF0IjoxNzAwNzcwODY2fQ.NlKG9IAI8M3jgGaIRNmhXylXJdHUvjcL4YLhKelUJRemW415xvg2sf3SbiZiUS-HvGVtCQxHboLBSDh3_1HY-w~WyIyb0g3VWprRl8tdjRaVHZtc1dHT0dBIiwic3ViIiwiMTAwNCJd~WyJpTEtqcnBTOVVjbjAzdWg5b29naVNnIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ~WyIxTzVSajFTSlo3NE52RW03R3NvdVFnIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd~WyJNamM2UmZBSzd2N3VJUVIteWpYUXV3IiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd~",
  "c_nonce": "a52hIZJFBU2J-Mo3c4ZLzvQNjxznwcXhgU90JcHjRjk",
  "c_nonce_expires_in": 79227
}

The value of the credential parameter in the response is the issued SD-JWT VC. If the SD-JWT VC is set to the shell variable SD_JWT, the content of the SD-JWT VC can be decoded by invoking the decode-sd-jwt script as follows.

./decode-sd-jwt $SD_JWT

The result will look like below.

{
  "kid": "ZbAAKwhynrqBnYlHdEkBIvNJFZZH_bRg1KIopKfZ6O8",
  "typ": "vc+sd-jwt",
  "alg": "ES256"
}
{
  "_sd": [
    "1AEva35Fa67E1TYcuCfCyztItfHwa6QNOcuCgMEjgvY",
    "9xIendyrEZQQWiqh4_7uwDKYPRL40NnNWJ6b016ir6c",
    "FBKI5twzQ-JaQrrkMPKaB_Jhgmw8juXQPM1ws5AFpAM",
    "MZlNz849vDL6aeeJyjUbRT4NMQWAGdl1gKZ5IxrNp9Q",
    "MdaAoRyfj_jUTYMWjt2wJxNR65ceg1LOi5zT49tUQbY",
    "X1TQN5cf2uRVWOP1g0W9-OcO9kuKfTPOFZy7NVjgl2A",
    "XL1vxKr7WTNr6tRoxJBNyIbOisaNVJDEz9nKFmN5dn8",
    "ga7hwZCzl87kj-IwTeEh0ru6CArg4KAiqakvhKQMmqs",
    "nJCksE_5Amri3_z7LM3y2hIt-ONFsWMa76xyUPwVBjI"
  ],
  "vct": "https://credentials.example.com/identity_credential",
  "_sd_alg": "sha-256",
  "iss": "https://trial.authlete.net",
  "cnf": {
    "jwk": {
      "kty": "EC",
      "crv": "P-256",
      "kid": "4M9kIrB9WYzt1GQgL12lzdBZsGyeV3lgPKov28oT5L4",
      "x": "PSxQrD2zl0_mXcAqz1mgqSeBoBhnmx2yxBEprBY8F20",
      "y": "xV8fbi1FSosUunLeuLNuLkJiqmY6TKiMnur-Gn2wR10"
    }
  },
  "iat": 1700770866
}
{
  "digest": "MZlNz849vDL6aeeJyjUbRT4NMQWAGdl1gKZ5IxrNp9Q",
  "WyIyb0g3VWprRl8tdjRaVHZtc1dHT0dBIiwic3ViIiwiMTAwNCJd": [
    "2oH7UjkF_-v4ZTvmsWGOGA",
    "sub",
    "1004"
  ]
}
{
  "digest": "FBKI5twzQ-JaQrrkMPKaB_Jhgmw8juXQPM1ws5AFpAM",
  "WyJpTEtqcnBTOVVjbjAzdWg5b29naVNnIiwiZ2l2ZW5fbmFtZSIsIkluZ2EiXQ": [
    "iLKjrpS9Ucn03uh9oogiSg",
    "given_name",
    "Inga"
  ]
}
{
  "digest": "X1TQN5cf2uRVWOP1g0W9-OcO9kuKfTPOFZy7NVjgl2A",
  "WyIxTzVSajFTSlo3NE52RW03R3NvdVFnIiwiZmFtaWx5X25hbWUiLCJTaWx2ZXJzdG9uZSJd": [
    "1O5Rj1SJZ74NvEm7GsouQg",
    "family_name",
    "Silverstone"
  ]
}
{
  "digest": "nJCksE_5Amri3_z7LM3y2hIt-ONFsWMa76xyUPwVBjI",
  "WyJNamM2UmZBSzd2N3VJUVIteWpYUXV3IiwiYmlydGhkYXRlIiwiMTk5MS0xMS0wNiJd": [
    "Mjc6RfAK7v7uIQR-yjXQuw",
    "birthdate",
    "1991-11-06"
  ]
}