11.1.2. Credential Issuance Low-Level Flows¶
11.1.2.1. Low-Level Issuance Flow¶
The Credential Issuance flow is based on [OpenID4VCI], as required by [CIR2024/2982]. The flow is profiled by [OPENID4VC-HAIP]. The flow is controlled by [ETSI TS 119 472-3] V1.1.1, clauses 4.1 through 4.3, where ETSI specializes those specifications. The following main reference standards/specifications MUST be supported on top of OpenID4VCI:
The OAuth 2.0 Authorization Framework [RFC 6749], as recommended in Section 3 of [OpenID4VCI].
Pushed Authorization Requests (PAR) [RFC 9126], as recommended in Section 5 of [OpenID4VCI].
Proof Key for Code Exchange (PKCE) [RFC 7636], as recommended in Section 5 of [OpenID4VCI].
JWT Authorization Requests (JAR) [RFC 9101].
Rich Authorization Requests (RAR) [RFC 9396].
OAuth 2.0 Attestation-Based Client Authentication [OAUTH-ATTESTATION-CLIENT-AUTH].
OpenID4VC High Assurance Interoperability Profile [OPENID4VC-HAIP].
The Trust Framework for issuance is selected as specified in Selection at Issuance.
The Credential Issuer MUST use an OAuth 2.0 Authorization Server based on RFC 6749 to authorize the User to obtain a Credential. The Wallet Unit MUST support the Authorization Code Grant. Each Credential Issuer MUST support the Authorization Code Grant. The Credential Issuer MUST advertise that support in Authorization Server metadata [ETSI TS 119 472-3]. Credential Issuers MUST support:
Authorization Code Flow: The Credential Issuer requires User authentication and consent at the Authorization Endpoint before collecting User information to create and provide a Credential.
Wallet Initiated Flow: The request from the Wallet Instance is sent to the Credential Issuer without any input from the Credential Issuer or a third party (i.e. via Credential Offer support).
Immediate Issuance Flow: The Credential Issuer issues the Credential directly in response to the Credential Request.
In addition, the Credential Issuers MAY support the following flows.
Third-Party Initiated Flow: The Wallet Instance sends its request to the Credential Issuer based on the input provided by the Credential Issuer or a third party (for example an Authentic Source). That party supports the Credential Offer mechanism as defined in [OpenID4VCI].
Same-device Issuance Flow: The User receives the Credential on the same device used to initiate the flow.
Cross-device Issuance Flow: The User receives the Credential on another device than the one that initiated the flow.
Refresh Token Flow: The Wallet Instance requests a new Access Token at the Token Endpoint of the PID/(Q)EEA Provider.
Re-issuance Flow: Following updates to an already stored Digital Credential, the Wallet Instance requests a refresh of the Digital Credential at the Credential Endpoint of the Credential Issuer.
Deferred Issuance Flow: The Credential Issuer may require time to issue the requested Digital Credential, due to the Authentic Sources data provisioning rules, and allows the Wallet to retrieve the requested Credential in the future.
Batch Credential Issuance Flow: It enables the issuance of a batch of one or more Digital Credential. Digital Credentials that are issued in a batch MUST share the same format and contain the same set of attributes about the Holder. Each Credential MUST contain different Cryptographic Data to achieve unlinkability between the Digital Credentials.
Note
Standard or Batch Credential Issuance
The User can configure the Wallet Solution to issue Digital Credentials in either batch or standard mode and define the preferred batch size.
The entire Issuance flow can be divided into two sub-flows:
User Request Flow, describing the modes through which the User can request the Credential. It can be:
On the initiative of the User (Wallet Initiated).
Upon proposal of the Credential Issuer (Issuer Initiated).
Issuance Flow, describing interactions between Wallet Instance and Credential Issuer.
The following diagram shows the User request flow.
Steps 1.1-1.4 (Wallet Initiated Flow): The User, using the Wallet Instance, selects the Credential Issuer from those listed in the list of trustworthy entities.
Steps 2.1-2.3 (Third Party Initiated Flow): The User, while browsing the Credential Issuer website or a Third Party website, finds a link to obtain a Digital Credential. The website supports the Credential Offer mechanism (for example an Authentic Source).
Steps 2.4-2.6 (Cross-Device): The Credential Offer is presented as a QR Code. The User scans it with the device camera or with the Wallet Instance's built-in scanner, triggering the Wallet Instance to retrieve the parameters defined in the Table of Credential Offer parameters (WP_047-048).
Steps 2.7-2.10 (Same-Device): The Credential Offer is presented as an href button containing the URL that allows the User to invoke the Wallet Instance using the Credential Offer Endpoint.
The Credential Offer can be sent by value (using the credential_offer parameter) or by reference (using the credential_offer_uri parameter) as defined in Section 4 of [OpenID4VCI]. Additional details and non-normative examples are provided in Section Credential Offer Flow.
The following diagram shows the Issuance flow. The normative trust requirements are defined in Trust Evaluation Process and summarized below.
Fig. 11.5 Credential Issuance - Detailed flow.¶
Once User Request flow is completed, the Wallet Instance MUST perform the following Credential Issuer trust checks before sending the PAR Request:
Trust path selection. The Wallet Instance selects exactly one trust path according to Selection at Issuance. The Wallet Instance MUST process only that path's authoritative metadata.
EUDIW Authentication: On the EUDIW path, the Wallet Instance MUST retrieve the signed Credential Issuer Metadata, validate the Provider of WRPAC List of Trusted Entities and the WRPAC certificate chain, and verify the metadata signature. The Wallet Instance applies List of Trusted Entities Validation, EUDIW Authentication, and EUDIW Metadata Retrieval and Validation.
EUDIW Authorization: On the EUDIW path, after successful Authentication, the Wallet Instance MUST validate the WRPRC conveyed in
issuer_info, including its format, algorithm, signature, certificate chain, temporal validity, status, and consistency. The Wallet Instance MUST then verify the binding between the authenticated Credential Issuer and its registration data, the entitlement for the expected Credential Issuer role, and the exact, case-sensitive registration of the requested Credential type. The Wallet Instance applies EUDIW Authorization.National Authentication, and Authorization: On the National path, the Wallet Instance MUST validate the Credential Issuer's Federation Trust Chain, authenticate the signed protocol artifact, and consume only the applicable final metadata. The Wallet Instance applies Federation Entity Authentication and Metadata Retrieval and Validation. After successful Authentication, the Wallet Instance MUST validate the registration Trust Mark, including its signature, temporal validity, and status, and verify the Credential Issuer's entitlement and authorization to issue the offered Credential type. The Wallet Instance applies Authorization.
Failure handling: If Authentication or Authorization does not succeed, the Wallet Instance applies Failure Handling.
Additionally, in the case of Batch Credential issuance, the Wallet Instance MUST check the support of batch issuance by looking for the batch_credential_issuance object in the selected metadata. The Wallet Instance can get the batch_size value from that object. Batch issuance applies only to the Credential types for which the Credential Issuer documents support, as specified in Metadata for openid_credential_issuer.
Note
Selected-path trust check: The Wallet Instance MUST complete the Credential Issuer Authentication, Authorization, and Metadata Retrieval and Validation checks above before continuing the Authorization Code Grant flow (WP_046).
In case of Issuer Initiated flow, in addition to the Federation Check defined above, the Wallet Instance MUST execute the following checks on the Credential Offer parameters.
Steps 1-2 (PAR Request): The Wallet Instance:
Creates a fresh PKCE code verifier, Wallet Instance Attestation Proof of Possession, and
stateparameter for the Pushed Authorization Request (WP_052).Provides to the Credential Issuer PAR endpoint the parameters previously listed above, using the
requestparameter (hereafter Request Object) according to RFC 9126 Section 3 to prevent Request URI swapping attack (WP_052). The Pushed Authorization Request enables client authentication prior to any User interaction. This step allows for the early rejection of illegitimate requests, effectively preventing spoofing attacks, tampering, and improper use of authorization requests.The Wallet Instance MUST create the
code_verifieras a random string with enough entropy, using the unreserved characters, with a minimum length of 43 characters and a maximum length of 128 characters. That length makes the value impractical for an attacker to guess. The value MUST be generated following the recommendation in Section 4.1 of RFC 7636 (WP_052a).Signs this request using the private key that is created during the setup phase to obtain the Wallet Instance Attestation. The related public key that is attested by the Wallet Provider is provided within the Wallet Instance Attestation
cnf.jwkclaim (WP_052c).MUST use the
OAuth-Client-AttestationandOAuth-Client-Attestation-PoPparameters according to OAuth 2.0 Attestation-based Client Authentication [OAUTH-ATTESTATION-CLIENT-AUTH], since in this flow the Pushed Authorization Endpoint is a protected endpoint (WP_052b).MUST specify the types of the requested Credentials using both the
scopeparameter, as required by [OPENID4VC-HAIP], and theauthorization_details[RAR RFC 9396] parameter (WP_052d).
Note
This specification uses JAR [RFC 9101] to ensure end-to-end integrity of authorization requests for all Request Object parameters.
When interacting with cross-border Credential Issuers whose Authorization Servers may not support JAR, Wallet Instances SHOULD check the require_signed_request_object parameter in the Authorization Server metadata to determine whether to sign the Request Object.
Wallet Solutions supporting both JAR-compliant and non-compliant Authorization Servers may duplicate parameters in both the request body and the signed Request Object. Section 10.7 of RFC 9101 provides the security requirements on how to manage this duplication properly.
Note
User authentication method selection, including the use of it_l2+document_proof, is defined in User Authentication Method Selection. The related protocol flow is defined in eID Substantial Authentication with MRTD Verification for IT-Wallet ID Issuance.
The Credential Issuer performs the following checks upon the receipt of the PAR request:
The Credential Issuer MUST validate the signature of the Request Object using the algorithm specified in the
algheader parameter (RFC 9126, RFC 9101). The Credential Issuer uses the public key retrieved from the Wallet Instance Attestation (cnf.jwk) referenced in the Request Object, using thekidJWT header parameter.The Credential Issuer MUST check that the used algorithm for signing the request in the
algheader is one of the listed within the Section Cryptographic Algorithms.The Credential Issuer MUST check that the
client_idin the request body of the PAR request matches theclient_idclaim included in the Request Object.The Credential Issuer MUST check that the
issclaim in the Request Object matches theclient_idclaim in the Request Object (RFC 9126, RFC 9101).The Credential Issuer MUST check that the
audclaim in the Request Object is equal to the identifier of the Credential Issuer (RFC 9126, RFC 9101).The Credential Issuer MUST reject the PAR request if the PAR request contains the
request_uriparameter (RFC 9126).The Credential Issuer MUST check that the Request Object contains all the mandatory parameters. The values are validated according to Table of the HTTP parameters [derived from RFC 9126].
The Credential Issuer MUST check that the Request Object is not expired, checking the
expclaim.The Credential Issuer MUST check that the Request Object was issued in a previous time than the value exposed in the
iatclaim. The Credential Issuer SHOULD reject the request if theiatclaim is far from the current time (RFC 9126) of more than 5 minutes.The Credential Issuer MUST check that the
jticlaim in the Request Object has not been used before by the Wallet Instance identified by theclient_id. This allows the Credential Issuer to mitigate replay attacks (RFC 7519).The Credential Issuer MUST validate the
OAuth-Client-Attestation-PoPparameter based on Section 5 of [OAUTH-ATTESTATION-CLIENT-AUTH].The Credential Issuer MUST verify, if present, the validity of the
issuer_stateand the coherence with thecredential_configuration_idrequested in the Request Object.
Below is a non-normative example of the PAR Request.
POST /as/par HTTP/1.1
Host: eaa-provider.example.org
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiNDk4ZGRlMDkxNzJhZGE3MDFkMDdlYjZmOTg2N2FkIiwidHlwIjoib2F1dGgtY2xpZW50LWF0dGVzdGF0aW9uK2p3dCIsIng1YyI6WyJNSUlEcWpDQ0FwS2dBd0lCQWdJRVNMTkV2REEgLi4uIiwiTUlJQ3d6Q0NBYXNDQ1FDS1Z5OWVLanZpK2pBIC4uLiIsIk1JSURURENDQWpTZ0F3SUJBZ0lKQVBsblFZSC4uLiJdfQ.eyJzdWIiOiJodHRwczovL3dhbGxldC1zb2x1dGlvbi5leGFtcGxlLm9yZy9kZDc2MmU2ZjhiNjEzMTkzIiwiY25mIjp7Imp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjRITnB0SS14cjJwanlSSktHTW56NFdtZG5RRF91SlNxNFI5NU5qOThiNDQiLCJ5IjoiTElablNCMzl2RkpoWWdTM2s3alhFNHIzLUNvR0ZRd1p0UEJJUnFwTmxyZyJ9fSwid2FsbGV0X25hbWUiOiJXYWxsZXRfdjEiLCJ3YWxsZXRfbGluayI6Imh0dHBzOi8vZXhhbXBsZS5jb20vd2FsbGV0L2RldGFpbF9pbmZvLmh0bWwiLCJ3YWxsZXRfdmVyc2lvbiI6IjEuMC4wIiwiY2xpZW50X3N0YXR1cyI6eyJzdGF0dXMiOnsic3RhdHVzX2xpc3QiOnsiaWR4IjoxMzM3LCJ1cmkiOiJodHRwczovL3Jldm9jYXRpb25fdXJsL3dpYS1zdGF0dXNsaXN0cy80MiJ9fSwiZXhwIjoxMzAzNDk3NzgwfSwiZXhwIjoxNzQwMTU4MTY3fQ.XOv01xkHC-_h-bZFcXgkEffs3xk8WezKWs8KA4yZD8aNabDq81Y80wkOuHCM25zSGHbABszaSDsyYaJv9bh40w
OAuth-Client-Attestation-PoP: eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWNsaWVudC1hdHRlc3RhdGlvbi1wb3Arand0In0.ew0KICAiaXNzIjogIiA0N2I5ODIzNjk3OTFkMDgwMDNhNzI4M2YwNTljYjBkMSIsDQogICJhdWQiOiAiaHR0cHM6Ly9hcy5leGFtcGxlLmNvbSIsDQogICJqdGkiOiAiZDI1ZDAwYWItNTUyYi00NmZjLWFlMTktOThmNDQwZjI1MDY0IiwNCiAgImlhdCI6IDE3NDAxNTg2MTcNCn0.B0KOkGi9vMxf3H2Y8rrF-mdLNsuluTvAUbjFfL1Hi-gdaPW7-8ziS9uVh7aTnSAHKWzMfkZLv5q-bxhkglR4PA
client_id=$thumbprint-of-the-jwk-in-the-cnf-wallet-instance-attestation$&
request=$SIGNED-JWT
Below is a non-normative example of the Wallet Instance Attestation Proof of Possession (WIA-PoP) header and body:
{
"typ": "oauth-client-attestation-pop+jwt",
"alg": "ES256"
}
{
"iss": "47b982369791d08003a7283f059cb0d1",
"aud": "https://eaa-provider.example.org",
"iat": 1715842560,
"jti": "f8555ceb-c65c-4025-9378-b6672b6149af"
}
Below is a non-normative example of the signed Request Object without encoding and signature applied:
{
"typ": "oauth-authz-req+jwt",
"alg": "ES256",
"kid": "b01b8208d9e6cc834d87dc356ab50170"
}
{
"jti": "f8555ceb-c65c-4025-9378-b6672b6149af",
"aud": "https://eaa-provider.example.org",
"iat": 1715842560,
"exp": 1715842860,
"response_type": "code",
"client_id": "47b982369791d08003a7283f059cb0d1",
"iss": "47b982369791d08003a7283f059cb0d1",
"state": "fyZiOL9Lf2CeKuNT2JzxiLRDink0uPcd",
"code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
"code_challenge_method": "S256",
"scope": "Education_degree",
"authorization_details": [
{
"type": "openid_credential",
"credential_configuration_id": "dc_sd_jwt_Education_degree"
}
],
"redirect_uri": "https://client.example.com/cb"
}
Note
Wallet Unit trust check: At the PAR Endpoint, the Credential Issuer MUST validate the Wallet Instance Attestation as specified in Selection at Issuance. A failed check MUST terminate the request.
Step 3 (PAR Response): The Credential Issuer provides a one-time use request_uri value. The issued request_uri value MUST be bound to the client identifier (client_id) that was provided in the Request Object.
Note
The entropy of the request_uri MUST be sufficiently large. The adequate shortness of the validity and the entropy of the request_uri depends on the risk calculation based on the value of the resource being protected. The validity time SHOULD be less than a minute.
The request_uri MUST include a cryptographic random value of 128 bits or more (RFC 9101). The entire request_uri SHOULD NOT exceed 512 ASCII characters due to the following two main reasons (RFC 9101):
Many phones on the market still do not accept large payloads. The restriction is typically either 512 or 1024 ASCII characters.
On a slow connection such as a 2G mobile connection, a large URL would cause a slow response. The use of such a URL is not advisable from the user-experience point of view.
The Credential Issuer returns the issued request_uri to the Wallet Instance. A non-normative example of the response is shown below.
HTTP/1.1 201 Created
Cache-Control: no-cache, no-store
Content-Type: application/json
{
"request_uri": "urn:ietf:params:oauth:request_uri:bwc4JK-ESC0w8acc191e-Y1LTC2",
"expires_in": 60
}
Steps 4-5 (Authorization Request): The Wallet Instance sends an authorization request to the Credential Issuer Authorization Endpoint (WP_053). Parts of this Authorization Request content, for example the code_challenge parameter value, are unique to a particular Authorization Request. The Wallet Instance MUST use a request_uri value once (RFC 9126) as per WP_053a. The Credential Issuer performs the following checks upon the receipt of the Authorization Request:
The Credential Issuer MUST treat
request_urivalues as one-time use. The Credential Issuer MUST reject an expired request. The Credential Issuer MAY allow duplicate requests due to a User reloading or refreshing their user-agent (derived from RFC 9126).The Credential Issuer MUST identify the request as a result of the submitted PAR (derived from RFC 9126).
The Credential Issuer MUST reject all the Authorization Requests that do not contain the
request_uriparameter. The PAR is the only way to pass the Authorization Request from the Wallet Instance (derived from RFC 9126).
GET /authorize?client_id=$thumprint-of-the-jwk-in-the-cnf-wallet-instance-attestation$&request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3Abwc4JK-ESC0w8acc191e-Y1LTC2 HTTP/1.1
Host: eaa-provider.example.org
Note
User Authentication and Consent: The Credential Issuer performs User authentication at the Authorization Endpoint according to User Authentication Method Selection. During this step, Credential Issuers MAY ask the User's contact details (e.g., their email address) to send notifications about the issued Digital Credential(s).
Steps 6-7 (Authorization Response): The Credential Issuer sends an authorization code together with state and iss parameters to the Wallet Instance. The Wallet Instance performs the following checks on the Authorization Response:
The Wallet Instance MUST check that the Authorization Response contains all the defined parameters according to Table of the HTTP Response parameters (WP_054).
The Wallet Instance MUST check that the value returned by the Credential Issuer for the
stateparameter equals the value sent by the Wallet Instance in the Request Object. The check follows RFC 6749 as per WP_054a.The Wallet Instance MUST check that the URL of the Credential Issuer in the
issparameter is equal to the URL identifier of the intended Credential Issuer. The Wallet Instance starts the communication with that Credential Issuer (RFC 9207) as per WP_054b.
Note
The Wallet Instance redirect URI is a universal or app link registered with the local operating system, so this latter will resolve it and pass the response to the Wallet Instance.
HTTP/1.1 302 Found
Location: https://start.wallet.example.org?code=SplxlOBeZQQYbYS6WxSbIA&state=fyZiOL9Lf2CeKuNT2JzxiLRDink0uPcd&iss=https%3A%2F%2Feaa-provider.example.org
Steps 8-9 (DPoP Proof for Token Endpoint): The Wallet Instance MUST create a new key pair for the DPoP and a fresh DPoP Proof JWT. The Wallet Instance follows the instruction provided in Section 4 of (RFC 9449) for the token request to the Credential Issuer (WP_055b). The DPoP Proof JWT is signed using the private key for DPoP created by Wallet Instance for this scope (WP_055c). DPoP binds the Access Token, and optionally the Refresh Token, to a certain Wallet Instance (RFC 9449) and mitigates the misuse of leaked or stolen tokens at the Credential Endpoint.
Step 10 (Token Request): The Wallet Instance sends a token request to the Credential Issuer Token Endpoint with a DPoP Proof JWT. The request includes the parameters code, code_verifier, and OAuth 2.0 Attestation based Client Authentication (OAuth-Client-Attestation and OAuth-Client-Attestation-PoP) as per WP_055.
The OAuth-Client-Attestation is signed using the private key bound to the Wallet Instance (WP_055a). The related public key that is attested by the Wallet Provider is provided within the Wallet Instance Attestation (cnf.jwk claim) as per WP_055d. The Credential Issuer performs the following checks on the Token Request:
The Credential Issuer MUST ensure that the Authorization
codeis issued to the authenticated Wallet Instance (RFC 6749) and was not replied.The Credential Issuer MUST ensure the Authorization
codeis valid and has not been previously used (RFC 6749).The Credential Issuer MUST ensure the
redirect_urimatches the value included in the previous Request Object (see Section 3.1.3.1. of [OIDC]).The Credential Issuer MUST validate the DPoP Proof JWT, according to (RFC 9449) Section 4.3.
The Credential Issuer MUST verify the
code_verifierparameter according to the PKCE mechanism, ensuring it matches thecode_challengeassociated with the authorization code as defined in Section 4.6 of RFC 7636. Otherwise, the Credential Issuer MUST reject the request (CI_061a).The Credential Issuer MUST repeat the Wallet Provider, Wallet Instance Attestation signature and signing certificate chain, temporal validity, revocation status, and proof-of-possession checks defined for the PAR Endpoint. A previously retrieved trust artifact MAY be reused only while the trust artifact remains valid and fresh under the applicable trust framework.
POST /token HTTP/1.1
Host: eaa-provider.example.org
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwieCI6IjR2dDhNdEFISmlsMzBDNnpUTmt2c0VVcnlHTEUtQW5BNkc5LV8xa3l5Rk0iLCJ5IjoiTWdiNTFfbjNSRjNtbHNtS3dMd0xtRUFqVmlJM3Q1bTVWNTI2MFA5MzR3RSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIjoiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0ZWRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOH0.3Tp1ZlZ05PQYeZUHhiZwaQ1etqnwYwoiJHFR_JHb32381lMJL-8o2rE3VZ8X3yuqrGFfCVeP90Ln4J5r8ASIBg
OAuth-Client-Attestation: eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiNDk4ZGRlMDkxNzJhZGE3MDFkMDdlYjZmOTg2N2FkIiwidHlwIjoib2F1dGgtY2xpZW50LWF0dGVzdGF0aW9uK2p3dCIsIng1YyI6WyJNSUlEcWpDQ0FwS2dBd0lCQWdJRVNMTkV2REEgLi4uIiwiTUlJQ3d6Q0NBYXNDQ1FDS1Z5OWVLanZpK2pBIC4uLiIsIk1JSURURENDQWpTZ0F3SUJBZ0lKQVBsblFZSC4uLiJdfQ.eyJzdWIiOiJodHRwczovL3dhbGxldC1zb2x1dGlvbi5leGFtcGxlLm9yZy9kZDc2MmU2ZjhiNjEzMTkzIiwiY25mIjp7Imp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjRITnB0SS14cjJwanlSSktHTW56NFdtZG5RRF91SlNxNFI5NU5qOThiNDQiLCJ5IjoiTElablNCMzl2RkpoWWdTM2s3alhFNHIzLUNvR0ZRd1p0UEJJUnFwTmxyZyJ9fSwid2FsbGV0X25hbWUiOiJXYWxsZXRfdjEiLCJ3YWxsZXRfbGluayI6Imh0dHBzOi8vZXhhbXBsZS5jb20vd2FsbGV0L2RldGFpbF9pbmZvLmh0bWwiLCJ3YWxsZXRfdmVyc2lvbiI6IjEuMC4wIiwiY2xpZW50X3N0YXR1cyI6eyJzdGF0dXMiOnsic3RhdHVzX2xpc3QiOnsiaWR4IjoxMzM3LCJ1cmkiOiJodHRwczovL3Jldm9jYXRpb25fdXJsL3dpYS1zdGF0dXNsaXN0cy80MiJ9fSwiZXhwIjoxMzAzNDk3NzgwfSwiZXhwIjoxNzQwMTU4MTY3fQ.XOv01xkHC-_h-bZFcXgkEffs3xk8WezKWs8KA4yZD8aNabDq81Y80wkOuHCM25zSGHbABszaSDsyYaJv9bh40w
OAuth-Client-Attestation-PoP: eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWNsaWVudC1hdHRlc3RhdGlvbi1wb3Arand0In0.ew0KICAiaXNzIjogIiA0N2I5ODIzNjk3OTFkMDgwMDNhNzI4M2YwNTljYjBkMSIsDQogICJhdWQiOiAiaHR0cHM6Ly9hcy5leGFtcGxlLmNvbSIsDQogICJqdGkiOiAiZDI1ZDAwYWItNTUyYi00NmZjLWFlMTktOThmNDQwZjI1MDY0IiwNCiAgImlhdCI6IDE3NDAxNTg2MTcNCn0.B0KOkGi9vMxf3H2Y8rrF-mdLNsuluTvAUbjFfL1Hi-gdaPW7-8ziS9uVh7aTnSAHKWzMfkZLv5q-bxhkglR4PA
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&redirect_uri=https://start.wallet.example.org/cb
Step 11 (Token Response): The Credential Issuer validates the request. If successful, the Issuer provides the Wallet Instance with an Access Token and, optionally, a Refresh Token, both bound to the DPoP key.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImM5NTBjMGU2ZmRlYjVkZTUwYTUwMDk2YjI0N2FmMDNjIn0.eyJpc3MiOiJodHRwczovL2VhYS1wcm92aWRlci53YWxsZXQuaXB6cy5pdCIsInN1YiI6ImQ0ZTBiYjM4N2FhMjU1NmZmMzA2OTI1ZmRmYjlhNzY1IiwiYXVkIjoiaHR0cHM6Ly9lYWEtcHJvdmlkZXIud2FsbGV0LmlwenMuaXQvY3JlZGVudGlhbCIsImlhdCI6MTczOTk1Mjk0OCwiZXhwIjoxNzM5OTUzNTQ4LCJqdGkiOiJmOTY1NWNlYi1jNjVjLTQwMjUtOTM3OC1iNjY3MmI2MTQ5YmciLCJjbGllbnRfaWQiOiI0N2I5ODIzNjk3OTFkMDgwMDNhNzI4M2YwNTljYjBkMSIsImNuZiI6eyJqa3QiOiI5NTE1NzRhZWUxYmI3OTA3YWUxZWMzMTA5ZGIyYjIyNSJ9fQ.XQ3TAo69f2iPNJuMYAS5HdOSN8G5wmIRMcSR6ocLEHdDgTUNI0TrMk-KapG4fvu5_bdZkjWBJvbjXeuP-Jngpg",
"refresh_token": "eyJ0eXAiOiJydCtqd3QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImM5NTBjMGU2ZmRlYjVkZTUwYTUwMDk2YjI0N2FmMDNjIn0.eyJpc3MiOiJodHRwczovL2VhYS1wcm92aWRlci53YWxsZXQuaXB6cy5pdCIsImNsaWVudF9pZCI6IjQ3Yjk4MjM2OTc5MWQwODAwM2E3MjgzZjA1OWNiMGQxIiwiYXVkIjoiaHR0cHM6Ly9lYWEtcHJvdmlkZXIud2FsbGV0LmlwenMuaXQiLCJpYXQiOjE3Mzk5NTI5NDgsIm5iZiI6MTczOTk1MzU0OCwiZXhwIjoxNzQyMzcyNzQ4LCJhdGgiOiJmVUh5TzJyMlozRFo1M0VzTnJXQmIweFdYb2FOeTU5SWlLQ0Fxa3NtUUVvIiwianRpIjoiYzY5NTVjZWItYzY1Zi00MDI1LTkzNzgtYjY2NzJiNjE0NWNmIiwiY25mIjp7ImprdCI6Ijk1MTU3NGFlZTFiYjc5MDdhZTFlYzMxMDlkYjJiMjI1In19.qiGM6E-7zci2-3Nnk4OMD7Tv_leUcRPsFsqaBHDHxEEzsGXLNh9qDbLIBk9sujZGVT9xs-28jZhwD6VT-MGTGw",
"token_type": "DPoP",
"expires_in": 3600,
"authorization_details": [
{
"type": "openid_credential",
"credential_configuration_id": "dc_sd_jwt_Education_degree",
"credential_identifiers": [ "education_degree_unibo_2017_l31_informatica", "education_degree_unibo_2019_lm18_informatica", "education_degree_unibo_2022_phd_informatica" ]
}
]
}
A non-normative example of the DPoP Access Token is given below.
{
"typ": "at+jwt",
"alg": "ES256",
"kid": "c950c0e6fdeb5de50a50096b247af03c"
}
{
"iss": "https://eaa-provider.example.org",
"sub": "d4e0bb387aa2556ff306925fdfb9a765",
"aud": "https://eaa-provider.example.org",
"iat": 1739952948,
"exp": 1739953548,
"jti": "f9655ceb-c65c-4025-9378-b6672b6149bg",
"client_id": "47b982369791d08003a7283f059cb0d1",
"cnf": {
"jkt": "951574aee1bb7907ae1ec3109db2b225"
}
}
A non-normative example of the DPoP Refresh Token is given below.
{
"typ": "rt+jwt",
"alg": "ES256",
"kid": "c950c0e6fdeb5de50a50096b247af03c"
}
{
"iss": "https://eaa-provider.example.org",
"sub": "d4e0bb387aa2556ff306925fdfb9a765",
"aud": "https://eaa-provider.example.org",
"iat": 1739952948,
"exp": 1742372748,
"nbf": 1739953548,
"ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo",
"jti": "c6955ceb-c65f-4025-9378-b6672b6145cf",
"client_id": "47b982369791d08003a7283f059cb0d1",
"cnf": {
"jkt": "951574aee1bb7907ae1ec3109db2b225"
}
}
Step 12 (Nonce Request): According to Section 7.1 of [OpenID4VCI], the Wallet Instance sends an HTTP POST request to the Nonce Endpoint to obtain a fresh c_nonce. The Wallet Instance can use that c_nonce to create the proof of possession of key material for the subsequent request to the Credential Endpoint (WP_056a).
Below is a non-normative example of a Nonce Request:
POST /nonce HTTP/1.1
Host: eaa-provider.example.org
Content-Length: 0
Step 13 (Nonce Response): The Credential Issuer provides the c_nonce to the Wallet Instance. The parameter c_nonce is a string value, which MUST be unpredictable. The Wallet Instance uses c_nonce later in Step 16 to create the proof(s) of possession of the key (proofs claim). The parameter is the primary countermeasure against a key proof replay attack.
Note that, the received c_nonce value can be used to create the proof(s) as long as the Issuer
provides the Wallet Instance with a new c_nonce value.
Below is a non-normative example of a Nonce Response:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"c_nonce": "ts_EtUQs0ieiIS1NYNBHEQSoy3ct4gpy-4FZKwHilkY"
}
Step 14 (DPoP Proof for Credential Endpoint): The Wallet Instance creates a DPoP Proof JWT using the same key in Step 8 and according to (RFC 9449) Section 4 (WP_056b).
Note
If the Wallet Instance requests a batch issuance of Digital Credentials, the flow continues with Step 17. Otherwise Steps 15-16 are performed.
Step 15 (Proof of Possession of Credential): The Wallet Instance creates a proof of possession of the key material to which the Digital Credential will be bound. The Wallet Instance uses the c_nonce obtained in Step 13. The Wallet Instance MUST sign that proof with the private key of that key material. The jwk value in the proof MUST be the public key of that key pair. The Credential binding key is independent of the DPoP key used in Step 14.
Step 16 (Credential Request): The Wallet Instance sends a request for the Digital Credential to the Credential endpoint. This request MUST include the Access Token, DPoP Proof JWT, Credential type, proof (which demonstrates possession of the key) as per WP_056. The proofs parameter MUST be an object that contains evidence of possession of the cryptographic key material to which the issued Digital Credential will be bound. To verify the proof, the Credential Issuer conducts the following checks at the Credential endpoint:
The JWT proof MUST include all required claims as specified in the table of Section Token Request.
The key proof MUST be explicitly typed using header parameters as defined for the respective proof type.
The header parameter
algMUST indicate a registered asymmetric digital signature algorithm. The header parameteralgMUST NOT be set to none.The signature on the key proof MUST be verified using the public key specified in the header parameter.
The header parameter MUST NOT contain a private key.
The signature on the Key Attestation JWT as the value of the
key_attestationheader parameter MUST be verified using the Wallet Provider's public key. Thekidheader parameter inside the Key Attestation JWT identifies that public key.If a
c_noncevalue was previously provided by the server, thenonceclaim in the JWT MUST match thisc_noncevalue. Furthermore, the creation time of the JWT, as indicated by theiatclaim or a server-managed timestamp via thenonceclaim, MUST be within an acceptable window of time as determined by the server.For each Key Attestation of a Wallet Unit, the Credential Issuer MUST establish trust in the Wallet Provider through the Trust Framework selected for the Wallet Instance Attestation, as specified in Selection at Issuance. The Credential Issuer MUST validate the Key Attestation signing certificate chain according to X509 Certificate Chain Validation Algorithm. The Credential Issuer MUST check the temporal validity of the Key Attestation. The Credential Issuer MUST check the revocation status of the Key Attestation. The Credential Issuer MUST verify the associated proof of possession. The Credential Issuer MUST verify that the attested key protection satisfies the Credential Issuer policy, if any. A failed check MUST terminate the request.
Note
The Credential Issuer MUST register all the issued Credentials for their later revocation, if needed.
Note
It is RECOMMENDED that the public key contained in the jwt_proof be specifically generated for the requested Credential (fresh cryptographic key) to ensure that different issued Credentials do not share the same public key, thereby remaining unlinkable to each other.
A non-normative example of the Credential Request is provided below.
POST /credential HTTP/1.1
Host: eaa-provider.example.org
Content-Type: application/json
Authorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R
1JEQSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIj
oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0Z
WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNF
c05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyJ9.2oW9RP35yRqzhrtNP86L-Ey71E
OptxRimPPToA1plemAgR6pxHF8y6-yqyVnmcw6Fy1dqd-jfxSYoMxhAJpLjA
{
"credential_identifier": "education_degree_unibo_2017_l31_informatica",
"proofs": {
"jwt": [
"eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiNEZpM3dESWdmYmVRSzYyNE55UjdYT3ZBazNzaGFXeTkzNXl2NVNIQnFNdyIsInkiOiJELVIxMHJQeE0weXFUSkY2STN2LTczVVNDVV82RkhhenlJSlBMRXN5VUZFIn0sImtleV9hdHRlc3RhdGlvbiI6ImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJalYwTlZsWmNFSm9UaTFGWjBsRlJVazFhVlY2Y2paeU1FMVNNREpNYmxaUk1FOXRaV3R0VGt0amFsa2lMQ0owY25WemRGOWphR0ZwYmlJNld5SmxlVXBvWWtkamFVOXBTa1pWZWk0dUxqWlRNRUVpTENKbGVVcG9Za2RqYVU5cFNrWlZlaTR1TG1wS1RFRWlMQ0psZVVwb1lrZGphVTlwU2taVmVpNHVMa2c1WjNjaVhTd2lkSGx3SWpvaWEyVjVZWFIwWlhOMFlYUnBiMjRyYW5kMEluMC5leUpoZEhSbGMzUmxaRjlyWlhseklqcGJleUpqY25ZaU9pSlFMVEkxTmlJc0ltdDBlU0k2SWtWRElpd2llQ0k2SWpSR2FUTjNSRWxuWm1KbFVVczJNalJPZVZJM1dFOTJRV3N6YzJoaFYzazVNelY1ZGpWVFNFSnhUWGNpTENKNUlqb2lSQzFTTVRCeVVIaE5NSGx4VkVwR05ra3pkaTAzTTFWVFExVmZOa1pJWVhwNVNVcFFURVZ6ZVZWR1JTSjlYU3dpYTJWNVgzTjBiM0poWjJWZmMzUmhkSFZ6SWpwN0luTjBZWFIxYzE5c2FYTjBJanA3SW1sa2VDSTZOREV5TENKMWNta2lPaUpvZEhSd2N6b3ZMM0psZG05allYUnBiMjVmZFhKc0wzTjBZWFIxYzJ4cGMzUnpMekVpZlN3aVpYaHdJam94TlRRME1UY3lNVEkwZlN3aWFXRjBJam94TmpnM01qZ3hNVGsxTENKbGVIQWlPakUyT0RjeU9EZ3pPVFVzSW10bGVWOXpkRzl5WVdkbElqcGJJbWx6YjE4eE9EQTBOVjl0YjJSbGNtRjBaU0pkTENKMWMyVnlYMkYxZEdobGJuUnBZMkYwYVc5dUlqcGJJbWx6YjE4eE9EQTBOVjl0YjJSbGNtRjBaU0pkZlEuWndqZUpUQTNnc3Z0Mk1NdzlTMy1iZzREblY1TGRRRXB6OUYxcmVCRWlDblRuaURVaE9jS010RzVnNmZwRFBVMWRQMjBzdTlXVXR1Rld6c0d1b0dhNFEifQ.eyJpc3MiOiI0N2I5ODIzNjk3OTFkMDgwMDNhNzI4M2YwNTljYjBkMSIsImF1ZCI6Imh0dHBzOi8vZWFhLXByb3ZpZGVyLmV4YW1wbGUub3JnIiwiaWF0IjoxNzA1NTcwMDU1LCJleHAiOjE3Nzg5MTQ1NjAsIm5vbmNlIjoidHNfRXRVUXMwaWVpSVMxTllOQkhFUVNveTNjdDRncHktNEZaS3dIaWxrWSJ9.2ZQF2kYONuzkqIJ7nJxSl41-f0bTVSBdDpLFSrhA6PfmrcQJzBlTsv7FuGtXN-6mF3tZroklA2zjLmcaSCP3QQ"
]
}
}
Where a non-normative example of the decoded content of the jwt parameter is represented below, without encoding and signature.
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "4Fi3wDIgfbeQK624NyR7XOvAk3shaWy935yv5SHBqMw",
"y": "D-R10rPxM0yqTJF6I3v-73USCU_6FHazyIJPLEsyUFE"
},
"key_attestation": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjV0NVlZcEJoTi1FZ0lFRUk1aVV6cjZyME1SMDJMblZRME9tZWttTktjalkiLCJ0cnVzdF9jaGFpbiI6WyJleUpoYkdjaU9pSkZVei4uLjZTMEEiLCJleUpoYkdjaU9pSkZVei4uLmpKTEEiLCJleUpoYkdjaU9pSkZVei4uLkg5Z3ciXSwidHlwIjoia2V5YXR0ZXN0YXRpb24rand0In0.eyJhdHRlc3RlZF9rZXlzIjpbeyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjRGaTN3RElnZmJlUUs2MjROeVI3WE92QWszc2hhV3k5MzV5djVTSEJxTXciLCJ5IjoiRC1SMTByUHhNMHlxVEpGNkkzdi03M1VTQ1VfNkZIYXp5SUpQTEVzeVVGRSJ9XSwia2V5X3N0b3JhZ2Vfc3RhdHVzIjp7InN0YXR1cyI6eyJzdGF0dXNfbGlzdCI6eyJpZHgiOjQxMiwidXJpIjoiaHR0cHM6Ly9yZXZvY2F0aW9uX3VybC9zdGF0dXNsaXN0cy8xIn19LCJleHAiOjE1NDQxNzIxMjR9LCJpYXQiOjE2ODcyODExOTUsImV4cCI6MTY4NzI4ODM5NSwia2V5X3N0b3JhZ2UiOlsiaXNvXzE4MDQ1X21vZGVyYXRlIl0sInVzZXJfYXV0aGVudGljYXRpb24iOlsiaXNvXzE4MDQ1X21vZGVyYXRlIl19.LG9hkaoGmznUAL43gytXyEOoH7J-kKZQj_sG7Row2DUl0OYArV3mJnDmIuK_4-YVPg4AM03xV028BvFRwv5XDw"
}
{
"iss": "47b982369791d08003a7283f059cb0d1",
"aud": "https://eaa-provider.example.org",
"iat": 1705570055,
"nonce": "ts_EtUQs0ieiIS1NYNBHEQSoy3ct4gpy-4FZKwHilkY"
}
Step 17 (Fresh Digital Credential Keys Generation): The Wallet Instance generates N fresh Credential key pairs, where the number of key pairs (N) is determined by the value defined in the batch_size (WP_058a).
Step 18 (Proofs of Possession of Credentials): The Wallet Instance MUST generate N key proofs using provided c_nonce in Step 13 and one for each Credential in the batch. The number of key proofs (N) is defined by the batch_size value (WP_058b).
Note
The c_nonce value in all the jwt proofs is identical and it is not needed to obtain separate nonce values per proof.
Step 19 (Batch Credential Request): The Wallet Instance sends a request for the batch of Digital Credential to the Credential endpoint. This request MUST include the Access Token, DPoP Proof JWT, Credential type, proofs (which demonstrates possession of the keys). The proofs parameter MUST set using a JSON object containing two or more evidence of possession of the cryptographic key materials to which the issued batch of Digital Credential will be bound. To verify the proofs, the Credential Issuer, in addition to the predefined checks in Step 16, must ensure the jwk attribute in each key proofs is unique (WP_058).
POST /credential HTTP/1.1
Host: eaa-provider.example.org
Content-Type: application/json
Authorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R
1JEQSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIj
oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0Z
WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNF
c05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyJ9.2oW9RP35yRqzhrtNP86L-Ey71E
OptxRimPPToA1plemAgR6pxHF8y6-yqyVnmcw6Fy1dqd-jfxSYoMxhAJpLjA
{
"credential_identifier": "education_degree_unibo_2017_l31_informatica",
"proofs": {
"jwt": [
"eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiNEZpM3dESWdmYmVRSzYyNE55UjdYT3ZBazNzaGFXeTkzNXl2NVNIQnFNdyIsInkiOiJELVIxMHJQeE0weXFUSkY2STN2LTczVVNDVV82RkhhenlJSlBMRXN5VUZFIn0sImtleV9hdHRlc3RhdGlvbiI6ImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJalYwTlZsWmNFSm9UaTFGWjBsRlJVazFhVlY2Y2paeU1FMVNNREpNYmxaUk1FOXRaV3R0VGt0amFsa2lMQ0owY25WemRGOWphR0ZwYmlJNld5SmxlVXBvWWtkamFVOXBTa1pWZWk0dUxqWlRNRUVpTENKbGVVcG9Za2RqYVU5cFNrWlZlaTR1TG1wS1RFRWlMQ0psZVVwb1lrZGphVTlwU2taVmVpNHVMa2c1WjNjaVhTd2lkSGx3SWpvaWEyVjVZWFIwWlhOMFlYUnBiMjRyYW5kMEluMC5leUpoZEhSbGMzUmxaRjlyWlhseklqcGJleUpqY25ZaU9pSlFMVEkxTmlJc0ltdDBlU0k2SWtWRElpd2llQ0k2SWpSR2FUTjNSRWxuWm1KbFVVczJNalJPZVZJM1dFOTJRV3N6YzJoaFYzazVNelY1ZGpWVFNFSnhUWGNpTENKNUlqb2lSQzFTTVRCeVVIaE5NSGx4VkVwR05ra3pkaTAzTTFWVFExVmZOa1pJWVhwNVNVcFFURVZ6ZVZWR1JTSjlYU3dpYTJWNVgzTjBiM0poWjJWZmMzUmhkSFZ6SWpwN0luTjBZWFIxYzE5c2FYTjBJanA3SW1sa2VDSTZOREV5TENKMWNta2lPaUpvZEhSd2N6b3ZMM0psZG05allYUnBiMjVmZFhKc0wzTjBZWFIxYzJ4cGMzUnpMekVpZlN3aVpYaHdJam94TlRRME1UY3lNVEkwZlN3aWFXRjBJam94TmpnM01qZ3hNVGsxTENKbGVIQWlPakUyT0RjeU9EZ3pPVFVzSW10bGVWOXpkRzl5WVdkbElqcGJJbWx6YjE4eE9EQTBOVjl0YjJSbGNtRjBaU0pkTENKMWMyVnlYMkYxZEdobGJuUnBZMkYwYVc5dUlqcGJJbWx6YjE4eE9EQTBOVjl0YjJSbGNtRjBaU0pkZlEuWndqZUpUQTNnc3Z0Mk1NdzlTMy1iZzREblY1TGRRRXB6OUYxcmVCRWlDblRuaURVaE9jS010RzVnNmZwRFBVMWRQMjBzdTlXVXR1Rld6c0d1b0dhNFEifQ.eyJpc3MiOiI0N2I5ODIzNjk3OTFkMDgwMDNhNzI4M2YwNTljYjBkMSIsImF1ZCI6Imh0dHBzOi8vZWFhLXByb3ZpZGVyLmV4YW1wbGUub3JnIiwiaWF0IjoxNzA1NTcwMDU1LCJleHAiOjE3Nzg5MTQ1NjAsIm5vbmNlIjoidHNfRXRVUXMwaWVpSVMxTllOQkhFUVNveTNjdDRncHktNEZaS3dIaWxrWSJ9.2ZQF2kYONuzkqIJ7nJxSl41-f0bTVSBdDpLFSrhA6PfmrcQJzBlTsv7FuGtXN-6mF3tZroklA2zjLmcaSCP3QQ",
"eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiNEZpM3dESWdmYmVRSzYyNE55UjdYT3ZBazNzaGFXeTkzNXl2NVNIQnFNdyIsInkiOiJELVIxMHJQeE0weXFUSkY2STN2LTczVVNDVV82RkhhenlJSlBMRXN5VUZFIn0sImtleV9hdHRlc3RhdGlvbiI6ImV5SmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJalYwTlZsWmNFSm9UaTFGWjBsRlJVazFhVlY2Y2paeU1FMVNNREpNYmxaUk1FOXRaV3R0VGt0amFsa2lMQ0owY25WemRGOWphR0ZwYmlJNld5SmxlVXBvWWtkamFVOXBTa1pWZWk0dUxqWlRNRUVpTENKbGVVcG9Za2RqYVU5cFNrWlZlaTR1TG1wS1RFRWlMQ0psZVVwb1lrZGphVTlwU2taVmVpNHVMa2c1WjNjaVhTd2lkSGx3SWpvaWEyVjVZWFIwWlhOMFlYUnBiMjRyYW5kMEluMC5leUpoZEhSbGMzUmxaRjlyWlhseklqcGJleUpqY25ZaU9pSlFMVEkxTmlJc0ltdDBlU0k2SWtWRElpd2llQ0k2SWpSR2FUTjNSRWxuWm1KbFVVczJNalJPZVZJM1dFOTJRV3N6YzJoaFYzazVNelY1ZGpWVFNFSnhUWGNpTENKNUlqb2lSQzFTTVRCeVVIaE5NSGx4VkVwR05ra3pkaTAzTTFWVFExVmZOa1pJWVhwNVNVcFFURVZ6ZVZWR1JTSjlYU3dpYTJWNVgzTjBiM0poWjJWZmMzUmhkSFZ6SWpwN0luTjBZWFIxYzE5c2FYTjBJanA3SW1sa2VDSTZOREV5TENKMWNta2lPaUpvZEhSd2N6b3ZMM0psZG05allYUnBiMjVmZFhKc0wzTjBZWFIxYzJ4cGMzUnpMekVpZlN3aVpYaHdJam94TXpBM09EQXpOVFkwZlN3aWFXRjBJam94TmpnM01qZ3hNVGsxTENKbGVIQWlPakUyT0RjeU9EZ3pPVFVzSW10bGVWOXpkRzl5WVdkbElqcGJJbWx6YjE4eE9EQTBOVjl0YjJSbGNtRjBaU0pkTENKMWMyVnlYMkYxZEdobGJuUnBZMkYwYVc5dUlqcGJJbWx6YjE4eE9EQTBOVjl0YjJSbGNtRjBaU0pkZlEuUGRFYWhhVlAwT2dqOTRwYTdiSGcxY1BKVmF6bHRibGJORHNxU2R1QVNDUkxjY1AycGxCaDYwNHZNcWp5WHpSV3pDN0xCbXZpQ29IN1hjVEJReXJKaUEifQ.eyJpc3MiOiI0N2I5ODIzNjk3OTFkMDgwMDNhNzI4M2YwNTljYjBkMSIsImF1ZCI6Imh0dHBzOi8vZWFhLXByb3ZpZGVyLmV4YW1wbGUub3JnIiwiaWF0IjoxNzA1NTcwMDU1LCJleHAiOjE3Nzg5MTQ1NjAsIm5vbmNlIjoidHNfRXRVUXMwaWVpSVMxTllOQkhFUVNveTNjdDRncHktNEZaS3dIaWxrWSJ9.mThywjS7lYdmYpR3FY0cVVQJlqaiOPfnEmQU2UcNt4mP6Gl6qgAyhuwqAZoAx5yEeZTzRVzwt44NMrk6mmra8w"
]
}
}
The decoded content of jwt elements in the jwt array is similar to what is explained in Step 16.
Step 20 (Credential Response): The Credential Issuer MUST validate the DPoP JWT Proof based on the steps defined in Section 4.3 of (RFC 9449). The Credential Issuer MUST also check whether the Access Token is valid and suitable for the requested Credential. The Credential Issuer MUST validate all the key proofs that are provided within the proofs parameter (Step 15 and Step 18). The new Credentials MUST be bound to those key proofs, according to OpenID4VCI Appendix F1. If all checks succeed, the Credential Issuer returns the issued Credential inside the credentials parameter. The number of elements in the Credentials array matches the number of the keys that the Wallet Instance has provided either via the proofs parameter (Step 16 and Step 19).
Step 21 (Credential Validation): The Wallet Instance MUST perform the following checks before proceeding with the secure storage of the Credential(s):
The Wallet Instance MUST check that the PID/(Q)EAA contained in the Credential Response contains all the mandatory parameters. The values are validated according to Table of the Credential response parameters (WP_059).
The Wallet Instance MUST check the Credential integrity by verifying the signature using the algorithm specified in the
algheader parameter of SD-JWT (Digital Credential Data Model). The Wallet Instance uses the public key that is identified using thekidheader of the SD-JWT (WP_062a).The Wallet Instance MUST check that the received Digital Credential(s) (in the
credentialclaim) match the requested Credential type and comply with the specific schema of that Credential defined in Digital Credential Data Model (WP_060).The Wallet Instance MUST process and verify the Credential in SD-JWT VC format (according to SD-JWT Section 4.) or mdoc-CBOR format (WP_062).
For each received Digital Credential, the Wallet Instance MUST evaluate trust with the signer. On the EUDIW path the Wallet Instance MUST apply EUDIW Attestation Signature Validation. On the National path the Wallet Instance MUST apply Signing Trust Anchor Validation Procedure (WP_061).
If the checks above are successful, the Wallet Instance MUST associate any resolved Embedded Disclosure Policy from the authenticated metadata with the corresponding issued EAA according to EUDIW Authorization.
Step 22 (Storage Consent Request): The Wallet Instance requests the User's consent to store the Digital Credential(s).
Step 23 (Storage Consent Response): The User provides or denies consent to store the Digital Credential(s).
Step 24 (Credential Storage): Upon receiving consent, the Wallet Instance securely stores the Digital Credential(s) (WP_063).
Below is a non-normative example of a successful response containing a Credential in SD-JWT VC format.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"credentials": [
{
"credential": "eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImM5NTBjMGU2ZmRlYjVkZTUwYTUwMDk2YjI0N2FmMDNjIn0.eyJfc2QiOlsiQ1JJQkdpbWhhbE1TSzhLZUxmNzg2N0w4cHV6MEZnRTVaS1VXLW12N0hiYyIsIlhhZjVJZFVGc0UzYWtabEszT0E5d3dHcjJKcVAwUU01M3BBY2hiempRZmMiLCJjR2FuQVdySG9WQVoyalBhNXk0SzE3U0xpYWFKcGRNUF9PdnBmTGx0VWJjIiwiNEFuZU1ZVVAxRWh3emRHdkRQOEhobnRaRGN1ejZrOHhHWVJ0NXo0SHh0SSIsIjRuYTVXSHRMYzdrYnNxdHFVaHd6WXdVdUQtY3hKVmdENmRaLTl0ZUdhZ3MiLCJiZS10a2U3YVU0WmhDNWUxcGZqRjcxUWpFRzRsZG1IaFRoUFl1TnQyOHo0IiwiSnNkaHhTMWRVTTJaN29MUmZYWVJvOFFEaDJ4M1dQNkdILUJnY21DdzJGayIsIlVobXBRSy1HS3hzaHJwXzZwYVpfZzROVG5fX29aeVdOb01zTGNaMUhlMjgiLCJOdDdQU3RxMkEyWkliUHBHdTJpdmVSek9rbWpYUEN1V0RBdy0tdktSQTBjIiwiV3Rtck5JVzFTVkN0UnZNUF9UM2YtRGlhRUdHNS1iVk5rWGJRckowRnpzRSJdLCJleHAiOjE3NDY1MTU5NzIsImlzcyI6Imh0dHBzOi8vZWFhLXByb3ZpZGVyLmlwenMuaXQiLCJzdWIiOiJOemJMc1hoOHVEQ2NkN25vV1hGWkFmSGt4WnNSR0M5WHMiLCJzdGF0dXMiOnsic3RhdHVzX2F0dGVzdGF0aW9uIjp7ImNyZWRlbnRpYWxfaGFzaF9hbGciOiJzaGEtMjU2In19LCJ2Y3QiOiJFZHVjYXRpb25fZGVncmVlIiwiX3NkX2FsZyI6InNoYS0yNTYiLCJjbmYiOnsiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoicFZVM2phdHU0YTN0azljOWFvd1ZnTHlCQl9ySjdNLTNXbGprMWVqVXoyRSIsInkiOiJUTDVPTnZSLUlnYXJuZ3J6NWpkdnNwb2ZmekZ3Y2pQUnRGVWtlbmVIRUkwIn19fQ.v9ynFXhKXPOhQSMmuLvIBKRWfPEPDf4QwDoNmDOjMROxr5J4Hshh9mBEM5qohH_PDE62i1TLc36C65jFYa7x3A~WyJjQ0ZDeXljV1J4alZINkZURVR5OTd3IiwidGF4X2lkX2NvZGUiLCJSU1NNUkE4MFIwMUg1MDFCIl0~...~WyIwQUx5SzRfUi1aVUpTekVKdW5HTFdRIiwiaWF0IiwiMTc0NzExOTU5NSJd"
}
],
"notification_id": "dab8ef51-fb43-43a5-a5c1-247c93ddb942"
}
Below is a non-normative example of a successful response containing a Credential in mdoc format.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"credentials": [
{
"credential": "omppc3N1ZXJBdXRohEOhASahG...ArQwggKwMIICVqADAgEC"
}
],
"notification_id": "dab8ef51-fb43-43a5-a5c1-247c93ddb942"
}
Below is a non-normative example of a successful response containing a batch of Credentials in SD-JWT VC format:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"credentials": [
{
"credential": "eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImM5NTBjMGU2ZmRlYjVkZTUwYTUwMDk2YjI0N2FmMDNjIn0.eyJfc2QiOlsiQ1JJQkdpbWhhbE1TSzhLZUxmNzg2N0w4cHV6MEZnRTVaS1VXLW12N0hiYyIsIlhhZjVJZFVGc0UzYWtabEszT0E5d3dHcjJKcVAwUU01M3BBY2hiempRZmMiLCJjR2FuQVdySG9WQVoyalBhNXk0SzE3U0xpYWFKcGRNUF9PdnBmTGx0VWJjIiwiNEFuZU1ZVVAxRWh3emRHdkRQOEhobnRaRGN1ejZrOHhHWVJ0NXo0SHh0SSIsIjRuYTVXSHRMYzdrYnNxdHFVaHd6WXdVdUQtY3hKVmdENmRaLTl0ZUdhZ3MiLCJiZS10a2U3YVU0WmhDNWUxcGZqRjcxUWpFRzRsZG1IaFRoUFl1TnQyOHo0IiwiSnNkaHhTMWRVTTJaN29MUmZYWVJvOFFEaDJ4M1dQNkdILUJnY21DdzJGayIsIlVobXBRSy1HS3hzaHJwXzZwYVpfZzROVG5fX29aeVdOb01zTGNaMUhlMjgiLCJOdDdQU3RxMkEyWkliUHBHdTJpdmVSek9rbWpYUEN1V0RBdy0tdktSQTBjIiwiV3Rtck5JVzFTVkN0UnZNUF9UM2YtRGlhRUdHNS1iVk5rWGJRckowRnpzRSJdLCJleHAiOjE3NDY1MTU5NzIsImlzcyI6Imh0dHBzOi8vZWFhLXByb3ZpZGVyLmlwenMuaXQiLCJzdWIiOiJOemJMc1hoOHVEQ2NkN25vV1hGWkFmSGt4WnNSR0M5WHMiLCJzdGF0dXMiOnsic3RhdHVzX2F0dGVzdGF0aW9uIjp7ImNyZWRlbnRpYWxfaGFzaF9hbGciOiJzaGEtMjU2In19LCJ2Y3QiOiJFZHVjYXRpb25fZGVncmVlIiwiX3NkX2FsZyI6InNoYS0yNTYiLCJjbmYiOnsiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoicFZVM2phdHU0YTN0azljOWFvd1ZnTHlCQl9ySjdNLTNXbGprMWVqVXoyRSIsInkiOiJUTDVPTnZSLUlnYXJuZ3J6NWpkdnNwb2ZmekZ3Y2pQUnRGVWtlbmVIRUkwIn19fQ.v9ynFXhKXPOhQSMmuLvIBKRWfPEPDf4QwDoNmDOjMROxr5J4Hshh9mBEM5qohH_PDE62i1TLc36C65jFYa7x3A~WyJjQ0ZDeXljV1J4alZINkZURVR5OTd3IiwidGF4X2lkX2NvZGUiLCJSU1NNUkE4MFIwMUg1MDFCIl0~...~WyIwQUx5SzRfUi1aVUpTekVKdW5HTFdRIiwiaWF0IiwiMTc0NzExOTU5NSJd"
},
{
"credential": "YXNkZnNhZGZkamZqZGFza23....29tZTIzMjMyMzIzMjMy"
}
],
"notification_id": "dab8ef51-fb43-43a5-a5c1-247c93ddb942"
}
Note
When the Wallet Instance receives a new batch of the same Credential with the same claims, the Wallet MUST delete previous Credentials (WP_073a).
Step 27 (Deferred Issuance): If the requested Credential cannot be issued immediately and requires more time, the Credential Issuer SHOULD support the Deferred Flow as specified in Section Deferred Endpoint (WP_065–067). The Wallet Instance uses the transaction_id from the HTTP 202 Credential Response to request the deferred Credential until it is available, respecting the indicated interval. Once available, processing continues at Step 20. In the case of batch issuance, the same transaction_id retrieves all Credentials requested in the batch.
Step 25 (Notification Request): According to Section 11.1 of [OpenID4VCI], the Wallet sends an HTTP POST request to the Notification Endpoint using the application/json media type. A non-normative example follows (WP_064).
POST /notification HTTP/1.1
Host: eaa-provider.example.org
Content-Type: application/json
Authorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R
1JEQSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIj
oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0Z
WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNF
c05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyJ9.2oW9RP35yRqzhrtNP86L-Ey71E
OptxRimPPToA1plemAgR6pxHF8y6-yqyVnmcw6Fy1dqd-jfxSYoMxhAJpLjA
{
"notification_id": "dab8ef51-fb43-43a5-a5c1-247c93ddb942",
"event": "credential_accepted"
}
Step 26 (Notification Response): When the Credential Issuer has successfully received the Notification Request from the Wallet, the Credential Issuer MUST respond with an HTTP status code 204. Section 11.2 of [OpenID4VCI] recommends that status code. Below is a non-normative example of response to a successful Notification Request:
HTTP/1.1 204 No Content
Note
In some cases there are multiple sets of Credential data available in the Authentic Source system, and the User may be interested in obtaining more than one Credential. In these cases the Issuance Flow remains the same as described in Low-Level Issuance Flow. In the Token Response (Step 11), the Credential Issuer generates a unique identifier (credential_identifier) for each Credential Dataset provided in the AttributeClaims parameter of the e-service PDND Get Attribute Claims. Therefore, the credential_identifiers array within the authorization_details object contains the identifiers of all Credentials available for issuance. The Wallet sends a Credential Request (Step 16) for each identifier, obtaining multiple distinct Credentials that are individually shown to the User for the acceptance. Finally, the Credential Issuer is notified by the Wallet of the outcome through the Notification endpoint (WP_057).
Note
For batch-issued Digital Credentials, a single notification_id covers the entire batch-issued Credentials. The notification response (e.g. credential_accepted or credential_stored) applies to all Credentials, any partial failure is treated as a batch failure.
11.1.2.2. Refresh Token Flow¶
To use the Deferred, Credential Request, and Notification endpoints, the Wallet Instance MUST present a valid DPoP Access Token to the Credential Issuer. When these endpoints are used in the Deferred Flow, for re-issuing or notifying the deletion of a Digital Credential, the Access Token might expire. The Access Token is designed to be short-lived, and these actions MAY occur days later (WP_066b). To address this, the specification RECOMMENDS the use of Refresh Tokens.
An Access Token obtained as a result of a Refresh Token flow MUST be limited to the following endpoints.
The Deferred endpoint, to obtain a new Digital Credential after the time set in the parameter
intervalor when the Digital Credential is notified as ready to be issued (WP_066).The Notification endpoint, to notify the deletion of a Digital Credential to the Credential Issuer.
The Credential endpoint, to refresh a Digital Credential that is already present in the Wallet Instance (also called Digital Credential re-issuance, see section Re-Issuance Flow).
To mitigate the impact of a stolen Refresh Token, the Refresh Tokens MUST be DPoP. These aspects are detailed and discussed in Section Security Considerations.
Figure below shows how to obtain a new DPoP Access Token and a new DPoP Refresh Token to the Token Endpoint.
Fig. 11.6 Refresh Token Flow.¶
Note
The refresh of a Token may be triggered by different actions (e.g., User deletion of a Digital Credential). In each case, Wallet Instances are supposed to be running and the corresponding cryptographic material unlocked.
Step 1: The Wallet Instance MUST create a fresh DPoP Proof JWT and a fresh Wallet Instance Attestation proof of possession for the token request of the Credential Issuer (WP_068a).
Step 2: To refresh a DPoP-bound Access Token, the Wallet Instance sends a token request using the parameter grant_type set to refresh_token. The request includes the DPoP header and the OAuth Client Attestation headers (WP_068).
A non-normative example of the token request for a DPoP Access Token using a Refresh Token is shown below.
POST /token HTTP/1.1
Host: eaa-provider.example.org
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwieCI6IjR2dDhNdEFISmlsMzBDNnpUTmt2c0VVcnlHTEUtQW5BNkc5LV8xa3l5Rk0iLCJ5IjoiTWdiNTFfbjNSRjNtbHNtS3dMd0xtRUFqVmlJM3Q1bTVWNTI2MFA5MzR3RSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIjoiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0ZWRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOH0.3Tp1ZlZ05PQYeZUHhiZwaQ1etqnwYwoiJHFR_JHb32381lMJL-8o2rE3VZ8X3yuqrGFfCVeP90Ln4J5r8ASIBg
OAuth-Client-Attestation: eyJhbGciOiJFUzI1NiIsImtpZCI6IjBiNDk4ZGRlMDkxNzJhZGE3MDFkMDdlYjZmOTg2N2FkIiwidHlwIjoib2F1dGgtY2xpZW50LWF0dGVzdGF0aW9uK2p3dCIsIng1YyI6WyJNSUlEcWpDQ0FwS2dBd0lCQWdJRVNMTkV2REEgLi4uIiwiTUlJQ3d6Q0NBYXNDQ1FDS1Z5OWVLanZpK2pBIC4uLiIsIk1JSURURENDQWpTZ0F3SUJBZ0lKQVBsblFZSC4uLiJdfQ.eyJzdWIiOiJodHRwczovL3dhbGxldC1zb2x1dGlvbi5leGFtcGxlLm9yZy9kZDc2MmU2ZjhiNjEzMTkzIiwiY25mIjp7Imp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjRITnB0SS14cjJwanlSSktHTW56NFdtZG5RRF91SlNxNFI5NU5qOThiNDQiLCJ5IjoiTElablNCMzl2RkpoWWdTM2s3alhFNHIzLUNvR0ZRd1p0UEJJUnFwTmxyZyJ9fSwid2FsbGV0X25hbWUiOiJXYWxsZXRfdjEiLCJ3YWxsZXRfbGluayI6Imh0dHBzOi8vZXhhbXBsZS5jb20vd2FsbGV0L2RldGFpbF9pbmZvLmh0bWwiLCJ3YWxsZXRfdmVyc2lvbiI6IjEuMC4wIiwiY2xpZW50X3N0YXR1cyI6eyJzdGF0dXMiOnsic3RhdHVzX2xpc3QiOnsiaWR4IjoxMzM3LCJ1cmkiOiJodHRwczovL3Jldm9jYXRpb25fdXJsL3dpYS1zdGF0dXNsaXN0cy80MiJ9fSwiZXhwIjoxMzAzNDk3NzgwfSwiZXhwIjoxNzQwMTU4MTY3fQ.XOv01xkHC-_h-bZFcXgkEffs3xk8WezKWs8KA4yZD8aNabDq81Y80wkOuHCM25zSGHbABszaSDsyYaJv9bh40w
OAuth-Client-Attestation-PoP: eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWNsaWVudC1hdHRlc3RhdGlvbi1wb3Arand0In0.ew0KICAiaXNzIjogIiA0N2I5ODIzNjk3OTFkMDgwMDNhNzI4M2YwNTljYjBkMSIsDQogICJhdWQiOiAiaHR0cHM6Ly9hcy5leGFtcGxlLmNvbSIsDQogICJqdGkiOiAiZDI1ZDAwYWItNTUyYi00NmZjLWFlMTktOThmNDQwZjI1MDY0IiwNCiAgImlhdCI6IDE3NDAxNTg2MTcNCn0.B0KOkGi9vMxf3H2Y8rrF-mdLNsuluTvAUbjFfL1Hi-gdaPW7-8ziS9uVh7aTnSAHKWzMfkZLv5q-bxhkglR4PA
grant_type=refresh_token
&refresh_token=eyJ0eXAiOiJydCtqd3QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImM5NTBjMGU2ZmRlYjVkZTUwYTUwMDk2YjI0N2FmMDNjIn0.eyJpc3MiOiJodHRwczovL2VhYS1wcm92aWRlci53YWxsZXQuaXB6cy5pdCIsImNsaWVudF9pZCI6IjQ3Yjk4MjM2OTc5MWQwODAwM2E3MjgzZjA1OWNiMGQxIiwiYXVkIjoiaHR0cHM6Ly9lYWEtcHJvdmlkZXIud2FsbGV0LmlwenMuaXQiLCJpYXQiOjE3Mzk5NTI5NDgsIm5iZiI6MTczOTk1MzU0OCwiZXhwIjoxNzQyMzcyNzQ4LCJhdGgiOiJmVUh5TzJyMlozRFo1M0VzTnJXQmIweFdYb2FOeTU5SWlLQ0Fxa3NtUUVvIiwianRpIjoiYzY5NTVjZWItYzY1Zi00MDI1LTkzNzgtYjY2NzJiNjE0NWNmIiwiY25mIjp7ImprdCI6Ijk1MTU3NGFlZTFiYjc5MDdhZTFlYzMxMDlkYjJiMjI1In19.qiGM6E-7zci2-3Nnk4OMD7Tv_leUcRPsFsqaBHDHxEEzsGXLNh9qDbLIBk9sujZGVT9xs-28jZhwD6VT-MGTGw
Step 3: The Credential Issuer validates the request according to the following checks:
The Credential Issuer MUST validate the OAuth-Client-Attestation-PoP parameter based on Section 5 of [OAUTH-ATTESTATION-CLIENT-AUTH].
The Credential Issuer MUST validate the DPoP Proof JWT, according to (RFC 9449) Section 4.3.
The Credential Issuer MUST check that the Refresh Token is not expired, not revoked, and bound to the same set of DPoP keys as the ones used in the DPoP Proof JWT.
If the request checks are successful, the Credential Issuer generates a new Access Token and a new Refresh Token. The new Access Token and the new Refresh Token MUST both be bound to the DPoP key. Both the Access Token and the Refresh Token are then sent back to the Wallet Instance.
A non-normative example of a successful response is shown below.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJFU..",
"refresh_token": "eyC3fiLdCtqd3QiLCJhbGciOiCL3..",
"token_type": "DPoP",
"expires_in": 3600
}
If the Refresh Token is expired or invalid, the Credential Issuer MUST issue an error, using the error type member set to invalid_grant. Therefore, to obtain the Digital Credential an issuance flow authenticating the User is required, as defined in Section Low-Level Issuance Flow.
11.1.2.2.1. Security Considerations¶
To mitigate the risks of Refresh Token compromise, the following protections are required:
Confidentiality of Refresh Tokens MUST be guaranteed in transit and storage.
TLS-protected connections MUST be used for token transmission.
Refresh tokens MUST be unguessable and secure from modification.
Authorization Servers MUST implement the following mechanism to detect replay attacks:
Sender-Constrained Tokens: Crypto-graphically bind the Refresh Token to the Wallet Instance according to RFC 9449. Access Tokens and Refresh Tokens MUST be bound to the same DPoP key. The DPoP Proof of the refresh token is required to refresh an Access Token. The same DPoP key MUST be used to generate Access Token DPoP Proofs in all the Credential Requests.
Limiting the use of Refresh Token: As specified in OPENID4VC-HAIP, Credential Issuers should be mindful of how long the usage of the refresh token is allowed to refresh a Credential. The profile contrasts that duration with starting the issuance flow from the beginning. For example, if the User is trying to refresh a Credential more than a year after its original issuance, the usage of the refresh tokens is NOT RECOMMENDED. In this specification a new Digital Credential obtained performing the re-issuance flow SHOULD have the same expiration of the refreshed one. Thus, this specification does not allow for infinite refresh of Digital Credential with a Refresh Token. Once a Digital Credential expires, the User MUST complete the entire issuance process again, to obtain a new Digital Credential. This specification recommends to set a Refresh Token expiration duration, based on the sensitivity of the associated grant.
Note
Short-lived Wallet Instance Attestations and DPoP: Following the specification draft OAuth 2.0 Attestation Based Client Authentication (OAUTH-ATTESTATION-CLIENT-AUTH), the Authorization Server MUST bind the Refresh Token to the Client Instance. To prove this binding the Client Instance MUST use the Client Attestation mechanism when refreshing the Access Token and the Client Instance MUST use the same key that was presented in the cnf.jwk claim of the Client Attestation that was used when the Refresh Token was issued. However this requires that all issued Client Attestations MUST be bound to the same key, thus opening to unlinkability issues. In this specification, both OAUTH-ATTESTATION-CLIENT-AUTH and OAuth 2.0 Demonstrating Proof of Possession (DPoP) (RFC 9449) MUST be used. Using DPoP guarantees the binding of the Refresh Token with the Client Instance as stated in section 5 of RFC 9449 "the Refresh Token MUST be bound to the respective public key [...] a Client MUST present a DPoP proof for the same key that was used to obtain the Refresh Token each time that Refresh Token is used to obtain a new Access Token" (WP_068b). DPoP ensures that the Refresh Token is bound to the Wallet Instance.
11.1.2.3. Re-Issuance Flow¶
Re-issuance involves replacing Digital Credentials already stored in a Wallet Instance with new ones of the same document type. The new Digital Credentials MUST be issued by the same Credential Issuers that originally provided the existing ones to the same Wallet Instance.
To facilitate this, particularly in scenarios where User authentication is not strictly required, a Refresh Token (RT) flow MAY be used (see Section Refresh Token Flow for more details). An Access Token obtained as a result of a Refresh Token flow MUST NOT be used to issue a Digital Credential that is not present in the Wallet Instance (first-time-issuance). The Refresh Token mechanism enables automated Credential replacement, streamlining the process for both the Credential Issuer and the User.
The re-issuance process outlined in this section is limited to the following scenarios:
Data model or format technical update.
User's attribute set update.
This flow does not apply to device migration or backup restore. In those cases the Wallet Instance MUST perform a Wallet-Initiated Authorization Code Issuance Flow as defined in Section Low-Level Issuance Flow (see Section Restore flow for a Device-Bound Credential).
In the first case, the new Digital Credential's Users attribute set will match the original one. For example, a Credential Issuer may need to update the Digital Credential metadata or data format without changing the User's attribute set. In this case, the direct involvement of the User is not mandatory for the replacement and storage of a Digital Credential.
In the second case, Credential Issuers may also need to modify one or more User's attribute values during re-issuance. In this case, the Wallet Instance MUST inform the User that the attribute data set has been changed. The Wallet Instance MUST then request the User's authorization to store the new Digital Credential.
In both cases, the newly issued Digital Credential MUST have the same expiry date as the previous one.
Re-issuance after Digital Credential expiration MUST always require User authentication.
The following diagram describes the Digital Credential re-issuance flow.
Fig. 11.7 Re-Issuance Flow Diagram.¶
Step 1: The flow starts when the User opens the Wallet Instance. This step MAY be triggered by a notification sent by the Credential Issuer, using for example one of the out-of-band communication contacts registered during the Issuance flow.
Step 2: The Wallet Instance MUST check the status of any stored Digital Credential by retrieving a valid Status List Token if one is not already available. The Wallet Instance follows the profile in Token Status List (Digital Credentials Profile) and the checking procedure in Checking Credentials Statuses, as covered by WP_069.
Then, the Wallet Instance MUST check (WP_070) if any Digital Credential has status set to 0x03 - UPDATE or 0x0F - ATTRIBUTE_UPDATE.
If the conditions above are not met, the flow MUST be interrupted.
Otherwise, the Wallet Instance MUST check the related Access Tokens. If they are still valid, then Step 3 MAY be skipped (WP_071).
Step 3: If the Access Token is expired and the Wallet Instance still has a valid Refresh Token, the Wallet Instance MUST obtain a new Access Token by starting a Refresh Token Flow. The Wallet Instance follows Section Refresh Token Flow (WP_071a). The Refresh Token Flow enables the Wallet Instance to obtain a new Refresh Token and a new DPoP Access Token to refresh the Digital Credential. If the Refresh Token is expired, a new Issuance Flow authenticating the User is required (WP_067 and WP_071b).
Step 4: The Wallet Instance MUST use a valid DPoP Access Token to retrieve the new Digital Credential from the Credential endpoint. The Wallet Instance follows the steps from 12 to 22 of Figure 9 in Section Low-Level Issuance Flow (WP_072). When the new Digital Credential is successfully stored in the secure storage, the Wallet Instance MUST delete the previous one (WP_073).
Note
Regardless of the Digital Credential revocation mechanism supported, if the Digital Credential status is set to ATTRIBUTE_UPDATE, the User's attribute set, in the refreshed Digital Credential, doesn't match the one in the stored Digital Credential. In this case, the Wallet Instance MUST request the User's authorization to store the new refreshed Digital Credential (WP_074).
If instead, either the Digital Credential status is set to UPDATE, only the Credential metadata parameters have changed. In this case, the Wallet Instance SHOULD store the new Digital Credential without requiring explicit user authorization and consent (WP_075).
11.1.2.3.1. Re-Issuance Flow: Security Considerations¶
To ensure the integrity and security of the re-issuance process, the following security considerations apply.
Access Token limitations: An Access Token obtained as a result of a Refresh Token flow MUST NOT be used for the first-time issuance of a Digital Credential. This ensures that only existing Credentials in the Wallet Instance are updated.
Credential expiry: The Credential Issuer MUST set the same expiry date for the re-issued Digital Credential as the previous one. This prevents indefinite Credential renewals without proper User authentication.
User consent: For re-issuance processes triggered by attribute changes, User consent MUST be obtained before storing the new Digital Credential. This ensures that the User is aware of and agrees to the updated information.
Sender-constrained Refresh Token: Refresh Tokens MUST be cryptographically bound to the Wallet Instance using DPoP protocol. This mitigates the risk of token misuse by ensuring that only the intended Wallet Instance (the same that originally has obtained the Digital Credential) can use that Refresh Token.
Note
During each Credential issuance and, where supported, each re-issuance/refresh transaction executed through the flows described in this section, the Wallet Instance MUST create and maintain a corresponding transaction record in the Wallet Instance transaction log (see Wallet Instance Dashboard and Transaction Logging).
The Wallet Instance MUST create a transaction record for each Credential issuance process. The record MUST capture relevant contextual information necessary to ensure traceability and accountability of the transaction.
The record MUST be updated as the flow progresses to reflect the evolving transaction state and outcome context (e.g., the issued Credential type(s) and quantity). In cases where the transaction does not complete successfully, the record MUST indicate the corresponding reason for non-completion.
11.1.2.4. Credential Offer Flow¶
A Credential Issuer or third party (e.g., Authentic Source, Registry, Catalogue) initiates Credential Issuance by sending a Credential Offer to the Wallet Instance.
To invoke the correct Wallet Instance, they need to know which Wallet Instance is installed on the User's device and the User wishes to use. This information SHOULD be obtained using the Selection Page described in User Experience Design.
If the Selection Page is supported, the User selects the Wallet, and then the Credential Issuer or third party retrieves the Wallet metadata as described in Wallet Metadata Retrieval Flow. The Wallet Instance invocation mechanism depends on the
credential_offer_endpointparameter in the Wallet metadata:If
credential_offer_endpointis available and contains an HTTPS URL (Universal Link), the Credential Issuer or third party SHOULD use that endpoint.Otherwise, on the EUDIW path, the Credential Issuer or third party MUST use
eu-eaa-offer://.The Wallet Instance MUST support
eu-eaa-offer://([ETSI TS 119 472-3] Section 4.1).The Wallet Instance MUST support
openid-credential-offer://.openid-credential-offer://is defined in Section 4 of [OpenID4VCI].The Wallet Instance MUST support
haip-vci://.haip-vci://is defined in Section 4.2 of [OPENID4VC-HAIP].On the National path, when
credential_offer_endpointis absent or is not an HTTPS URL, the Credential Issuer or third party MAY retain its national Universal Link or other retained national invocation mechanism.Without the Selection Page, or when metadata retrieval fails, the Credential Issuer or third party MUST invoke the Wallet Instance with the scheme required above for that path.
The Credential Offer can be transmitted by value or by reference:
By value (
credential_offerparameter): the Credential Offer object is directly embedded in the URI as a JSON-encoded string.By reference (
credential_offer_uriparameter): the URI points to a resource hosted by the Credential Issuer or third party. The Wallet Instance retrieves the Credential Offer object by sending an HTTP GET request to that URI.
The Credential Offer object is a JSON object containing the parameters defined in Section 4.1.1 of [OpenID4VCI]. The table below specifies the parameters with IT-Wallet specific requirements:
Claim |
Description |
Reference |
|---|---|---|
credential_issuer |
It MUST be set with an HTTPS URL that uniquely identifies the Credential Issuer. The Wallet uses this parameter value to obtain the Credential Issuer's metadata. |
Section 4.1.1 of [OpenID4VCI]. |
credential_configuration_ids |
Array of Strings, each of them specifying a unique identifier of the Credential being described in the |
Section 4.1.1 of [OpenID4VCI]. |
grants |
REQUIRED. It MUST contain
|
Section 4.1.1 of [OpenID4VCI] and Section 4.1 of [OPENID4VC-HAIP]. |
Note
When using credential_offer_uri (by reference), the Credential Issuer or third party SHOULD use a unique URI for each Credential Offer or otherwise prevent caching of the URI, as recommended in Section 4.1.3 of [OpenID4VCI].
11.1.2.4.1. Non-normative Examples¶
The following example is a Credential Offer passed by value.
The Credential Offer can be transmitted by value using any supported invocation method (custom URL scheme or Universal Link):
eu-eaa-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A//credential-issuer.example.org%22%2C%22credential_configuration_ids%22%3A%5B%22dc_sd_jwt_Education_degree%22%5D%2C%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22eyJhbGciOiJSU0Et...F77QK8%22%7D%7D%7D
The decoded Credential Offer object:
{
"credential_issuer": "https://credential-issuer.example.org",
"credential_configuration_ids": ["dc_sd_jwt_Education_degree"],
"grants": {
"authorization_code": {
"issuer_state": "eyJhbGciOiJSU0Et...F77QK8",
}
}
}
The following example is a Credential Offer passed by reference from the Credential Issuer.
The QR Code or the href button contains:
eu-eaa-offer://?credential_offer_uri=https%3A%2F%2Fcredential-issuer.example.org%2Foffers%2F8f3a2b1c
The Wallet Instance sends an HTTP GET request:
GET /offers/8f3a2b1c HTTP/1.1
Host: credential-issuer.example.org
Accept: application/json
The Credential Issuer responds:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"credential_issuer": "https://credential-issuer.example.org",
"credential_configuration_ids": ["dc_sd_jwt_EuropeanDisabilityCard"],
"grants": {
"authorization_code": {
"issuer_state": "eyJhbGciOiJSU0Et...F77QK8",
}
}
}
The following example is a Credential Offer passed by reference from a third party.
A third party, such as an Authentic Source, generates a Credential Offer for a Credential Issuer.
The QR Code contains:
eu-eaa-offer://?credential_offer_uri=https%3A%2F%2Fauthentic-source.gov.example%2Fcredential-offers%2Fabc123
The Wallet Instance sends:
GET /credential-offers/abc123 HTTP/1.1
Host: authentic-source.gov.example
Accept: application/json
The Authentic Source responds:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"credential_issuer": "https://credential-issuer.example.org",
"credential_configuration_ids": ["mso_mdoc_mDL"],
"grants": {
"authorization_code": {
"issuer_state": "eyJhbGciOiJSU0Et...F77QK8",
}
}
}