Skip to main content

Introduction

This article explains the procedure for issuing Verifiable Credentials (VCs) that comply with the OpenID4VC High Assurance Interoperability Profile 1.0 (hereinafter “HAIP”).

Specification Key Points

Regarding the VC issuance procedure, HAIP can be broadly seen as a combination of OpenID for Verifiable Credential Issuance 1.0 (hereinafter “OID4VCI”) and the FAPI 2.0 Security Profile (hereinafter “FAPI2SP”). However, there are some important differences to be aware of, which are outlined below.

Sender-Constrained Access Token

In FAPI2SP, the following mechanisms can be used to achieve sender-constrained access tokens. In contrast, HAIP permits only DPoP.

Client Authentication

In FAPI2SP, the following client authentication methods are available. In HAIP, in addition to these, OAuth 2.0 Attestation-Based Client Authentication (hereinafter “ABCA”) is also supported.
  • attest_jwt_client_auth (ABCA)
Note that when using ABCA in the context of HAIP, the client attestation must include the x5c header parameter. In addition, the X.509 certificate containing the public key for signature verification (i.e., the leaf certificate in the certificate chain) must not be self-signed.
A literal reading of the HAIP specification suggests that client authentication methods other than ABCA are also permitted. However, since discussions around the HAIP specification appear to assume the use of ABCA, and the specification may be updated to reflect this view (i.e., to allow only ABCA), this article adopts ABCA as the client authentication method.

Key Attestation

OID4VCI defines several formats for Key Proofs included in a credential request. Among them, the jwt Proof Type (OID4VCI Appendix F.1) allows a Key Attestation (OID4VCI Appendix D) to be specified in the key_attestation header parameter. In addition, the attestation Proof Type (OID4VCI Appendix F.3) allows the Key Attestation itself to be used directly as the Key Proof. When using a Key Attestation in the context of HAIP, it must include the x5c header parameter. Furthermore, the X.509 certificate containing the public key for signature verification (i.e., the leaf certificate in the certificate chain) must not be self-signed.

Scope

In general, an access token is associated with one or more scopes. In the context of HAIP, those scopes must include at least one that refers to a specific credential configuration. More specifically, the access token must be associated with the value of the scope property of at least one credential configuration listed in credential_configurations_supported in the Credential Issuer Metadata.

Procedure Overview

An overview of the VC issuance procedure using the authorization code flow is as follows:
  1. PAR Request
  2. Authorization Request
  3. Token Request
  4. Credential Request
However, since each request requires various tokens, the actual procedure becomes more complex. The following outlines the procedure, including the generation of those tokens.
  1. PAR Request
    • PKCE Token (Code Verifier and Code Challenge) Generation (PKCE)
    • Client Attestation Generation (ABCA)
    • Attestation Challenge Retrieval (ABCA)
    • Client Attestation PoP Generation (ABCA)
    • DPoP Proof JWT Generation (DPoP)
    • PAR Request Submission (PAR)
  2. Authorization Request
    • Authorization Request Submission
  3. Token Request
    • Client Attestation Generation (ABCA) (reusable)
    • Attestation Challenge Retrieval (ABCA) (reusable)
    • Client Attestation PoP Generation (ABCA) (reusable)
    • DPoP Proof JWT Generation (DPoP)
    • Token Request Submission
  4. Credential Request
    • Nonce Retrieval (OID4VCI)
    • Key Attestation Generation (OID4VCI)
    • Key Proof Generation (OID4VCI)
    • DPoP Proof JWT Generation (DPoP)
    • Credential Request Submission (OID4VCI)

Actual Procedure

In this section, we will walk through the actual procedure. For the scripts used to generate the various tokens, as well as the private keys, public keys, and certificates, we use those published in authlete/oid4vci-demo.

PAR Request

HAIP is based on FAPI2SP, and since FAPI2SP mandates the use of PAR, PAR is also required for HAIP-compliant authorization requests. Here, we register the authorization request at the PAR endpoint and obtain a request URI.

PKCE Token Generation

HAIP is based on FAPI2SP, and since FAPI2SP mandates the use of PKCE, HAIP-compliant authorization requests must include a code challenge, and token requests must include a code verifier. Therefore, we use the pkce script to generate them.
Output:
By using the shell built-in command eval as shown below, you can directly assign the output of the pkce script to shell variables.

Client Attestation Generation

A client attestation can be generated using the generate-client-attestation script. Note that in HAIP, the x5c header parameter is required, so you must use the --x5c option to specify the X.509 certificates to be included in the x5c header parameter. The --x5c option can be specified multiple times, and the X.509 certificates are added to the x5c header parameter in the order they are provided.
Below is an example of the header and payload of a generated Client Attestation.
Please note the following points:
  • The value of the typ header parameter is oauth-client-attestation+jwt.
  • The x5c header parameter includes the X.509 certificate for signature verification (as specified with the --x5c option).
  • The sub claim is set to the client identifier (as specified with the --client-id option).
  • The cnf.jwk claim contains the client’s key (as specified with the --client-key option).

