OID4VCI Issuer Service
The OID4VCI Issuer service implements the OpenID for Verifiable Credential Issuance specification. It handles the server-side protocol for issuing verifiable credentials to holder wallets. The service supports SD-JWT, mDoc (ISO 18013-5), and JWT VC JSON credential formats, multiple grant types (authorization code and pre-authorized code), deferred issuance for asynchronous workflows, and batch issuance for requesting multiple credentials in one round trip. In practice it exposes both holder-facing OID4VCI endpoints and a separate issuer-integration surface used by issuer backend apps and web apps to create and manage issuance flows.
Holder (Wallet) Endpoints
| Method | Path | Description |
|---|---|---|
GET | /.well-known/openid-credential-issuer | Issuer metadata. Returns a JSON document describing the issuer's capabilities: supported credential types, endpoints, proof types, display information, and encryption settings. Wallets fetch this to discover how to interact with the issuer. |
GET | /credential-offers/{offerId} | Retrieve a credential offer by ID. When the issuer creates an offer, it generates a URI that points here. The wallet resolves the offer to learn which credentials are available and which grant types to use. |
POST | /nonce | Issue a c_nonce for proof generation. The wallet calls this to get a fresh nonce before creating the proof-of-possession JWT it includes in the credential request. |
POST | /credential | Handle credential requests. The wallet submits an access token and a proof-of-possession JWT. The service validates the token, verifies the proof, resolves credential attributes via registered CredentialAttributeContributor instances, and delegates to the appropriate CredentialFormatHandler to build the signed credential. |
POST | /deferred-credential | Handle deferred credential polling. When immediate issuance is not possible (pending approval, background checks), the issuer returns a transactionId from /credential. The wallet polls this endpoint with that ID until the credential is ready. |
POST | /notification | Receive holder notifications. After storing a credential, the wallet can notify the issuer whether it was accepted, rejected, or deleted. Processed idempotently, so duplicate deliveries from retries are safe. |
Issuer Integration Endpoints
These are the three issuer-facing endpoints used by issuer backend apps or web apps to initiate and manage issuance sessions:
| Method | Path | Description |
|---|---|---|
POST | /oid4vci/backend/credential/offers | Create a credential offer session. Accepts credential_configuration_ids, optional pre-seeded credential_subject_data, optional grants, issuer_id, correlation_id, deeplink scheme, QR code options, callback configuration, opaque state, and ttl_seconds. Returns the correlation_id, offer_uri, optional status_uri, optional QR data URI, and optional tx_code. |
GET | /oid4vci/backend/credential/offers/{correlationId} | Get credential offer session status for the issuer app. Returns credential_offer_created, credential_offer_retrieved, token_requested, credential_requested, credential_issued, or error, plus issuance data when a credential has been issued. |
DELETE | /oid4vci/backend/credential/offers/{correlationId} | Delete a credential offer session created through the issuer-facing backend API. Useful when an issuance flow is cancelled or should be cleaned up explicitly before TTL expiry. |
These issuer-integration endpoints are separate from the holder-facing OID4VCI spec endpoints. They are deployment-specific because they depend on your issuer UX and business process, while the holder-facing routes remain standardized for wallet interoperability.
Including in Your Server
dependencies {
implementation("com.sphereon.idk:services-oid4vci-issuer-rest:0.25.0")
}
Configuration
The issuer is configured through the issuer metadata DSL or YAML. There are two main approaches:
DSL approach: build issuer metadata programmatically in Kotlin:
val metadata = issuerMetadata("https://issuer.example.com") {
authorizationServer("https://auth.example.com")
credentialConfiguration("IdentityCredential", CredentialFormat.SD_JWT_DC) {
vct = "https://issuer.example.com/identity"
scope = "identity"
bindingMethod("jwk")
signingAlg(JwaAlgorithm.ES256)
proofType(ProofType.JWT, JwaAlgorithm.ES256)
display {
name = "Identity Credential"
locale = "en-US"
}
}
}
YAML approach: define everything in configuration files:
oid4vci:
issuer:
identifier: https://issuer.example.com
authorizationServers: https://auth.example.com
credentialConfigurationIds: IdentityCredential
credentials:
"[IdentityCredential]":
format: dc+sd-jwt
scope: identity
signingAlgorithms: ES256
bindingMethods: jwk
proofTypes:
jwt:
signingAlgorithms: ES256
display:
name: Identity Credential
locale: en-US
Additional configuration points include:
- Config provider: Choose between
ConfigDrivenOid4vciIssuerConfigProvider(reads from YAML/env vars) orDesignBackedOid4vciIssuerConfigProvider(reads from the credential design store). - Per-credential issuance policy: Control grant types, nonce TTL, deferred retry intervals, encryption requirements, and Interactive Authorization Exchange (IAE) settings per credential type.
- Extension points: Register custom
CredentialFormatHandlerimplementations for additional credential formats,CredentialAttributeContributorimplementations to supply claims from your business logic, and anOid4vciAuthorizationServerBridgeto connect to your OAuth2 infrastructure.
See OID4VCI Issuer for the full guide, including credential offer creation, attribute contributors, deferred handling, and signed metadata.
Docker
Each service ships with a Dockerfile and docker-compose configuration in its container/ directory.
Building the image
# Build the fat JAR first
./gradlew :services-oid4vci-issuer-rest:buildFatJar
# Build the Docker image
docker compose -f services/oid4vci-issuer/container/docker-compose.yaml build
Running with Docker Compose
docker compose -f services/oid4vci-issuer/container/docker-compose.yaml up
The service starts on port 8080. Configuration is loaded from container/config/ inside the image. Override settings via environment variables in a .env file next to the docker-compose.yaml.
Image details
| Property | Value |
|---|---|
| Base image | eclipse-temurin:21-jre |
| Docker image | sphereon/idk-oid4vci-issuer:latest |
| Exposed port | 8080 |
| Config location | /app/config/ |
Next Steps
- Services Overview for an introduction to all available IDK services and the
CommandBackedHttpAdapterpattern - OID4VCI Issuer Guide for the full guide on credential offer creation, issuer integration, attribute contributors, and deferred handling
- OID4VCI Holder for the wallet-side credential acquisition service
- OAuth2 Authorization Server for the authorization server that works alongside the issuer
- Ktor Integration for details on installing and configuring the
KotlinInjectPlugin