Home Supported Standards Terms & Conditions Privacy Policy Documentation Contact About
  1. 0. Choose a flowHow the verifier asks
  2. 1. The requestWho is asking, and for what
  3. 2. Choose & presentSelect disclosures, sign the KB-JWT
  4. 3. The verdictWhat the verifier saw and decided

← Back to step 1 (the request)

VC Presentation — Step 2 of 3: Choose What To Disclose

This is the page selective disclosure exists for. The credential carries everything the issuer put in it; the verifier asked for some of it; what leaves this browser is the holder’s choice. Pick the Disclosures to send, and the wallet assembles the presentation — the issuer-signed JWT, those Disclosures, and a Key Binding JWT signed over this request’s nonce, addressed to this verifier, carrying sd_hash over exactly the bytes being sent (RFC 9901 section 4.3). Nothing is sent until you press Present.

Tip: click a pane's title to collapse/expand it.

What This Verifier Asked For
The Client Identifier from the request. It becomes the aud of the Key Binding JWT.
Signed into the Key Binding JWT, which is what ties this presentation to this one request.
The claim paths in the DCQL query. Everything else in the credential is the holder's business.
require_cryptographic_holder_binding in the credential query. It defaults to true: a presentation without a holder proof is the exception.
Claims in the issuer-signed JWT itself rather than in a Disclosure. They cannot be withheld without breaking the issuer's signature, so they travel whatever you choose below.
 
Choose What To Disclose

One row per Disclosure the credential carries. The default selection is exactly what the verifier asked for — a wallet should not have to be told to minimise — but the choice is the holder’s, in both directions: send less and the verifier will refuse, send more and it will accept happily and keep what it was given.

 

The Presentation

Assembled from the choice above, before anything is sent: issuer-signed JWT~selected Disclosure~~KB-JWT.

base64url of the hash of the issuer-signed JWT and the selected Disclosures, each followed by a tilde. Recomputed here; the verifier recomputes it too, and a mismatch means the presentation was altered after it was signed.
typ must be kb+jwt and alg must not be none (RFC 9901 section 4.3).
 
Exactly iat, aud, nonce and sd_hash. OID4VP fixes two of them: the nonce is the request's, and the aud is the verifier's Client Identifier.
 
The signed Key Binding JWT, as it will be appended to the presentation.
The SD-JWT+KB in Combined Serialization: what the verifier will receive.
OID4VP section 8.1: a JSON object keyed by the DCQL credential query id, each value an array of presentations.
 
The claim set that follows from exactly these bytes — computed from the presentation, not from what this page intended to send.
 
Key Binding and DPoP

 

Why this workflow has no DPoP switch

DPoP (RFC 9449) sender-constrains an OAuth access token. OpenID4VP does not mention DPoP at all, and the reason is structural rather than an omission: in its own words, “the result of an OpenID4VP interaction is one or more Verifiable Presentations and/or Presentations in the same or different Credential formats … instead of an Access Token”. There is no token in this exchange to bind.

What plays the same role here is the Key Binding JWT (RFC 9901 section 4.3), and the parallel is close enough to be worth seeing side by side. Both are short-lived JWTs of their own media type, signed by a key the holder possesses, that commit to the specific request they accompany:

 DPoP proofKey Binding JWT
typdpop+jwtkb+jwt
proves possession ofthe key in the token's cnf.jktthe key in the credential's cnf.jwk
freshness fromiat, jti, and a server noncethe verifier's nonce and iat
bound to the recipient byhtu (the endpoint URI)aud (the Client Identifier)
commits to the payload byath, the hash of the access tokensd_hash, the hash of the exact bytes sent

So the wallet's proof of possession in a presentation is the KB-JWT, and the thing DPoP would have protected — a bearer token that anyone holding the bytes could spend — does not exist here. DPoP does apply to the issuance side, where there is an access token, and it is switched on there.

Present It

Present these claims to this verifier?

With response_mode=direct_post the wallet is the one making the request: it POSTs the vp_token to the verifier’s Response URI. Refusing is a real answer too — OID4VP section 8.4 has access_denied for it, and the verifier learns the request was seen and declined.

The fully assembled HTTP request: method, the Response URI, the content type, and the form body carrying the vp_token. This is exactly what Present sends.

 
VC Tools

Tools that are useful at any point in these workflows, and belong to none of their steps.

Resolve a DID to its document, read the keys it publishes, check that one of them really signed the credential this browser is holding, and check that the DID and an origin are the same entity.
Resolve a Decentralized Identifier (did:jwk, did:key, did:web) to its document, verify that a key it publishes signed the credential in this browser, and verify a domain linkage (DIF Well Known DID Configuration). Opens in this tab; the workflow's state is untouched.
Build a Root, Intermediate and Issuing CA, issue certificates with any X.509v3 extension, and test a real TLS or mutual-TLS handshake with what you issued.
Build a certificate authority — Root, Intermediate and Issuing — and issue TLS server, TLS client, code-signing and S/MIME certificates with full X.509v3 extension control. Useful when the issuer or verifier is behind TLS you control. Opens in this tab; the workflow's state is untouched.