Skip to content

OID4VP

  • OpenID for Verifiable Presentations 1.0 (final)
  • OAuth 2.0 Authorization Framework (request/response parameter model)
  • JOSE (JWT/JWS/JWE processing for request objects and direct_post.jwt)
  • DID Core / did:web (for decentralized_identifier client_id scheme handling)
  • RFC 5280 / PKIX (X.509 certificate chain validation for x509_san_dns)
Flow IDNameStepsDescription
oid4vp-direct-postDCQL + direct_post4Verifier creates signed request object, wallet fetches from request_uri, posts vp_token + state to response_uri, verifier validates and evaluates policy
oid4vp-direct-post-jwtDCQL + direct_post.jwt4Verifier creates signed request, wallet fetches from request_uri, posts encrypted JWE containing signed oauth-authz-resp+jwt, verifier decrypts and evaluates policy
  • DCQL direct_post — Create request object, hand off to wallet, evaluate verifier policy
  • DCQL direct_post.jwt — Same flow with encrypted response transport and inner JWT validation

The Looking Glass OID4VP controls preflight the selected VC credential profile against the DCQL preset before wallet submission. If the preset requests dc+sd-jwt, jwt_vc_json, jwt_vc_json-ld, or ldp_vc, the wallet auto-issue profile must issue one of those formats. Otherwise the wallet action is blocked with an explanation, because the wallet cannot build a matching vp_token from the selected profile.

When a credential_jwt is pasted manually, the UI allows submission but warns when the selected auto-issue profile does not match the DCQL format. The wallet and verifier still validate the actual credential and will reject the presentation if the credential evidence does not satisfy the request.

Example DCQL query requesting a university_degree credential with degree and graduation_year claims:

{
"credentials": [{
"id": "university_degree",
"claims": [
{ "path": ["degree"] },
{ "path": ["graduation_year"] }
],
"meta": {
"vct_values": ["https://protocolsoup.com/credentials/university_degree"]
}
}]
}
  • Authorization request enforces XOR: exactly one of dcql_query or scope.
  • For response_mode direct_post and direct_post.jwt, response_uri is required and redirect_uri must be absent.
  • Request object header typ must be oauth-authz-req+jwt.
  • VP token header typ must be vp+jwt.
  • direct_post.jwt inner response header typ must be oauth-authz-resp+jwt.

OpenID4VP defines multiple client identification schemes. ProtocolSoup implements the following:

Verifier identity is established by the redirect URI trust context. The client_id equals the response_uri.

Verifier identity is resolved via DID document. The DID document is fetched and validated at runtime — id must match the presented DID, and verification material must be present in authentication, assertionMethod, or verificationMethod.

Verifier presents a signed attestation JWT. The verifier’s attestation issuer publishes OpenID discovery metadata and JWKS. The wallet validates the attestation JWT against the published JWKS. When OID4VP_VERIFIER_ATTESTATION_PRIVATE_KEY_PEM is unset, an ephemeral in-memory key is auto-provisioned at startup.

Verifier identity is bound to a DNS name via X.509 certificate Subject Alternative Name. The request object carries an x5c JOSE header containing the certificate chain. Trust validation requires:

  1. PKIX chain validation (leaf signed by CA, validity period, key usage)
  2. Leaf certificate DNS SAN matches the client_id DNS name
  3. response_uri hostname matches the client_id DNS name
  4. JWT signature verifies against the leaf certificate public key
  5. The client_id_scheme claim is present in the signed request object

When no certificate material is configured via environment variables, the verifier auto-provisions an ephemeral ECDSA P-256 self-signed CA + leaf chain at startup, with the leaf SAN bound to the deployment hostname.

  • pre_registered, x509_hash, openid_federation are defined in the spec but not currently implemented.

Verifier Evaluation Flow (OID4VP §5, §6, §8)

Section titled “Verifier Evaluation Flow (OID4VP §5, §6, §8)”
  1. Create Authorization Request — Verifier builds a signed request object (typ=oauth-authz-req+jwt) containing client_id, response_type=vp_token, response_mode, response_uri, dcql_query (or scope), nonce, state, and exp. Publishes it at a request_uri.
  2. Wallet Fetches Request Object — Wallet fetches the signed request from request_uri via GET or POST. Validates JWT signature, typ header, and expiry before processing.
  3. Wallet Submits Response — Wallet evaluates DCQL query, selects matching credentials, creates signed vp_token (typ=vp+jwt), and submits to response_uri:
    • direct_post: form-post vp_token + state
    • direct_post.jwt: form-post encrypted response JWE containing signed inner JWT (typ=oauth-authz-resp+jwt) with vp_token, state, and audience bound to response_uri
  4. Verifier Validates + Policy Decision — Verifier resolves session by state, then validates:
    • nonce binding (VP token nonce matches request nonce)
    • audience binding (VP token audience includes client_id)
    • token expiry
    • holder binding (iss/sub matches wallet identity, cnf.jkt matches key thumbprint)
  • credential evidence (issuer signature, subject binding, disclosure integrity, and trust policy checks)
  • DCQL claim-path completeness (all required claim paths present in disclosed claims)
  1. Result — Policy decision is stored and returned as allowed/denied with code, message, reasons, and reason_codes.
Endpoint RolePurpose
Authorization Request EndpointVerifier creates and publishes a request object / request URI
Request URI Retrieval EndpointWallet fetches request object by request_uri
Verifier Attestation DiscoveryOpenID discovery metadata for the verifier attestation issuer
Verifier Attestation AS MetadataOAuth 2.0 Authorization Server metadata for the attestation issuer
Verifier Attestation JWKSPublic signing keys used to validate verifier_attestation request objects
Response URI EndpointWallet submits direct_post or direct_post.jwt response payloads
Verification Result Endpoint (optional)Verifier exposes policy evaluation outcome to client tooling
  • direct_post response parameters: state=<opaque-value>&vp_token=<compact-jws>
  • direct_post.jwt response parameter: response=<compact-jwe> where the decrypted plaintext is an inner JWT with typ=oauth-authz-resp+jwt
  • VP token essentials: typ=vp+jwt, nonce bound to request nonce, audience bound to verifier client_id
  • Authorization request: signed request object with typ=oauth-authz-req+jwt, client_id, response_mode, response_uri, nonce, state, exp
  • DCQL contract: exactly one of dcql_query or scope; required claim paths derived from DCQL are enforced
  • Client identification: chosen client_id scheme follows OpenID4VP scheme-specific trust validation
  • direct_post response: state maps to active request and vp_token is present
  • direct_post.jwt response: decrypts successfully and inner JWT signature/type/audience/subject/expiry checks pass
  • VP token checks: typ=vp+jwt, nonce match, audience match, expiry valid, holder binding verified
  • Credential evidence checks: presented credential subject/issuer/signature/disclosures satisfy verifier trust policy and required claim paths
  • Policy output: verifier returns explicit decision with status, code, and reason details