Authorization Request & Response
The OID4VP protocol exchanges credential presentation requests and responses between verifiers and wallets. Understanding the structure of these messages is essential for implementing secure credential verification.
Authorization Request
The verifier initiates the protocol by sending an authorization request to the wallet. This can be delivered inline or by reference using a request_uri.
Request Parameters
| Property | Type | Required | Description |
|---|---|---|---|
| Required | Must be "vp_token" for OID4VP | ||
| Required | Verifier identifier (DID, URL, or pre-registered) | ||
| Optional | Where to send response (fragment mode) | ||
| Optional | Where to POST response (direct_post mode) | ||
| Required | fragment, direct_post, or direct_post.jwt | ||
| Required | Random value to prevent replay attacks | ||
| Optional | Opaque value for session correlation | ||
| Optional | Inline DIF Presentation Exchange object | ||
| Optional | URL to fetch presentation definition | ||
| Optional | How to interpret client_id (did, x509_san_dns, etc.) | ||
| Optional | Verifier metadata (name, logo, policy URLs) |
RequiredMust be "vp_token" for OID4VP
RequiredVerifier identifier (DID, URL, or pre-registered)
OptionalWhere to send response (fragment mode)
OptionalWhere to POST response (direct_post mode)
Requiredfragment, direct_post, or direct_post.jwt
RequiredRandom value to prevent replay attacks
OptionalOpaque value for session correlation
OptionalInline DIF Presentation Exchange object
OptionalURL to fetch presentation definition
OptionalHow to interpret client_id (did, x509_san_dns, etc.)
OptionalVerifier metadata (name, logo, policy URLs)
Request by Reference
Using request_uri is recommended for production deployments because:
- Request can be signed as a JWT (Request Object) for authenticity
- Avoids URL length limits with complex presentation definitions
- Enables request confidentiality with encryption
- Allows dynamic request generation
Example Authorization Request
// Authorization Request (URL-encoded query params)
openid4vp://?
response_type=vp_token
&client_id=https://verifier.example.com
&client_id_scheme=redirect_uri
&response_mode=direct_post
&response_uri=https://verifier.example.com/callback
&nonce=n-0S6_WzA2Mj
&state=af0ifjsldkj
&presentation_definition={...}Presentation Definition
The Presentation Definition (from DIF Presentation Exchange) describes exactly what credentials and claims the verifier needs. It's the core of OID4VP's flexibility.
Field Constraints
Each input descriptor can specify field constraints to require specific claims:
| Property | Type | Required | Description |
|---|---|---|---|
| JSONPath[] | Optional | Paths to the claim in credential (e.g., $.credentialSubject.dateOfBirth) | |
| JSON Schema | Optional | Value constraints (type, pattern, minimum, etc.) | |
| boolean | Optional | Whether field is required (default: false) | |
| boolean | Optional | Verifier intends to store this data | |
| string | Optional | Human-readable field name | |
| string | Optional | Why this field is needed |
OptionalJSONPath[]Paths to the claim in credential (e.g., $.credentialSubject.dateOfBirth)
OptionalJSON SchemaValue constraints (type, pattern, minimum, etc.)
OptionalbooleanWhether field is required (default: false)
OptionalbooleanVerifier intends to store this data
OptionalstringHuman-readable field name
OptionalstringWhy this field is needed
Selective Disclosure
Setting limit_disclosure: "required" tells the wallet to only disclose the fields explicitly requested. This is essential for privacy when using SD-JWT or mdoc credentials that support selective disclosure.
Example Presentation Definition
{
"id": "age-verification-request",
"name": "Age Verification",
"purpose": "Verify you are over 21 for alcohol purchase",
"input_descriptors": [
{
"id": "age_credential",
"name": "Proof of Age",
"purpose": "We need to verify your age",
"format": {
"jwt_vp_json": {
"alg": ["ES256", "EdDSA"]
},
"mso_mdoc": {
"alg": ["ES256"]
}
},
"constraints": {
"limit_disclosure": "required",
"fields": [
{
"path": ["$.credentialSubject.dateOfBirth", "$.vc.credentialSubject.dateOfBirth"],
"filter": {
"type": "string",
"format": "date"
},
"intent_to_retain": false
},
{
"path": ["$.credentialSubject.ageOver21", "$.vc.credentialSubject.ageOver21"],
"filter": {
"type": "boolean",
"const": true
},
"optional": true
}
]
}
}
]
}VP Token Response
After user consent, the wallet creates a VP Token containing the requested credentials and sends it back to the verifier along with a Presentation Submission that maps the credentials to the request.
VP Token Structure
The VP Token is a Verifiable Presentation containing the credentials. It binds the presentation to the specific request using the nonce.
// VP Token (JWT format - decoded payload)
{
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"aud": "https://verifier.example.com",
"nonce": "n-0S6_WzA2Mj",
"iat": 1704067200,
"exp": 1704070800,
"vp": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiablePresentation"],
"verifiableCredential": [
"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..." // Embedded VC (JWT or SD-JWT)
],
"holder": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}
}Presentation Submission
Maps how the VP Token fulfills the presentation definition, including the path to each credential.
{
"id": "submission-1",
"definition_id": "age-verification-request",
"descriptor_map": [
{
"id": "age_credential",
"format": "jwt_vp_json",
"path": "$",
"path_nested": {
"format": "jwt_vc_json",
"path": "$.vp.verifiableCredential[0]"
}
}
]
}Presentation Submission Properties
| Property | Type | Required | Description |
|---|---|---|---|
| Optional | Matches the presentation_definition.id | ||
| Optional | ID of the presentation definition being fulfilled | ||
| Optional | Array mapping input descriptors to credentials in VP token |
OptionalMatches the presentation_definition.id
OptionalID of the presentation definition being fulfilled
OptionalArray mapping input descriptors to credentials in VP token
Direct Post Response
The direct_post response mode sends the VP Token directly to the verifier's backend, which is more secure for sensitive credentials.
// POST to response_uri
POST /callback HTTP/1.1
Host: verifier.example.com
Content-Type: application/x-www-form-urlencoded
vp_token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...
&presentation_submission=%7B%22id%22%3A%22submission-1%22...%7D
&state=af0ifjsldkjResponse Encryption
Using direct_post.jwt wraps the response in a JARM (JWT-Secured Authorization Response Mode) JWT, providing confidentiality and integrity protection for the VP Token in transit.