OID4VP Presentation Protocol
You do not need this page to integrate
Everything described here is carried out by the credential verification service on your behalf. It is step 5 of the verification flow — the part you do not implement. Read it if you want the wire-level detail, are debugging a wallet interaction, or are assessing interoperability.
For the calls your own application makes, see Request a presentation.
This is a detailed account of the OID4VP exchange between the wallet and the credential verification service, from the moment the wallet has the request URI to the moment verification completes.
It is also where the verifier's side of the trust model becomes concrete: a wallet that establishes who this verifier is does it by validating the certificate chain in the signed request object below — though whether it does so at all is the wallet's choice.
Wallet retrieves the request object
At this point in the flow, the wallet has received the request URI from your application.
After the holder approves the interaction, the wallet retrieves the request object from the credential verification service. This is done through a secure API call, signaling the credential verification service to proceed with the presentation process.
For example, the wallet might send the following:
curl -X 'GET' \
'https://verifier.example.org/presentation/oid4vp/authorization-request/02a61fbd1ac7c23f90a48a6b9107a45bbd9b05b09b' \
-H 'accept: application/oauth-authz-req+jwt'
Credential verification service returns a signed request object as a JWT
The credential verification service responds to the wallet's request by sending the following key components in the payload of a signed JWT:
- DCQL Query: A object specifying the rules and requirements for the verifiable presentation.
- Response URI: The endpoint to which the wallet must later send the completed presentation.
- Client metadata: Information about the verifier, for the wallet to present to the user.
- Nonce: A unique identifier to maintain the integrity of the interaction.
- Client ID: An identifier for validating the verifier's identity and ensuring the request's authenticity.
For example, the payload of the signed JWT response might look like this:
{
"dcql_query": {
"credentials": [
{
"id": "example_credential",
"format": "jwt_vc_json-ld",
"meta": {},
"claims": [
{
"id": "firstName",
"path": ["credentialSubject", "firstName"]
}
]
}
]
},
"response_uri": "https://verifier.example.org/presentation/oid4vp/authorization-response/02c025eb679059d80cf88a7adcf1d357185a19e161",
"client_metadata": {
"client_name": "Partisia Verifier",
"logo_uri": "https://verifier.example.org/static/logo"
},
"nonce": "FOuK4_lFesM6-rBkoHkrng==",
"client_id": "x509_hash:VA7rWJULU4O_gDap9PCzgpjMyo8-R3dz7zaU-MRkaBs"
}
A wallet that establishes who the verifier is performs the following steps. Doing so is optional for the wallet, and a wallet that skips it proceeds straight to the holder's consent:
- Validates the certificate chain in the x5c header of the JWT with its own set of trusted root certificates.
- Validates the JWT signature using the public key from the x5c header.
- Checks that the hash of the signing certificate matches the client ID
The wallet then extracts the payload fields and uses the DCQL query to guide the holder in selecting and preparing the required credentials for presentation.
User Confirmation in the Wallet
The wallet prompts the user to confirm their intent to proceed. The user must swipe to confirm or approve the preparation and submission of the verifiable presentation.
Wallet submits the Verifiable Presentation
Once confirmed, the wallet prepares and submits the verifiable presentation to the credential verification
service, as a form-encoded POST to the response_uri.
Which of two endpoints it posts to follows from the response_mode in the request object:
response_mode |
Endpoint | Form field |
|---|---|---|
direct_post |
/presentation/oid4vp/authorization-response/{session_id} |
vp_token |
direct_post.jwt |
/presentation/oid4vp/encrypted-authorization-response/{session_id} |
response |
Both also accept an error field, which is how a wallet reports that the holder declined.
The response mode is derived, not configured. A verification query asking for an mso_mdoc
credential gets direct_post.jwt; anything else gets direct_post. mdoc device authentication is bound to the
encryption key of the response, so the encryption is part of the format rather than a choice — there is nothing to
set, and the wallet follows what the signed request tells it.
vp_token, for a plain response
The form field holds a JSON object keyed by the id of each credential query in your DCQL query, with the
presentations for it as an array:
{
"studentCard": [
"eyJhbGciOiJFUzI1NiIsInR5cCI6ImRjK3NkLWp3dCJ9…~WyJ4cjRnako0N3FUeXJNbDBnaVNKa1dnIiwiZmlyc3RfbmFtZSIsIkplbnMiXQ~"
]
}
Those keys are the ids you chose when writing the query, which is what ties each presentation back to the credential it answers. Each value is an SD-JWT with its selected disclosures appended, carrying both the issuer's signature over the credential and the wallet's signature over the presentation.
response, for an encrypted response
For direct_post.jwt the same object arrives as a JWE instead, encrypted to the verifier. The service decrypts it
and proceeds identically from there.
Credential verification service runs its verification
Upon receiving the Verifiable Presentation, the credential verification service processes the submission. It performs the following checks:
- Signature Verification: Ensures the presentation is signed by the wallet and the credentials by the respective issuers.
- Credential Validation: Confirms that the submitted credentials meet the rules specified in the DCQL query.
- Integrity Check: Validates the nonce and session ID to ensure no tampering occurred.
The credential verification service then returns the verification result to the customer application, completing the presentation flow.