Attestation Challenge Retrieval

According to the ABCA specification, if the authorization server provides a challenge endpoint, the attestation challenge issued from that endpoint must be embedded in the Client Attestation PoP. Whether the authorization server provides a challenge endpoint can be determined by checking if the challenge_endpoint parameter is included in the server metadata. The challenge endpoint accepts an HTTP POST request and returns JSON containing an attestation_challenge property. Below is an example of a request and response excerpted from the ABCA specification. Attestation Challenge Request Example:
Attestation Challenge Response Example:
If the URL of the challenge endpoint is stored in a shell variable named CHALLENGE_ENDPOINT, you can assign the value of the attestation challenge to a shell variable named CHALLENGE by executing the following command.

Client Attestation PoP Generation

A client attestation PoP can be generated using the generate-client-attestation-pop script. If you need to include a challenge claim, specify the --challenge option.
Below is an example of the header and payload of a generated Client Attestation PoP.
Please note the following points:
  • The value of the typ header parameter is oauth-client-attestation-pop+jwt.
  • The aud claim is set to the authorization server identifier (as specified with the --as-id option).
  • The challenge claim is set to the attestation challenge (as specified with the --challenge option).

DPoP Proof JWT Generation

Since FAPI2SP states the following, the authorization code must also be DPoP-bound.
if using DPoP, shall support “Authorization Code Binding to DPoP Key” (as required by Section 10.1 of RFC9449);
This can be achieved either by adding the dpop_jkt request parameter to the request registered at the PAR endpoint, or by including a DPoP Proof JWT. As noted in the specification, using a DPoP Proof JWT simplifies the implementation (since the client can consistently include a DPoP Proof JWT in all requests sent to the authorization server, regardless of the request type). Therefore, in this article, we will generate a DPoP Proof JWT. Use the generate-dpop-proof script to generate a DPoP Proof JWT by passing the -m POST option (to specify the HTTP method for the PAR request), the -u $PAR_ENDPOINT option (to specify the PAR endpoint URL), and the -k client.jwk option (to specify the client’s key).
Below is an example of the header and payload of a generated DPoP Proof JWT.
Please note the following points:
  • The value of the typ header parameter is dpop+jwt.
  • The jwk header parameter contains the client’s key (as specified with the -k option).
  • The htm claim is set to the HTTP method of the PAR request (as specified with the -m option).
  • The htu claim is set to the URL of the PAR endpoint (as specified with the -u option).
Some authorization servers require the DPoP Proof JWT to include a nonce claim. In such cases, generate the DPoP Proof JWT with the -n option. For details on the DPoP nonce, see “DPoP Nonce.”

PAR Request Submission

Now that the required tokens are ready, send the PAR request.
The key points of this request are as follows: If the PAR request is successful, the PAR endpoint returns JSON containing a request_uri property. Below is an example of a PAR response excerpted from the PAR specification.
The value of the request_uri property is the issued request URI. This request URI will later be used as the value of the request_uri request parameter in the authorization request.
Per FAPI2SP requirements, the lifetime of the request URI must be less than 600 seconds.

Authorization Request

Send an authorization request to the authorization endpoint of the authorization server via the browser. At that time, use the request URI issued by the PAR endpoint as the value of the request_uri request parameter.

When the user completes authentication and grants consent on the authorization page returned by the authorization endpoint, an authorization code is issued. This authorization code will later be used as the value of the code request parameter in the token request.
Per FAPI2SP requirements, the authorization code has a maximum lifetime of 60 seconds. Since it expires quickly, take care when sending the token request manually.

Token Request

Tokens Generation

The Client Attestation and Client Attestation PoP are also required for the token request; however, if they have not yet expired, those created for the PAR request can be reused. On the other hand, the DPoP Proof JWT cannot be reused, because the htu claim must be set to the URL of the target endpoint. Therefore, you must regenerate the DPoP Proof JWT by rerunning the generate-dpop-proof script with the token endpoint URL specified via the -u option.

Token Request Submission

After preparing the required tokens, send the token request.
The key points of this request are as follows: If the token request is successful, the token endpoint returns JSON containing an access_token property.
The value of the access_token property is the issued access token. This access token will later be set in the Authorization HTTP header of the credential request.

Credential Request

A literal interpretation of the HAIP specification suggests that it is not strictly required to include a Key Proof in the credential request. However, since it is difficult to imagine real-world use cases without key binding, the credential request example shown here includes a Key Proof.

