Skip to main content
Version: v0.25.0 (Latest)

OpenID for Verifiable Credential Issuance

OpenID for Verifiable Credential Issuance (OID4VCI) is a protocol that enables the issuance of verifiable credentials from a credential issuer to a holder's wallet. The IDK implements OID4VCI with support for both the pre-authorized code flow and the authorization code flow, covering immediate and deferred credential issuance.

What is OID4VCI?

Where OID4VP handles presenting credentials to a verifier, OID4VCI handles the other side of the lifecycle: obtaining credentials in the first place. OID4VCI extends OAuth 2.0 with a credential issuance layer, allowing issuers to publish what credentials they offer through discoverable metadata, and wallets to request those credentials through a standardized protocol.

The protocol supports multiple credential formats, proof-of-possession mechanisms, and grant types, making it suitable for a wide range of issuance scenarios from government-issued identity documents to organizational membership cards.

The protocol uses the same role terminology as the broader verifiable credentials ecosystem:

RoleDescription
IssuerThe entity that creates and signs verifiable credentials
Holder (Wallet)The entity that requests, receives, and stores credentials
Authorization ServerThe OAuth 2.0 server that authenticates the holder and issues access tokens

Together with OID4VP, this creates the complete credential lifecycle: IssuerHolderVerifier.

Protocol Flow

The most common OID4VCI flow uses a pre-authorized code, where the issuer initiates the process by creating a credential offer:

OID4VCI Pre-Authorized Code Flow

The flow begins when the issuer creates a credential offer and delivers it to the wallet, typically via a QR code or deep link. The wallet fetches the issuer's metadata to understand what credentials are available and how to request them, then exchanges the pre-authorized code for an access token. With the token, the wallet creates a proof of possession and requests the credential. The issuer responds with the credential immediately or provides a transaction ID for deferred retrieval.

Grant Types

OID4VCI supports two OAuth 2.0 grant types for obtaining access tokens:

Pre-Authorized Code

The issuer initiates the flow by generating a credential offer that includes a pre-authorized code. This is useful when the issuer has already authenticated the holder through an out-of-band process (e.g., in-person verification, existing account login). The offer may optionally require a transaction code (PIN) as an additional security measure.

Authorization Code

The wallet directs the holder to the authorization server for authentication. This follows the standard OAuth 2.0 authorization code flow with PKCE, making it suitable for scenarios where the holder needs to authenticate directly with the authorization server. The wallet builds an authorization URL, the user authenticates, and the wallet exchanges the resulting code for an access token.

Interactive Authorization Exchange (IAE)

IAE is an OID4VCI 1.1 extension that adds an interactive verification step to the authorization process. Before issuing an authorization code, the authorization server can challenge the holder to perform an additional action, typically presenting an existing credential via OID4VP.

This enables a "present-to-obtain" pattern: for example, a holder might need to present their national ID credential to receive a university degree credential. The issuer configures which interaction type and DCQL query to use per credential configuration. See the Holder and Issuer documentation for implementation details.

Architecture

The IDK's OID4VCI implementation is split into three modules:

OID4VCI Architecture

The Holder module provides wallet-side functionality: parsing credential offers, exchanging tokens, creating proofs, requesting credentials, and handling deferred issuance with automatic polling.

The Issuer module provides server-side functionality: creating credential offers, publishing metadata, issuing nonces, handling credential requests, and managing deferred credentials. It uses an extensible format handler system to support multiple credential formats and can be configured through a Kotlin DSL or external configuration (YAML/environment variables).

The Common module contains shared data models and the metadata DSL used by both sides: credential offers, requests, responses, issuer metadata definitions, and DSL builders for constructing metadata programmatically.

Credential Formats

The IDK supports multiple credential formats within OID4VCI:

FormatDescription
jwt_vc_jsonJWT-encoded W3C Verifiable Credentials
mso_mdocISO/IEC 18013-5 mobile document format
dc+sd-jwtSD-JWT based verifiable credentials with selective disclosure

The issuer declares supported formats in its metadata, and the holder specifies the desired format when requesting a credential. Each format has its own CredentialFormatHandler on the issuer side, making it straightforward to add support for additional formats.

Proof of Possession

When requesting a credential, the holder must prove it controls the key that the credential should be bound to. OID4VCI supports several proof types:

Proof TypeDescription
jwtJWT signed with the holder's key, containing the issuer URL and a nonce
cwtCBOR Web Token proof (for constrained environments)
attestationAttestation-based proof from a trusted entity

The proof includes a c_nonce (credential nonce) provided by the issuer, which prevents replay attacks and ensures freshness. The IDK's holder automatically manages nonce acquisition when autoRequestNonce is enabled.

Deferred Issuance

Not all credentials can be issued immediately. The issuer may need time for background checks, manual approval, or asynchronous signing. OID4VCI handles this through deferred issuance:

  1. The issuer returns a transaction_id instead of a credential
  2. The holder polls the deferred credential endpoint at a configured interval
  3. When the credential is ready, the issuer returns it in the polling response

The IDK holder includes a built-in polling orchestrator with configurable retry intervals and maximum attempts, so wallet developers don't need to implement polling logic manually.

Notifications

After receiving a credential, the holder can notify the issuer about the outcome. This is useful for the issuer to track issuance success rates and handle failure cases:

EventDescription
credential_acceptedThe holder successfully stored the credential
credential_failureThe holder encountered an error processing the credential
credential_deletedThe holder deleted the credential from their wallet

Security Features

OID4VCI in the IDK includes several security mechanisms:

Proof of Possession ensures the credential is bound to a key the holder controls. The issuer validates the proof signature and nonce before issuing the credential.

Transaction Codes provide an additional layer of security for pre-authorized code flows. The issuer can require the holder to enter a PIN or code received through a separate channel.

Signed Issuer Metadata allows the issuer to sign its metadata as a JWT, enabling holders to verify the metadata hasn't been tampered with.

DPoP (Demonstrating Proof-of-Possession) binds access tokens to the holder's key, preventing token theft and replay.

Credential Response Encryption allows the holder to request that the credential response be encrypted, protecting the credential in transit.

Core Components

// Holder-side component for wallet operations
val holder: Oid4vciHolderService = session.graph.oid4vciHolderService

// Issuer-side component for credential provider operations
val issuer: Oid4vciIssuerService = session.graph.oid4vciIssuerService

Next Steps