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)
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 thekey_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 thescope 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:- PAR Request
- Authorization Request
- Token Request
- Credential Request
- PAR Request
- Authorization Request
- Authorization Request Submission
- Token Request
- Credential Request
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

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 thepkce script to
generate them.
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 thegenerate-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.
- The value of the
typheader parameter isoauth-client-attestation+jwt. - The
x5cheader parameter includes the X.509 certificate for signature verification (as specified with the--x5coption). - The
subclaim is set to the client identifier (as specified with the--client-idoption). - The
cnf.jwkclaim contains the client’s key (as specified with the--client-keyoption).
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 thechallenge_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:
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 thegenerate-client-attestation-pop script. If you need to
include a challenge claim, specify the --challenge option.
- The value of the
typheader parameter isoauth-client-attestation-pop+jwt. - The
audclaim is set to the authorization server identifier (as specified with the--as-idoption). - The
challengeclaim is set to the attestation challenge (as specified with the--challengeoption).
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).
- The value of the
typheader parameter isdpop+jwt. - The
jwkheader parameter contains the client’s key (as specified with the-koption). - The
htmclaim is set to the HTTP method of the PAR request (as specified with the-moption). - The
htuclaim is set to the URL of the PAR endpoint (as specified with the-uoption).
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.
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.
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 therequest_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 thehtu 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.
If the token request is successful, the token endpoint returns JSON containing
an
access_token property.
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

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 thenonce_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_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 thegenerate-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.
- The value of the
typheader parameter iskey-attestation+jwt. - The
x5cheader parameter includes the X.509 certificate for signature verification (as specified with the--x5coption). - The
attested_keysclaim contains the attested key(s) (as specified with the--attested-keyoption). - The
nonceclaim is set to the nonce (as specified with the--nonceoption).
Key Proof Generation
A Key Proof of the JWT Proof Type can be generated using thegenerate-key-proof script.
- The value of the
typheader parameter isopenid4vci-proof+jwt. - The
jwkheader parameter contains the client’s key (as specified with the--keyoption). - The
key_attestationheader parameter contains the Key Attestation (as specified with the--key-attestationoption). - The
issclaim is set to the client identifier (as specified with the--client-idoption). - The
audclaim is set to the credential issuer identifier (as specified with the--issueroption). - The
nonceclaim is set to the nonce (as specified with the--nonceoption).
DPoP Proof JWT Generation
Re-run thegenerate-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 thedigital_credential scope is
DigitalCredential.
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.
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.- The value of the
typheader parameter isdc+sd-jwt. - The
_sdclaim contains a list of SHA-256 digest values of the disclosure set (note that it also includes fake digest values). - The
_sd_algclaim indicates the hash algorithm used to compute the disclosure digests. - The required
vctclaim for SD-JWT VC is included. - The
cnf.jwkclaim contains the client key specified in the Key Proof.
Certificate Chain Validation
In HAIP, both the Client Attestation and the Key Attestation must include thex5c 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.