Authors:
Reviewers:
Status: Approved
Table of Contents
This specification implements the OIDC4VP workflow for any verifier (relying party) as per reference specification [1]. This minimises risks towards interoperability across the European Wallet Ecosystem with a standard specification in the EUDI wallet ecosystem as per the ARF [2] requirements.
The EWC LSP must align with the standard protocol for issuing credentials. This is the basis of interoperability between Verifiers (Relying Parties) and Holders across the EWC LSPs. The assumption is that the user is familiar with the EWC-chosen protocols and standards and can refer to original standards references when necessary.
As shown in Figure 1 below, a individual using the wallet presents a credential to a verifier on the same device that the device the wallet resides on.
sequenceDiagram
participant I as Individual using EUDI Wallet
participant V as Verifier
V->>+I: GET: Authorization Request (e.g., via redirect)
Note over I,V: User Authentication / Consent
I->>+V: Authorization Response
Figure 1: Same Device Verification Flow (Conceptual, based on [1])
For cross device verification, or when the response size is large, the direct_post
response mode combined with passing the request by reference (request_uri
) is common, as shown in Figure 2.
sequenceDiagram
participant User as User Device (Wallet)
participant VerifierFE as Verifier Frontend (e.g., Browser)
participant VerifierBE as Verifier Backend / Response Endpoint
VerifierFE->>User: Displays Authorization Request (e.g., QR Code containing `request_uri`)
User->>+VerifierBE: GET: Request object, resolved from the `request_uri`
VerifierBE-->>-User: Authorization Request Object (JWT)
Note over User,VerifierBE: User Authentication / Consent
User->>+VerifierBE: POST: Authorization Response (`vp_token`, `presentation_submission`, `state` to `response_uri`)
VerifierBE-->>-User: HTTP 200 OK (Optionally with `redirect_uri`)
Note over User,VerifierFE: If `redirect_uri` is present, Wallet redirects User Agent
User->>VerifierFE: GET: `redirect_uri` (if provided)
Figure 2: Cross Device / Direct Post Verification Flow (Conceptual, based on [1])
Alternatively, The digital credentials api can be used to request a presentation. The underlying platform (i.e. browser, android, ios) will take care of performing this securely in same device and cross device scenarios.
The authorisation request is based on OAuth 2.0 [RFC6749] and includes the following parameters relevant to OIDC4VP [1]:
Parameter | Description |
---|---|
client_id |
REQUIRED. Verifier identifier (e.g., URI / DID). Interpreted according to the Client Identifier Scheme. This value MUST be used by the Wallet as the audience for the Verifiable Presentation and verified by the Verifier. Must be present in the aud claim) of the Verifiable Presentation JWT/structure. |
client_metadata |
OPTIONAL. A JSON object containing Verifier metadata values, passed by value. Used primarily when metadata cannot be obtained via the client_id . Includes parameters like vp_formats (Verifier supported formats) e.t.c. |
nonce |
REQUIRED. String value used to associate a Client session with the VP Token and mitigate replay attacks. It MUST be included in the Verifiable Presentation (e.g., nonce claim in JWT VPs). Values MUST only contain ASCII URL safe characters. |
presentation_definition |
CONDITIONAL. A JSON object conforming to the DIF Presentation Exchange specification [4], describing the required credentials. |
presentation_definition_uri |
CONDITIONAL. An HTTPS URL referencing a resource containing the presentation_definition JSON object. |
dcql_query |
CONDITIONAL. A JSON-encoded object representing a query using the Digital Credentials Query Language (DCQL). See OIDC4VP [1] Spec Section 6 [1]. |
scope |
CONDITIONAL. OAuth 2.0 scope parameter. Can be used as an alias for a pre-defined credential request. If openid is included, it becomes an OpenID Connect request potentially combined with SIOPv2. |
response_type |
REQUIRED. Must include vp_token . May be combined with id_token (for SIOPv2) or code (for Auth Code flow, where VP Token is returned at Token Endpoint). EWC mandates only vp_token . |
response_mode |
OPTIONAL. Specifies how the Authorization Response is returned. OIDC4VP defines direct_post and supports JARM modes like direct_post.jwt . The default for response_type=vp_token is fragment (not recommended by EWC). EWC RFC002 context only supports direct_post , direct_post.jwt , dc_api or dc_api.jwt . |
response_uri |
REQUIRED when response_mode is direct_post or direct_post.jwt . The URL where the Wallet sends the Authorization Response via HTTP POST. MUST NOT be present if redirect_uri is used. |
redirect_uri |
REQUIRED for non-direct_post flows like fragment or when using Authorization Code flow (response_type=code ). MUST NOT be present if response_uri is used. EWC context deprecates its use. |
state |
RECOMMENDED. Opaque value used by the Verifier to maintain state between the request and response. The Wallet MUST return this value unmodified. Values MUST only contain ASCII URL safe characters. |
request |
OPTIONAL. The request parameters packaged as a Request Object (JWT). See [RFC9101]. |
request_uri |
OPTIONAL. An HTTPS URL referencing a Request Object (JWT). See [RFC9101]. Preferred for QR codes. |
request_uri_method |
OPTIONAL. HTTP method (get or post ) to use when fetching the Request Object from request_uri . Default is get . post allows the Wallet to send its capabilities. MUST NOT be present if request_uri is absent. |
transaction_data |
OPTIONAL. Array of strings, each a base64url encoded JSON object detailing a transaction the user is authorizing (e.g., payment, signature). Binds the VP to a specific transaction. |
expected_origins |
REQUIRED for signed requests when using the DC API (see Appendix A [1]). Array of strings representing expected Verifier origins. |
OIDC4VP Constraint: Exactly one of
presentation_definition
,presentation_definition_uri
,dcql_query
, or ascope
value representing a presentation definition MUST be present in the Authorization Request.
OIDC4VP [1] defines a mechanism to specify how the client_id
should be interpreted and validated, enabling different trust models beyond simple pre-registration.
client_id=<scheme>:<scheme-specific-identifier>
:
character. If present, interpret the prefix as the scheme.:
is present, the client_id
refers to a pre-registered client [RFC6749].client_id
(including the scheme prefix) when validating the audience (aud
) of the VP Token.redirect_uri
: client_id
is the Verifier’s redirect_uri
. Requires client_metadata
. MUST NOT be signed.did
: client_id
is a DID. Request MUST be signed using a key from the DID Document. Metadata often via client_metadata
.verifier_attestation
: Request MUST be signed by a key confirmed in a Verifier Attestation JWT provided in the jwt
JOSE header. See Section 11 [1]. Metadata often via client_metadata
.x509_san_dns
: client_id
is a DNS name matching an X.509 certificate’s Subject Alternative Name (SAN). Request MUST be signed using the certificate’s private key (x5c
header). Metadata often via client_metadata
.x509_san_uri
: client_id
is a URI matching an X.509 certificate’s SAN. Request MUST be signed (x5c
header). Metadata often via client_metadata
.web-origin
: Used only with the Digital Credentials API. The client_id
is constructed by the Wallet as web-origin:<origin>
, where <origin>
is asserted by the underlying platform. See Appendix A [1].[!NOTE] EWC RFC002 does not explicitly mandate a specific scheme, but Verifiers and Wallets within the EWC ecosystem need to agree on supported schemes for interoperability.
https
,did
, andx509_san_*
schemes often imply a higher level of Verifier trust establishment.
Authorisation requests can be presented to the wallet by the Verifier:
By value: All request parameters are URL-encoded directly into the invocation URI.
request
: The Request Object JWT itself.request_uri
: A URL pointing to the Request Object JWT.[!NOTE] The EWC RFC002 example uses a custom URL scheme
openid4vp://
. Wallet invocation can also use Universal Links / App Links or manual QR code scanning without a specific scheme [1].
Authorization request parameters are included directly in the authorization request URI. Suitable for simple requests.
openid4vp://?client_id=https://example.verifier.com&response_type=vp_token&response_uri=...&response_mode=direct_post&nonce=...&presentation_definition=...&state=...
Passing by reference using request_uri
(the default get
method). The Wallet performs an HTTP GET to the request_uri
to retrieve the Request Object JWT. Recommended for QR codes to keep them small.
openid4vp://?request_uri=https://server.example.com/presentation-request/tRf739_poiU
The Verifier hosting the request_uri
endpoint MUST expose it via HTTPS without requiring authentication. The resolved Request Object JWT contains the actual authorization parameters (e.g., client_id
, nonce
, presentation_definition
).
Passing by reference using request_uri
and request_uri_method=post
[1].
POST /authorize?
client_id=x509_san_dns:client.example.org
&client_metadata=...
&request_uri=https://server.example.com/presentation-request/tRf739_poiU
&request_uri_method=post HTTP/1.1
request_uri
endpoint.application/x-www-form-urlencoded
and MAY contain:
wallet_metadata
: JSON string with Wallet capabilities (e.g., supported formats vp_formats_supported
, signing algorithms request_object_signing_alg_values_supported
).wallet_nonce
: A nonce generated by the Wallet for replay protection of the Verifier’s response.Content-Type: application/oauth-authz-req+jwt
.wallet_nonce
, the Verifier MUST include it as a claim in the Request Object JWT.This method allows the Wallet to inform the Verifier about its capabilities before the Verifier generates the final Request Object, enabling a more tailored request.
[!NOTE] The Digital Credentials APIs are outside the scope of the EWC LSP, but are included here to support early testing and piloting efforts.
When using the Digital Credentials API (DC API) [1, Appendix A], the request parameters are passed as a JSON object to the API call.
openid4vp
.response_mode
MUST be dc_api
(unsigned response) or dc_api.jwt
(signed/encrypted JARM response).client_id
parameter MUST be omitted in unsigned requests; the Wallet derives it using the web-origin
scheme from the origin asserted by the platform.request
parameter), the client_id
(e.g., a DID or HTTPS entity ID) MUST be included inside the signed Request Object JWT. The expected_origins
parameter MUST also be included outside the JWT.// Request credentials
const credentialResponse = await navigator.credentials.get({
digital: {
protocol: "openid4vp",
request: [{
response_type: "vp_token",
response_mode: "dc_api",
nonce: "hcXlreRWiJIyjv3VPp7z5ARuwbhArLFJnYje4XZs4lg",
dcql_query: {
credentials: [{
id: "pid",
format: "dc+sd-jwt",
meta: {
vct_values: ["urn:eu.europa.ec.eudi:pid:1"]
},
claims: [{
path: ["family_name"]
},
{
path: ["given_name"]
}
]
}]
}
}]
}
});
// Process the response
const data = credentialResponse.data;
// Send to verifier...
As defined in OIDC4VP [1], the scope
parameter MAY be used as an alternative to presentation_definition
or presentation_definition_uri
.
definition_id
, descriptor_map.id
) in the presentation_submission
response and the expected credential formats/types in the vp_token
.Example usage:
openid4vp://?client_id=https://example.verifier.com
&response_type=vp_token
&scope=com.example.passport_credential_presentation openid // Also requests SIOPv2 ID Token
&response_uri=https://example.verifier.com/direct_post
&response_mode=direct_post
&state=...
&nonce=...
OIDC4VP provides multiple ways to specify the credentials the Verifier wants the user to present. Exactly one method must be used.
presentation_definition
The request directly includes the Presentation Definition JSON object [4] as a request parameter.
...&presentation_definition={"id": "pd_1", "input_descriptors": [...]}...
presentation_definition_uri
The request provides an HTTPS URL. The Wallet MUST perform an HTTP GET request to this URL to retrieve the Presentation Definition JSON object. The resource MUST be accessible without authentication [1].
...&presentation_definition_uri=https://verifier.example.com/defs/pd_1...
dcql_query
The request includes a JSON-encoded object representing a query using the Digital Credentials Query Language (DCQL) [1]. This offers a potentially more flexible way to specify credential requirements compared to Presentation Exchange.
...&dcql_query={"credentials": [{"id": "cred1", "format": "vc+sd-jwt", "claims": [...]}]}...
scope
See Chapter 3.1.4.
OIDC4VP allows binding the presentation request to a specific transaction using the transaction_data
parameter [1]. This is crucial for high-assurance use cases like payments or document signing.
transaction_data
: Array of strings.type
: REQUIRED. String identifying the transaction type (e.g., payment_auth
, document_signature
). Values are profile-specific.credential_ids
: REQUIRED. Array of strings referencing the id
of the Credential(s) requested (in presentation_definition
or dcql_query
) that can authorize this transaction.transaction_data_hashes_alg
: OPTIONAL. Array of supported hash algorithm identifiers (from IANA “Named Information Hash Algorithm” registry) for hashing the transaction data. Default is sha-256
. Wallet MUST support sha-256
.transaction_data
objects in the response....&transaction_data=["eyJ0eXBlIjogInBheW1lbnRfYXV0aCIsICJhbW91bnQiOiAiMTAuMDAiLCAiY3VycmVuY3kiOiAiRVVSIiwgImNyZWRlbnRpYWxfaWRzIjogWyJpZF9jcmVkIl0sICJ0cmFuc2FjdGlvbl9kYXRhX2hhc2hlc19hbGciOiBbInNoYS0yNTYiXX0="]...
Depending on the request and response mode, the response includes:
Parameter | Description |
---|---|
vp_token |
REQUIRED if requested. Contains the Verifiable Presentation(s).<ul><li>If Presentation Exchange was used: A single VP (JSON string or object) or an array of VPs (JSON strings/objects). Encoding depends on format (Appendix B [1]). MUST NOT use array for single VP.</li><li>If DCQL was used: A JSON object where keys are the Credential Query id s and values are the corresponding VPs (JSON strings/objects).</li></ul> |
presentation_submission |
REQUIRED if Presentation Exchange was used in the request. JSON object conforming to DIF Presentation Exchange [4]. Maps requested credentials (input descriptors) to the returned VPs in vp_token . Uses path ($ for single VP, $[n] for n-th VP in array) and potentially path_nested . MUST be a separate parameter alongside vp_token . |
state |
REQUIRED if present in the request. MUST be the unmodified value from the request. |
id_token |
Included if openid scope and id_token response type were requested (SIOPv2 flow). |
code |
Included if code response type was requested (Authorization Code flow). vp_token is returned later at the Token Endpoint. |
iss |
RECOMMENDED for Self-Issued ID Tokens (SIOPv2). Contains the End-User’s DID or other identifier, indicating the Authorization Server is the Wallet itself [RFC9207]. |
transaction_data_hashes |
REQUIRED if transaction_data was present in the request. Included within the proof mechanism of the credential used for authorization (format-specific). Contains hashes of the transaction_data objects. |
transaction_data_hashes_alg |
REQUIRED if transaction_data was present in the request. Included alongside transaction_data_hashes . The hash algorithm used. |
error |
REQUIRED on error. |
error_description |
OPTIONAL on error. Human-readable explanation. |
error_uri |
OPTIONAL on error. URI for human-readable web page with error info. |
The response_mode
parameter dictates how the response parameters are sent back to the Verifier.
[!NOTE] EWC RFC002 context only supports
direct_post
,direct_post.jwt
,dc_api
ordc_api.jwt
. The base OIDC4VP specification’s default forresponse_type=vp_token
isfragment
, which is not used here.
direct_post
)[1] The Wallet sends the response parameters via an HTTP POST request to the response_uri
specified by the Verifier.
Content-Type
: application/x-www-form-urlencoded
.vp_token
, presentation_submission
, state
).response_uri
processes this POST.redirect_uri
parameter.
{"redirect_uri": "https://verifier.example.com/ui/callback?response_code=..."}
redirect_uri
MUST contain a fresh, single-use cryptographic random value (e.g., response_code
).redirect_uri
, the flow ends at the Wallet after the POST, which might be desired in some API-based scenarios but lacks session fixation protection.Example Wallet POST:
POST https://example.verifier.com/direct_post
Content-Type: application/x-www-form-urlencoded
vp_token=eyJraWQiOiJk...Z-1_msCBcxh7XEA
&presentation_submission={...}
&state=475e634e-2633-4235-953d-eb879334cae7
Example Verifier Response to Wallet POST:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"redirect_uri": "https://verifier.example.com/ui/callback?response_code=091535f699ea575c7937fa5f0f454aee"
}
direct_post.jwt
)[1] This mode works like direct_post
, but the authorization response parameters are packaged inside a JWT (Response Object) according to JARM [JARM].
response_uri
.Content-Type
: application/x-www-form-urlencoded
.response=<Response Object JWT>
.vp_token
, presentation_submission
, state
, etc.).redirect_uri
as described for direct_post
.dc_api
, dc_api.jwt
)[!NOTE] The Digital Credentials APIs are outside the scope of the EWC LSP, but are included here to support early testing and piloting efforts.
[1, Appendix A] When the request uses response_mode=dc_api
or dc_api.jwt
, the response is returned via the platform’s DC API mechanism.
DigitalCredential
interface.data
attribute of this interface contains the OIDC4VP response parameters as a JSON object.dc_api.jwt
was used, the data
object will contain the response
parameter holding the JARM JWT, otherwise it contains the parameters directly (e.g., vp_token
, presentation_submission
).OIDC4VP uses JWT Secured Authorization Response Mode (JARM) [JARM] to sign and/or encrypt responses [1].
response_mode
values like direct_post.jwt
, dc_api.jwt
.jwks
in client_metadata
.If the Authorization Request included transaction_data
, the Wallet MUST include corresponding hashes in the response [1].
transaction_data_hashes
: REQUIRED. An array of hashes. Each hash corresponds to one of the base64url-encoded JSON objects received in the transaction_data
request parameter. Hashing ensures integrity.transaction_data_hashes_alg
: REQUIRED. String identifying the hash algorithm used (must be one proposed by the Verifier, or the default sha-256
).credential_ids
in the request’s transaction_data
). The exact method is format-specific (see Appendix B [1]). For JWT-based VCs/VPs, these would typically be claims within the VP JWT, signed by the Holder.The Verifier MUST validate the received VP Token and its contents thoroughly [1]:
vp_token
structure matches the expected format based on the query language used (PE vs. DCQL).presentation_submission
to identify which VPs correspond to which input descriptors.vp_token
according to its specific format’s rules (e.g., JWS signature, ZKP proof).nonce
value received in the request matches the nonce
(or challenge
) bound within each Verifiable Presentation’s proof.client_id
(used in the request) matches the audience (aud
or domain
) bound within each Verifiable Presentation’s proof.presentation_definition
or dcql_query
.transaction_data
was used, verify the presence and correctness of transaction_data_hashes
within the authorizing credential’s proof.Error responses follow OAuth 2.0 [RFC6749] and JARM [JARM] conventions. OIDC4VP defines additional error codes [1]:
Error Code | Description (from OIDC4VP Draft 23) |
---|---|
invalid_request |
Standard OAuth 2.0 error. OIDC4VP specifics: Request includes more than one credential request method (e.g., both presentation_definition and dcql_query ); vp_token response type used without any credential request method; PE definition invalid; unsupported Client ID scheme used; Client ID scheme requirements violated (e.g., unsigned request for did scheme). |
invalid_client |
Standard OAuth 2.0 error. OIDC4VP specifics: client_metadata provided but Wallet already has metadata for the recognized client_id ; Verifier’s pre-registered metadata found but client_metadata also present. |
invalid_scope |
Standard OAuth 2.0 error. Requested scope value is invalid, unknown, or malformed. |
access_denied |
Standard OAuth 2.0 error. Wallet doesn’t have matching credentials; User denied consent; User authentication failed. |
vp_formats_not_supported |
Wallet does not support any of the VC/VP formats requested by the Verifier (e.g., in vp_formats metadata or format in Presentation Definition). |
invalid_presentation_definition_uri |
The Presentation Definition referenced by presentation_definition_uri could not be fetched (network error, invalid URL). |
invalid_presentation_definition_reference |
The resource at presentation_definition_uri was fetched, but it did not contain a valid Presentation Definition object or the expected one. |
invalid_request_uri_method |
The value of the request_uri_method parameter is neither get nor post . |
invalid_transaction_data |
Error related to the transaction_data parameter: unrecognized type, missing fields, invalid values, referenced credential_ids don’t match request or aren’t available. |
wallet_unavailable |
Used in specific scenarios (e.g., claimed HTTPS URI invocation) where the user agent cannot invoke the Wallet, but another component (like the Wallet provider backend) handles the request to inform the Verifier. |
Example error response using direct_post
:
POST /post HTTP/1.1
Host: client.example.org
Content-Type: application/x-www-form-urlencoded
error=invalid_request&
error_description=unsupported%20client_id_scheme&
state=eyJhb...6-sVA
Discovering capabilities of Wallets and Verifiers is crucial for interoperability.
Verifiers can discover Wallet capabilities using OAuth 2.0 Authorization Server Metadata [RFC8414]. OIDC4VP defines additional metadata parameters [1]:
Metadata Parameter | Description |
---|---|
presentation_definition_uri_supported |
OPTIONAL. Boolean indicating if the Wallet supports fetching Presentation Definitions via presentation_definition_uri . Default true . |
vp_formats_supported |
REQUIRED. JSON object listing supported credential formats (keys are format identifiers like jwt_vc_json , mso_mdoc , vc+sd-jwt , see Appendix B [1]). Values are objects that MAY contain alg_values_supported and format-specific capabilities. |
client_id_schemes_supported |
OPTIONAL. Array of strings listing the Client ID schemes the Wallet supports (e.g., redirect_uri , did , https ). Default is pre-registration only. |
Metadata can be obtained dynamically (e.g., via .well-known/oauth-authorization-server
) or statically (pre-configured).
Wallets can obtain Verifier capabilities using OAuth 2.0 Dynamic Client Registration Metadata [RFC7591]. OIDC4VP defines one key parameter:
Metadata Parameter | Description |
---|---|
vp_formats |
REQUIRED. JSON object defining the VP and VC formats the Verifier supports. Structure is the same as vp_formats_supported in Wallet metadata, specifying algorithms, proof types etc. |
Verifier metadata can be passed dynamically using the client_metadata
request parameter, obtained via EWC Trust List, fetched using Dynamic Client Registration, or statically configured in the Wallet. Data obtained from trusted sources (like EWC Trust List) takes precedence over client_metadata
.
OIDC4VP is format-agnostic, but specific formats require particular handling, especially within presentation_submission
and metadata. See Appendix B [1] for details on:
path_nested
: Used in presentation_submission.descriptor_map
to specify the path within a VP where the actual VC can be found, along with the VC’s format.jwt_vc_json
, jwt_vp_json
format identifiers. Specifies algorithm parameters.mso_mdoc
format. DCQL uses namespace
and claim_name
. vp_token
contains base64url-encoded DeviceResponse
. presentation_submission
is often not used as data is self-contained.vc+sd-jwt
format. Metadata includes sd-jwt_alg_values
and kb-jwt_alg_values
. vp_token
contains the combined SD-JWT VC and KB-JWT.Please refer to the implementers table.
Refer RFC012 for key resolution and trust mechanims.
For a JWT (e.g., Request Object, VP Token, ID Token), there are multiple ways for resolving the public key needed for signature verification:
jwk
Header: The public key JWK is directly embedded in the JOSE header.kid
Header: The header contains a Key ID. The key needs to be retrieved:
kid
is a DID URL (e.g., did:example:123#key-1
), use DID resolution to obtain the verification method (public key) from the DID Document.iss
claim), fetch the jwks_uri
from the metadata and find the key matching the kid
in the JWK Set.x5c
Header: The header contains the X.509 certificate chain containing the public key. Validate the chain according to PKI rules.