Skip to main content

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_offer parameter 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:

  1. Parses the credential offer and discovers the issuer's metadata (supported credential types, endpoints, signing keys).
  2. Exchanges the pre-authorized code for an access token.
  3. 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 holderBinding in the offer status.
  4. 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_AUTHORIZED means you've already authenticated the user — issue offers only in authenticated contexts.
  • Offers expire — set offerTtlSeconds to match your UX (default flows work well at 300 seconds). An expired offer can't be revived; create a new one.
  • One offer, one claim — poll GET /offers/{offerId} until a terminal state.

Where to go next​