Translating OID4VCI 1.0 into Human Language
OpenID for Verifiable Credential Issuance 1.0 became a Final specification in September 2025. It is the protocol that decides how a digital credential, such as a diploma, a driving licence, an employee badge, or a proof of age, gets from the organization that issues it into the wallet on your phone.
It is also a document of well over a hundred pages, full of words like Pre-Authorized Code, Credential Configuration, c_nonce, and authorization_details. Most of the people who need to understand it, including product managers, architects, compliance teams, and even many engineers, will never read it end to end.
This post is a translation. It walks through what OID4VCI does, why each piece exists, and what each technical term means in plain language. By the end, you should be able to follow a conversation between two wallet engineers without nodding along blindly.
The one-sentence version
OID4VCI is OAuth 2.0 with a new kind of thing at the end of it: instead of receiving an access token that lets you call an API, the wallet uses that access token to collect a signed credential that it gets to keep.
If you have ever clicked "Sign in with Google," you have already used most of the machinery. OID4VCI reuses the same building blocks of authorization servers, tokens, redirects, and client authentication, then adds a new endpoint whose only job is to hand out credentials.
That design choice matters. It means issuers do not need to invent a new security model. They can reuse identity infrastructure they already operate, audit, and trust.
Meet the cast
Every OID4VCI interaction has three main characters.
- The Credential Issuer is the organization that creates and signs the credential: a university, a government agency, a bank, or an employer. It is the entity whose signature gives the credential its value.
- The Wallet is the app that requests, receives, and stores the credential on behalf of a person. In OAuth vocabulary, the wallet is the client.
- The Authorization Server decides whether the wallet is allowed to receive a credential and hands it an access token. It can be operated by the issuer itself or by an existing identity provider the issuer already uses.
There is one more character who is easy to forget: the person holding the phone. The spec calls them the End-User. The whole protocol exists so that this person ends up with something useful in their wallet, so every design decision should be evaluated from their point of view as well as the issuer's.
To keep things concrete, imagine Ana, who has just graduated. Her university wants to give her a digital diploma that she can later show to employers.
Step zero: the issuer describes itself
Before any credential changes hands, the wallet needs to know what the issuer can offer and how to talk to it.
The issuer publishes a JSON document at a well-known address, /.well-known/openid-credential-issuer, called the Credential Issuer Metadata. Think of it as the menu posted outside a restaurant. It lists:
- where the issuer's endpoints live;
- which authorization servers it trusts;
- which credentials it can issue, described as Credential Configurations;
- how each credential should be displayed, including names, logos, colours, and languages.
A Credential Configuration is a recipe for one type of credential. "University diploma as an SD-JWT VC" is one configuration. "University diploma as an ISO mdoc" would be another. Each configuration specifies the credential format, the claims it contains, the signing algorithms the issuer uses, and how the wallet must prove that it controls the key the credential will be bound to.
In plain words: before ordering, the wallet reads the menu.
Step one: the offer
There are two ways a credential journey can begin.
In the first, the person starts in the wallet: Ana opens her wallet, searches for her university, and asks for a diploma. This is the wallet-initiated flow.
In the second, the issuer starts it. Ana logs into the university portal, clicks "Add diploma to my wallet," and is shown a QR code or a deep link. This is the issuer-initiated flow, and the thing inside that QR code is called a Credential Offer.
A Credential Offer is a small invitation that says, essentially: "I am this issuer, I would like to give you these credentials, and here is how to start."
{
"credential_issuer": "https://credentials.university.example",
"credential_configuration_ids": ["UniversityDiploma_SD-JWT"],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "oaKazRN8I0IbtZ0C7JuMn5",
"tx_code": { "input_mode": "numeric", "length": 6 }
}
}
}
The offer can be sent in full ("by value") or as a link the wallet fetches ("by reference," via credential_offer_uri). Sending it by reference keeps QR codes small and scannable, which sounds trivial until you try to scan a dense QR code from across a desk.
The grants section is the interesting part. It tells the wallet which of the two main paths it should take next.
Two roads to the same credential
The Authorization Code flow: "prove who you are first"
This is the classic OAuth flow. The wallet redirects Ana to the university's login page. She authenticates there, perhaps with her student account and multi-factor authentication, and approves the request. The authorization server then sends the wallet an authorization code, which the wallet exchanges for an access token.
Use this when the issuer needs the person to log in during the issuance process itself.
The spec recommends the same security hardening you would expect in any modern OAuth deployment: PKCE, which prevents a stolen authorization code from being used by someone else, and Pushed Authorization Requests (PAR), which send the request details over a back channel rather than squeezing them into a browser URL.
The Pre-Authorized Code flow: "we already know it's you"
Sometimes the issuer has already identified the person through a separate process. Ana might have completed identity verification at the university front desk, or she may already be logged into the student portal when she clicks the button.
In that case, forcing another login would be redundant. The Credential Offer carries a Pre-Authorized Code, a one-time ticket that the wallet can exchange directly for an access token.
The obvious risk is that a ticket is useful to whoever holds it. If someone photographs the QR code over Ana's shoulder, they could try to claim her diploma first.
That is why the issuer can require a Transaction Code (tx_code). It is a short PIN delivered through a different channel, such as an email or text message. The offer only tells the wallet that a PIN is needed, including its length and whether it is numeric. The PIN itself never appears in the QR code.
In plain words: the QR code is the ticket, and the transaction code is the ID you show at the door.
Which one should you use?
As a rough guide:
- Use the Authorization Code flow when login is part of the issuance experience, when the wallet starts the journey, or when the issuer wants full OAuth protections.
- Use the Pre-Authorized Code flow when the person is already authenticated in another channel and the goal is a one-tap "add to wallet" experience.
Whichever flow you choose, the wallet ends up in the same place: holding an access token that authorizes it to request a specific credential.
Asking precisely: authorization details
In ordinary OAuth, a client asks for permissions using scopes, short strings like read:email. OID4VCI still supports scopes, but it also allows a more precise mechanism called authorization_details, borrowed from the OAuth Rich Authorization Requests specification.
With authorization_details of type openid_credential, the wallet can say exactly which Credential Configuration it wants. The authorization server can answer with Credential Identifiers: concrete handles for specific credential datasets.
This matters when one person has several credentials of the same type. Ana may hold a bachelor's degree and a master's degree from the same university. Both use the same "diploma" recipe, but they contain different data. Credential Identifiers let the wallet request each one specifically.
In plain words: scopes say "a diploma, please." Authorization details can say "this diploma, please."
The heart of it: proving you hold the key
This is the most important idea in the entire protocol, and also the easiest to misunderstand.
A good digital credential is not just signed by the issuer. It is also bound to a key held by the wallet. When Ana later presents her diploma, she will sign the presentation with that key, and the verifier will check that the signature matches the key embedded in the credential.
Without that binding, a credential is like a bearer cheque: anyone who copies the file can use it. With binding, a stolen copy is useless without the private key that lives in the phone's secure hardware.
So before the issuer embeds a key into a credential, it needs evidence that the wallet actually controls that key. The wallet provides a proof of possession: a small structure signed with the private key.
Three proof types are defined:
jwt: a signed JWT containing the public key and fresh request details. This is the most common option.di_vp: a W3C Data Integrity presentation, used in JSON-LD credential ecosystems.attestation: a key attestation, a statement from a trusted party, such as the wallet provider, that describes the key and how it is protected.
The c_nonce: why freshness matters
A proof is only useful if it cannot be replayed. If an attacker captured a valid proof yesterday, they should not be able to reuse it today.
The issuer therefore provides a c_nonce, a random value that must be included in the proof. In OID4VCI 1.0, the wallet obtains this value from a dedicated Nonce Endpoint. Earlier drafts returned it inside the token response, but moving it to a separate endpoint makes it simpler for the issuer to control nonce lifetimes independently of the OAuth flow.
The proof also includes the issuer's identifier as its audience, so a proof made for one issuer cannot be redirected to another.
In plain words: the issuer says "sign this random number," the wallet signs it with the key, and now the issuer knows the key is real, current, and meant for it.
Key attestations: "and the key lives somewhere safe"
Proof of possession tells the issuer that the wallet controls a key. It does not tell the issuer where that key lives. A key held in a phone's secure element is very different from a key stored in a plain file that malware can read.
For high-assurance credentials, such as national identity documents, the issuer may need to know that. Key attestations let a trusted party vouch for the key's protection: whether it is hardware-backed, whether it requires user authentication, and what level of resistance to attack it offers.
This is one of the pieces that makes OID4VCI usable for government-grade credentials, including the European Digital Identity Wallet ecosystem, rather than just low-risk badges.
Collecting the credential
With an access token and a proof in hand, the wallet calls the Credential Endpoint. The request is short: it names the credential it wants and includes the proof.
If everything checks out, the issuer returns the signed credential, and Ana's diploma appears in her wallet.
Batch issuance: many copies for privacy
OID4VCI 1.0 allows the wallet to request several instances of the same credential at once, each bound to a different key. The wallet does this by sending multiple proofs in the proofs parameter. Issuers advertise support, and their maximum batch size, through batch_credential_issuance in their metadata.
Why would anyone want ten copies of the same diploma?
Privacy. If Ana presents the identical credential to ten different verifiers, those verifiers could compare notes and recognize her as the same person through the shared signature, the shared key, or the shared identifiers. This is called linkability.
If she presents a different copy each time, there is far less for them to correlate. Batch issuance makes that strategy practical without forcing the wallet to repeat the whole flow over and over.
In plain words: the wallet asks for a stack of single-use copies so verifiers cannot use the same fingerprint to follow her around.
Formats: the protocol does not care what is inside the envelope
OID4VCI is deliberately format-agnostic. Its appendices define profiles for the three credential families that matter most today:
- IETF SD-JWT VC: JSON-based credentials with built-in selective disclosure, increasingly the default for many new deployments.
- ISO/IEC 18013-5 mdoc: the format behind mobile driving licences, designed for both in-person and online use.
- W3C Verifiable Credentials: secured either as JWTs or with Data Integrity proofs.
The protocol is the delivery service. The format is the envelope. The same delivery process can carry different envelopes.
When the credential is not ready yet
Not every credential can be issued instantly. A background check, a manual review, or a back-office process may need hours or days.
For this, OID4VCI defines the Deferred Credential Endpoint. Instead of returning the credential immediately, the issuer returns a transaction_id and an interval suggesting when to check back. The wallet later presents that transaction_id to collect the result.
In plain words: "Your order is being prepared; here is your ticket number."
It is a small feature, but an important one. Without it, issuers with real-world approval processes would be forced to keep connections open or invent their own polling protocols.
Closing the loop: notifications
After issuance, the issuer often wants to know what happened next. Did the credential actually reach the wallet? Did the person reject it? Did they delete it later?
The optional Notification Endpoint lets the wallet report events such as credential_accepted, credential_failure, and credential_deleted.
This is useful for support and operations: an issuer can tell the difference between "we issued it, but something went wrong in the wallet" and "the person chose not to keep it." It is also a privacy-sensitive feature, so wallets should send these notifications only for the purposes the protocol intends, not as a channel for tracking usage.
Trusting the wallet itself
Everything so far has focused on the issuer trusting the key. But some issuers also need to trust the wallet application holding that key.
A government issuing a national ID may only be willing to issue into certified wallets that meet specific security requirements. OID4VCI supports this through Wallet Attestations: signed statements from the wallet provider confirming that this instance is a genuine, uncompromised version of their app.
The wallet uses this attestation as a form of client authentication when it talks to the authorization server.
This is where OID4VCI meets governance. The protocol can carry the attestation, but it cannot decide which wallet providers deserve trust. That decision belongs to the ecosystem's trust framework: the rules, certification schemes, and trusted lists that sit around the protocol.
Keeping the credential private in transit
Credentials contain personal data, and TLS already protects that data in transit. But in some architectures, TLS terminates at a load balancer, an API gateway, or another intermediary before the request reaches the system that actually handles the credential.
For those cases, OID4VCI allows credential response encryption. The wallet supplies a key, and the issuer encrypts the credential so only the wallet can open it. The request can be encrypted as well.
This adds end-to-end protection on top of transport-level protection. It is not always necessary, but it is valuable when the infrastructure between issuer and wallet is broader than a single trusted service.
A complete journey, in plain words
Putting it all together, here is Ana's diploma journey:
- Ana logs into her university portal and clicks "Add to wallet." She sees a QR code.
- Her wallet scans it and finds a Credential Offer with a Pre-Authorized Code.
- The wallet reads the university's Credential Issuer Metadata to understand what is on offer and how to display it.
- The university emails Ana a six-digit Transaction Code. She types it into the wallet.
- The wallet exchanges the code and PIN for an access token.
- The wallet fetches a fresh c_nonce from the Nonce Endpoint.
- It generates keys in secure hardware and signs proofs of possession over that nonce, optionally with a key attestation.
- It calls the Credential Endpoint and receives a batch of signed diplomas, each bound to a different key.
- The wallet sends a
credential_acceptednotification, and the diploma appears on Ana's screen with the university's logo.
From Ana's perspective, she scanned a code, typed a PIN, and saw a card appear. Everything else happened behind the scenes.
That is the goal of a good protocol: it can be complex underneath while remaining simple for the person using it.
What OID4VCI does not solve
OID4VCI is a delivery protocol. It is very good at answering one question: "How does a credential get from the issuer into the wallet securely?"
It deliberately leaves several other questions to other layers:
- What goes inside the credential? That is the credential format's job, including SD-JWT VC, mdoc, and W3C VC.
- How is the credential presented later? That is OID4VP's job, which is the subject of the next post in this series.
- Who is allowed to issue what, and which wallets are trustworthy? That is the trust framework's job.
- How do verifiers know whether a credential has been revoked? That is the job of status mechanisms such as Token Status Lists or Bitstring Status Lists.
The spec also offers many options. That flexibility helps it serve very different ecosystems, but it is also a source of interoperability problems. Two "OID4VCI-compliant" implementations can still fail to talk to each other if they made different optional choices.
That is why interoperability profiles such as HAIP, the OpenID4VC High Assurance Interoperability Profile, matter. They narrow the menu to a specific set of formats, algorithms, and security features that everyone in an ecosystem agrees to support.
Closing thought
OID4VCI does not reinvent identity. It teaches OAuth to hand out things you keep instead of things you spend.
Strip away the terminology, and OID4VCI is a familiar story. An organization publishes what it can issue. A wallet asks for something. The organization checks that the right person is asking and that the wallet controls a safe key. Then it signs the credential, delivers it, and optionally learns whether it arrived.
The vocabulary is dense because the security requirements are real. Every term exists to close a specific gap: replay, theft, linkability, untrusted wallets, or slow back-office processes.
For teams adopting OID4VCI, the hardest decisions are rarely about the protocol mechanics. They are about choosing the right flow for the user experience, choosing a credential format and profile that the ecosystem will accept, defining which wallets deserve trust, and designing what happens when issuance fails.
Get those decisions right, and the person on the other side of the QR code will never need to know what a c_nonce is.