OID4VPRequest & Response

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

Required
Type:

Must be "vp_token" for OID4VP

Required
Type:

Verifier identifier (DID, URL, or pre-registered)

Optional
Type:

Where to send response (fragment mode)

Optional
Type:

Where to POST response (direct_post mode)

Required
Type:

fragment, direct_post, or direct_post.jwt

Required
Type:

Random value to prevent replay attacks

Optional
Type:

Opaque value for session correlation

Optional
Type:

Inline DIF Presentation Exchange object

Optional
Type:

URL to fetch presentation definition

Optional
Type:

How to interpret client_id (did, x509_san_dns, etc.)

Optional
Type:

Verifier 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:

Optional
Type:JSONPath[]

Paths to the claim in credential (e.g., $.credentialSubject.dateOfBirth)

Optional
Type:JSON Schema

Value constraints (type, pattern, minimum, etc.)

Optional
Type:boolean

Whether field is required (default: false)

Optional
Type:boolean

Verifier intends to store this data

Optional
Type:string

Human-readable field name

Optional
Type:string

Why 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

Optional
Type:

Matches the presentation_definition.id

Optional
Type:

ID of the presentation definition being fulfilled

Optional
Type:

Array 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=af0ifjsldkj

Response 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.

Security Considerations

Request Validation

Response Security