Translating OID4VP 1.0 into Human Language

2,980 words11–13 min read20 min listen

In the previous post, we followed a credential from the issuer into the wallet with OID4VCI. That is only half of the story. A credential sitting in a wallet is worth nothing until someone can check it.

That second half is handled by OpenID for Verifiable Presentations 1.0, which became a Final specification in July 2025. It defines how a website, an app, or a terminal asks a wallet for proof, and how the wallet answers.

Like its issuance sibling, OID4VP is long, precise, and full of terms that make sense only after you understand the problem they solve: DCQL, Client Identifier Prefix, direct_post.jwt, Key Binding JWT, transaction_data. This post translates those concepts into plain language, explains why each piece exists, and highlights the decisions that matter when you build with it.

The one-sentence version

OID4VP is OAuth 2.0 turned around: the verifier plays the role of the app asking for something, the wallet plays the role of the server that decides what to share, and instead of receiving an access token, the verifier receives a cryptographic proof called a Verifiable Presentation.

If OID4VCI is "OAuth that hands out things you keep," OID4VP is "OAuth that lets you show those things without giving them away."

Credential vs presentation: the difference that matters

Before going further, one distinction needs to be clear.

A credential is what the issuer gave you: a signed diploma, licence, or ID. It stays in your wallet.

A presentation is what you show to a specific verifier at a specific moment. It is created by the wallet from one or more credentials, contains only the information that is actually needed, and is cryptographically tied to that verifier and that request.

The best real-world analogy is not handing over your passport. It is a bartender asking whether you are over eighteen, and you answering in a way they can trust without revealing your name, address, or exact birthday.

In plain words: the credential is the document; the presentation is the answer you give with it.

Meet the cast

OID4VP has two main characters on the wire:

  • The Verifier is the party asking for proof: a bank opening an account, an online shop checking age, an employer checking a diploma, or a car rental desk checking a driving licence. In OAuth terms, the verifier is the client.
  • The Wallet holds the credentials and builds the presentation. In OAuth terms, it plays the role of the authorization server.

The issuer is notably absent. The verifier checks the issuer's signature using public information, but the issuer is not contacted during the presentation and does not learn where the credential is used.

That absence is one of the most important privacy properties in self-sovereign identity. A government should not receive a notification every time a citizen proves their age to buy wine.

Let us return to Ana, who now holds the digital diploma from the previous post. She is applying for a job, and the employer wants to verify that she actually graduated.

Step one: the verifier asks

The verifier creates an Authorization Request. It contains:

  • who the verifier is;
  • what it wants to see;
  • a fresh random nonce so the answer cannot be replayed;
  • where and how the wallet should send the response.

The request reaches Ana's wallet in one of two ways.

Same-device flow

Ana is applying from her phone. The employer's website opens her wallet app directly through a link or redirect. When she approves, the wallet sends the response, and she ends up back in the browser where she started.

Cross-device flow

Ana is applying from her laptop. The website shows a QR code. She scans it with her phone, approves the request in the wallet, and the wallet sends the response directly to the verifier's backend. The laptop page then updates to show that the check succeeded.

The cross-device flow is convenient, but it is also where the most interesting security problems appear. We will return to them later.

Small QR codes, signed requests

A full request can be large, and nobody enjoys scanning a QR code dense enough to look like static. So the QR code usually contains only a request_uri, a link from which the wallet fetches the full request.

That fetched request is typically a signed Request Object. The signature lets the wallet confirm that the request really came from the verifier it claims to be from, and that nobody changed it along the way.

OID4VP also adds request_uri_method=post. With this option, the wallet can tell the verifier what it supports, such as formats and algorithms, before the verifier builds the final request. It is a simple capability negotiation: "Here is what I can do; please ask in a way I understand."

Who is asking? Client Identifier Prefixes

This is the part that most people skip, and the part that matters most for security.

Before Ana shares anything, her wallet needs to answer a basic question: who is actually asking? A request saying "I am Big Employer Inc." is meaningless unless the wallet can verify it.

OID4VP solves this with Client Identifier Prefixes. The verifier's client_id begins with a label that tells the wallet how to check the verifier's identity:

  • x509_san_dns: — the verifier signs the request with an X.509 certificate, and the certificate must contain the stated DNS name. This is similar to how browsers verify websites.
  • x509_hash: — the verifier is identified by the hash of its certificate. This is useful when there is no DNS name to match.
  • decentralized_identifier: — the verifier is identified by a DID, and the wallet resolves that DID to find the verification key.
  • verifier_attestation: — a trusted third party has issued the verifier an attestation describing who it is and what it may request.
  • openid_federation: — the verifier's identity is established through an OpenID Federation trust chain.
  • redirect_uri: — the weakest option. The verifier is identified only by where the response goes, and the request cannot be signed. It is useful for testing and low-risk cases, but it should not be used for sensitive data.

