OID4VCI (Credential Issuance)
OID4VCI (OpenID for Verifiable Credential Issuance) is the OpenID Foundation protocol that defines how a wallet obtains a credential from an issuer. It builds on OAuth 2.0, so wallets and issuers exchange tokens in a standardised way rather than inventing bespoke flows.
iDen2's Credential Factory implements OID4VCI for you. Your system never speaks the protocol directly — you create an offer, display a QR code, and poll for status. The wallet and Credential Factory handle everything in between.
The pre-authorized code flow
OID4VCI defines several flows. Credential Factory uses the pre-authorized code flow (authenticationMethod: "PRE_AUTHORIZED"): you, the issuer, have already authenticated the user through your own means (they're logged into your app, completed KYC, etc.), so the wallet doesn't need to authenticate again. The offer embeds a pre-authorized code that the wallet exchanges directly for the credential.
Anatomy of the offer URL
The offerUrl returned by POST /api/v1/iaas/credential-offers looks like:
openid-credential-offer://...?credential_offer=...
- The
openid-credential-offer://scheme is registered by wallet apps, so scanning the QR (or tapping the link on mobile) opens the wallet directly. - The
credential_offerparameter carries (or references) a JSON object identifying the issuer, the credential being offered, and the pre-authorized code grant.
You never need to construct or parse this URL — treat it as opaque and present it to the holder. The pre-rendered qrCodeDataUrl does this for you.
What the wallet does
When the holder scans the QR code, the wallet:
- Parses the credential offer and discovers the issuer's metadata (supported credential types, endpoints, signing keys).
- Exchanges the pre-authorized code for an access token.
- Generates (or reuses) a key pair and sends a proof of possession with the credential request — this is what binds the credential to the holder and later surfaces as
holderBindingin the offer status. - Receives, validates, and stores the signed SD-JWT credential.
From your side, the observable effect is the offer gaining claimedAt and credentialId: its status moves from pending to claimed, or stays accepted if the holder accepted before issuance.
Key takeaways
PRE_AUTHORIZEDmeans you've already authenticated the user — issue offers only in authenticated contexts.- Offers expire — set
offerTtlSecondsto match your UX (default flows work well at 300 seconds). Anexpiredoffer can't be revived; create a new one. - One offer, one claim — poll
GET /offers/{offerId}until a terminal state.
Where to go next
- Issuance Integration Guide — the full production integration
- SD-JWT & Selective Disclosure — controlling disclosability at issuance time