Nonce Retrieval

If the credential issuer provides a nonce endpoint, the nonce issued by that endpoint must be included in the Key Proof and Key Attestation. Whether the credential issuer provides a nonce endpoint can be determined by checking whether the nonce_endpoint parameter is included in the credential issuer metadata. The nonce endpoint accepts an HTTP POST request and returns JSON containing a c_nonce property. Below is an example of a request and response excerpted from the OID4VCI specification. Nonce Request Example:
Nonce Response Example:
If the URL of the nonce endpoint is stored in a shell variable named NONCE_ENDPOINT, you can assign the value of the nonce to a shell variable named NONCE by executing the following command.

Key Attestation Generation

A Key Attestation can be generated using the generate-key-attestation script. Note that in HAIP, the x5c header parameter is mandatory, so you must use the --x5c option to specify the X.509 certificates to be included in the x5c header parameter. The --x5c option can be specified multiple times, and the X.509 certificates are added to the x5c header parameter in the order in which they are provided.
Below is an example of the header and payload of a generated Key Attestation.
Please note the following points:
  • The value of the typ header parameter is key-attestation+jwt.
  • The x5c header parameter includes the X.509 certificate for signature verification (as specified with the --x5c option).
  • The attested_keys claim contains the attested key(s) (as specified with the --attested-key option).
  • The nonce claim is set to the nonce (as specified with the --nonce option).

Key Proof Generation

A Key Proof of the JWT Proof Type can be generated using the generate-key-proof script.
Below is an example of the header and payload of a generated Key Proof.
Please note the following points:
  • The value of the typ header parameter is openid4vci-proof+jwt.
  • The jwk header parameter contains the client’s key (as specified with the --key option).
  • The key_attestation header parameter contains the Key Attestation (as specified with the --key-attestation option).
  • The iss claim is set to the client identifier (as specified with the --client-id option).
  • The aud claim is set to the credential issuer identifier (as specified with the --issuer option).
  • The nonce claim is set to the nonce (as specified with the --nonce option).

DPoP Proof JWT Generation

Re-run the generate-dpop-proof script with the credential endpoint URL specified via the -u option to regenerate the DPoP Proof JWT. Note that in the credential request, the DPoP Proof JWT is sent together with the access token, so the DPoP Proof JWT must include an ath claim. Therefore, when running the generate-dpop-proof script, add the -a option.

Credential Request Submission

Now that the Key Proof has been prepared, send the credential request. In this example, it is assumed that the identifier of the credential configuration associated with the digital_credential scope is DigitalCredential.
The key points of this request are as follows: In the request above, a key proof using the JWT Proof Type was used, and therefore the Key Attestation was embedded in a separate JWT. In contrast, with a Key Proof using the Attestation Proof Type, the Key Attestation can be used directly as the Key Proof, as shown below.
The following is an example of a credential response.
Each element of the credentials array is a JSON object, and the value of its credential property represents the issued VC. In this example, only one VC is issued, and its format is SD-JWT VC.

SD-JWT VC

Decoding the header and payload of the Issuer-signed JWT part of the issued SD-JWT VC yields the following.
Please note the following points:
  • The value of the typ header parameter is dc+sd-jwt.
  • The _sd claim contains a list of SHA-256 digest values of the disclosure set (note that it also includes fake digest values).
  • The _sd_alg claim indicates the hash algorithm used to compute the disclosure digests.
  • The required vct claim for SD-JWT VC is included.
  • The cnf.jwk claim contains the client key specified in the Key Proof.
The following shows information about the disclosure set.

Certificate Chain Validation

In HAIP, both the Client Attestation and the Key Attestation must include the x5c header parameter. When an authorization server or credential issuer receives such an attestation, it verifies that the certificate chain specified in the x5c header can be traced back to one of the trusted root certificates. However, HAIP does not define which root certificate set must be used for this validation. That said, given that HAIP is being developed with the EUDI Wallet in mind, it is reasonable to expect that HAIP deployments will be required to use root certificates designated by EU regulatory authorities. In such a context, a general-purpose HAIP implementation should provide a mechanism to configure the set of root certificates used for validation. Accordingly, Authlete has introduced the following set of service properties. The following is an example configuration of the root certificate set used to validate the certificate chains of Client Attestations and Key Attestations.

HAIP Activation

The method for determining whether to perform HAIP validation on requests to the authorization server or credential issuer is implementation-dependent. Authlete provides the following three methods. As of April 2026, in the current Authlete implementation, the only value that can be set for the haipVersion property and the haip attribute is the string 1.0.

Conclusion

HAIP is a profile designed to enhance interoperability and security across VC-related specifications. The HAIP-related features introduced in this document are available in Authlete version 3.0.31 and later. For further details, please contact us via the contact form.