When OID4VP is used through the browser's Digital Credentials API, an additional reserved prefix, origin:, lets the browser tell the wallet which website the request came from.

Requests without a prefix are treated as coming from a verifier that the wallet already knows through pre-registration.

In plain words: the prefix tells the wallet which kind of ID card the verifier is showing, and how to check whether that ID card is real.

Asking for permission to ask

Knowing who the verifier is does not automatically mean it should receive everything it requests.

An online shop may be a real company with a valid certificate and still have no reason to ask for someone's national ID number. Ecosystems such as the European Digital Identity Wallet therefore add registration certificates or similar attestations that state what a verifier is allowed to request.

OID4VP carries this information through the optional verifier_info parameter. The protocol provides the slot; the trust framework decides what goes into it and how wallets should enforce it.

What is being asked for? DCQL

The verifier now needs to describe exactly what it wants. OID4VP 1.0 uses a new language for this called the Digital Credentials Query Language, or DCQL, usually pronounced "dackle."

Earlier drafts relied on DIF Presentation Exchange, a powerful but complex query format. The final 1.0 specification replaced it with DCQL, which is smaller, more predictable, and easier to implement correctly.

A DCQL query for Ana's diploma might look like this:

{
  "credentials": [
    {
      "id": "diploma",
      "format": "dc+sd-jwt",
      "meta": { "vct_values": ["https://university.example/diploma"] },
      "claims": [{ "path": ["degree"] }, { "path": ["graduation_year"] }]
    }
  ]
}

Translated, this says: "I want one credential, which I will call diploma. It must be an SD-JWT VC of the university diploma type. From it, I only need the degree and the graduation year."

Notice what is missing. The verifier did not ask for Ana's student number, birth date, grades, or home address. If the credential supports selective disclosure, the wallet can reveal only the requested claims and keep everything else hidden.

Alternatives and combinations

Real requests are rarely as simple as "give me this one credential." DCQL can also express choices:

  • credential_sets lets the verifier say: "Give me either a national ID or a passport, plus a proof of address."
  • claim_sets lets the verifier list acceptable combinations of claims inside one credential: "Give me your exact birth date, or just confirm that you are over eighteen."
  • trusted_authorities lets the verifier say which issuers it accepts, such as issuers in a specific trusted list or certificate hierarchy, so the wallet does not offer credentials that the verifier will reject anyway.

In plain words: DCQL is a shopping list that can say "this or that," "only these fields," and "only from these sellers."

Proving it is really you: holder binding

Recall the most important idea from the OID4VCI post: the credential is bound to a key that only Ana's wallet controls.

OID4VP is where that binding is used. When Ana presents her diploma, the wallet signs the presentation with the private key. For an SD-JWT VC, this signature is a Key Binding JWT. For an mdoc, it is a device signature over a structure called the session transcript.

That signature covers two key values:

  • the nonce from the verifier's request, which proves the presentation is fresh;
  • the verifier's client_id, which proves the presentation was created for this verifier.

Together, they solve two different attacks.

If someone records Ana's presentation and tries to replay it later, the nonce will not match the new request. If a malicious verifier receives Ana's presentation and tries to forward it to another website, the audience will not match.

In plain words: every presentation is signed, dated, and addressed. A copy is useless because it was made for one specific verifier at one specific moment.

Some credentials do not require holder binding. OID4VP supports them, but they behave more like bearer documents. Anyone who obtains the credential may be able to present it. For anything sensitive, holder binding should be the default.

How does the answer get back? Response modes

Once Ana approves, the wallet returns a vp_token. In OID4VP 1.0, this is a JSON object whose keys match the IDs from the DCQL query. In our example, the response contains a diploma entry with the presentation inside.

How the response travels depends on the response mode:

  • fragment — the response is returned in the URL fragment of a redirect. This works for some same-device flows, but URLs are a poor place for large or sensitive data.
  • direct_post — the wallet sends the response directly to the verifier's backend over HTTPS. This is the standard choice for cross-device flows.
  • direct_post.jwt — the same as direct_post, but the response is encrypted to the verifier's public key. Only the verifier's backend can decrypt it, even if the response passes through infrastructure along the way.
  • dc_api and dc_api.jwt — used when the request and response pass through the browser's Digital Credentials API.

For production systems handling personal data, encrypted responses should be the norm. HAIP, the high-assurance interoperability profile, requires them.

The cross-device trap

Earlier, we said cross-device flows are where security gets interesting. Here is why.

Imagine an attacker opens a real bank login page that uses OID4VP. The page displays a QR code. The attacker copies that QR code into a phishing email that says, "Scan to claim your reward."

Ana scans it, sees a familiar bank name, and approves. Her wallet sends the presentation to the real bank backend, but the session that receives the result belongs to the attacker's browser.

This is known as session fixation or, more broadly, a cross-device phishing attack.

OID4VP addresses part of this problem in same-device flows. With direct_post, the verifier can return a redirect_uri containing a one-time response_code. The browser must present that code to complete the session, so a presentation submitted in a different browser session cannot easily be claimed by the attacker.

Cross-device flows are harder because the device that approves the request is not the device that receives the session. The protocol cannot fully solve that alone. Good wallet design helps by showing clearly who is asking, what they are asking for, and why. Short-lived requests, verifier authentication, and user education also matter.

The more robust long-term direction is the Digital Credentials API, where the browser and operating system mediate the request and can confirm which website is asking. Instead of trusting a QR code copied from anywhere, the wallet receives the request through a channel that carries the website's origin.

In plain words: a QR code does not know who showed it to you. The browser does.

Beyond "who are you": transaction data

Sometimes a verifier does not only need to know who someone is. It needs proof that the person approved a specific action.

For example:

  • confirm a payment of €250 to a specific merchant;
  • sign a particular contract;
  • authorize a change to bank account details.

OID4VP supports this with transaction_data. The verifier includes the transaction details in the request. The wallet shows them to the person and includes a hash of those details in the holder-binding signature.

This turns a presentation from "I am Ana" into "I am Ana, and I approved this exact transaction." That distinction is what makes OID4VP useful for payments, strong customer authentication, and other high-value actions where identity and consent must be tied together.

The browser joins the conversation: the Digital Credentials API

For years, websites relied on custom URL schemes such as openid4vp:// to open wallets. That approach has real limitations. Any app can register the same scheme, the website cannot reliably choose the right wallet, and the wallet does not know which website actually made the request.

The W3C Digital Credentials API changes this. A website calls a browser API, the browser and operating system show a credential picker, and the selected wallet receives the request together with the verified origin of the website.

OID4VP 1.0 includes an appendix describing how to use the protocol through this API. The request and response stay largely the same, but the channel between website and wallet becomes far more trustworthy.

This may be the most important shift in the next phase of digital identity. The protocol defines what is being asked and answered; the platform makes sure the right parties are in the conversation.

A complete journey, in plain words

Here is Ana's job-application check from start to finish:

  1. Ana clicks "Verify my degree" on the employer's website.
  2. The employer creates a signed Authorization Request with a fresh nonce, an x509_san_dns Client Identifier, and a DCQL query asking only for the degree and graduation year.
  3. Her browser passes the request to her wallet through the Digital Credentials API, or she scans a QR code.
  4. Her wallet verifies the employer's signature and shows the employer's name, the requested fields, and the purpose.
  5. Ana approves.
  6. The wallet selects her diploma, reveals only the requested fields, and creates a Key Binding JWT over the nonce and the employer's identifier.
  7. It encrypts the vp_token and sends it to the employer using direct_post.jwt.
  8. The employer decrypts it, checks the university's signature, the holder-binding signature, the nonce, the audience, and the credential's revocation status.
  9. The website updates: "Degree verified."

The employer never contacted the university. The university never learned that Ana applied for a job. And the employer learned two facts instead of receiving a full copy of her diploma.

What OID4VP does not solve

Like OID4VCI, OID4VP has a clear and deliberately limited scope. It defines how to request and deliver presentations securely. It leaves several important decisions to other layers.

  • Is the issuer trustworthy? OID4VP can tell the wallet which issuers the verifier accepts, but the verifier still needs trust lists, governance, and policy to decide.
  • Is the credential still valid? Revocation and suspension are handled by status mechanisms, not by the presentation protocol.
  • Is the verifier asking for too much? The protocol can carry request rules and verifier information, but a trust framework must define what is proportionate.
  • Will the person understand what they are sharing? That depends on wallet design. The protocol can provide the right data, but only the user experience can make consent meaningful.

There is also the same interoperability challenge that exists with OID4VCI. The core specification supports many formats, prefixes, and response modes. Real ecosystems need profiles such as HAIP to agree on a smaller set of choices that every wallet and verifier will implement.

Closing thought

OID4VP is not about handing over your documents. It is about answering a question, once, for one verifier, with only what that verifier needs.

Behind the acronyms, OID4VP follows a simple pattern. The verifier proves who it is and asks a precise question. The wallet checks the request, shows the person what will be shared, and creates a fresh, signed answer bound to that verifier. The verifier checks the answer without needing to call the issuer.

Each technical feature exists to protect part of that interaction: Client Identifier Prefixes authenticate the verifier, DCQL limits the request, nonces prevent replay, audience binding prevents forwarding, encrypted responses protect the data in transit, and transaction data connects identity to consent.

Together with OID4VCI, it completes the circle: credentials go in through one protocol and are shown out through another, using the same OAuth foundations that already secure much of the web.

The hard part now is not the protocol. It is everything around it: verifier registration, trust lists, wallet certification, clear consent screens, and the discipline to ask for less data than you could.