OpenID for Verifiable Credential Issuance (OpenID4VCI)
The IDK already implements the OpenID4VCI issuer: the metadata DSL, the credential offer endpoints, the protocol handlers for /credential, /credential_deferred, /nonce, /notification, the DI graph wiring, and the in-memory stores you need to run a self-contained issuer. The full reference is in the IDK OpenID4VCI issuer guide; the protocol primitives, the DSL, and the basic handler extension points all live there and are unchanged in the EDK.
What changes in the EDK is how the issuer gets the claim values it puts into a credential. In the IDK, that's an extension point: you write a custom attribute contributor and wire it in. In the EDK, attribute sourcing is a first-class subsystem with a phase model, a pluggable source SPI, an out-of-the-box catalogue of source implementations (database lookups, HTTP enrichment, identity resolution, wallet-presentation passthrough, OIDC userinfo, CSV tabular data, an encrypted vault for retained attributes), an explicit per-credential session, deferral and approval gates, an async-callback ingress for slow upstream systems, and persistent encrypted session storage. The result is that a typical EDK issuer deployment is built by composing existing attribute sources into a PipelineConfiguration rather than by writing custom Kotlin code.
This section covers those EDK additions. Protocol-level material (offers, the metadata document, the credential request/response shapes, the proof types) stays in the IDK guide.
The Mental Model
An EDK issuance is an IssuancePipelineSession. The session carries an attribute bag (the resolved attributes that will become credential claims), a lookup-key set (correlation tokens like email or employee_id that sources use to fetch attributes), and a status that tracks where in the lifecycle the session is.
The session passes through one or more phases. A phase corresponds to a moment in the protocol flow: authorization, pre-authorized-code dispatch, token exchange, the credential request, deferred polling, post-issuance. At each phase, the EDK runs every attribute source bound to that phase. A source reads from the bag and the lookup keys, optionally calls something outside (a database, an HTTP API, a vault), and contributes attributes and/or new lookup keys back into the session.
Sources within a phase are not run in declaration order. The EDK plans them by lookup-key dependency: a source that produces identity_id runs before sources that consume it. Sources at the same level run in parallel. When the phase finishes, the bag has grown with everything the bound sources added.
When the wallet calls /credential, the EDK has the full assembled bag, applies the per-credential claim mapping, and hands the result to the IDK protocol handler to sign and return. Sources that could not answer synchronously (an upstream HR system that takes 30 seconds) defer: the wallet gets a 202 Accepted with a deferred-credential transaction, the upstream system answers later via a callback URL, and the next /credential_deferred poll completes synchronously.
What the EDK Adds
A registered PipelineConfiguration per issuance flow. It declares which attribute sources contribute in which phases, what lookup keys they consume and produce, whether each source is required, how long the engine may wait for an async source within the synchronous /credential window, and which credentials the pipeline can assemble. See Attribute Pipeline.
An out-of-the-box catalogue of attribute sources. The EDK ships sources for: invitation context, wallet OID4VP presentations used as authentication, OIDC userinfo claims, internal identity resolution, internal database lookups, external HTTP enrichment, CSV tabular data, encrypted attribute vault reads, and VCALM credential-subject passthrough. Each one is a Metro-DI-contributed AttributeSource that the engine discovers automatically; the per-pipeline AttributeSourceBinding chooses which ones run when. Full reference in Attribute Sources.
Pipeline session management endpoints. The EDK adds REST endpoints under /oid4vci/sessions/... for initialising a session, contributing attributes from outside the protocol path, evaluating completeness, approving issuance, marking a source failed, and reading the current attribute state for an admin UI. The IDK protocol endpoints (/oid4vci/credential, /oid4vci/deferredCredential, /oid4vci/notification) are unchanged. See REST API.
How attributes reach the issuer. There are two attribute-provisioning surfaces, and most integrations use both. Mid-flow contribution through POST /api/v1/oid4vci/sessions/{correlationId}/attributes (and its callback twin) pushes attributes after a session is already running, typically when an upstream system answers a /credential request. Seed-at-offer-time provisioning through POST /api/v1/oid4vci/offers carries a preSeededGroups field with the identical group shape, so an issuer can create the offer and supply attributes in one round-trip when the data is already known up front. Both surfaces accept the same compact body of one-or-more groups, where each group factors out a shared sourceId / phase / retention once for every attribute it contains. The recommended notation links each group to a semantic attribute set by bundleId: the integrator passes term-name → value pairs and the server derives the per-attribute path, value kind, data classification, legal basis, and retention from the resolved SemanticAttributeDefinition. Raw JSON-Pointer paths remain available as an escape hatch when no bundle exists for the data being contributed. See Driving attribute provisioning from a bundle and the REST API for the wire format and the precedence rules between group-level, set-level, and per-item overrides.
An async-callback ingress. When a source is configured with callbackStyle = ASYNC_CALLBACK and a syncWaitWindow, the EDK dispatches the source's outbound request, holds the /credential response open for the configured window, and either completes synchronously when the callback arrives in time or falls through to a deferred response. The callback endpoint at /oid4vci/sessions/{correlationId}/callbacks/{callbackToken} is secured by an opaque capability token minted at dispatch time and validated through the EDK CallbackTokenService.
Deferral and approval gates. Each credential binding carries a DeferralPolicy that controls whether issuance may be deferred when required attributes are not yet present, and whether an explicit approval is required even when all attributes are present. The session statuses AWAITING_DEFERRED and AWAITING_APPROVAL make these gates observable separately from the issuer-core's DEFERRED state. See Attribute Pipeline.
Encrypted session persistence. Sessions carry sensitive data (the resolved attributes, the lookup keys, the approval evidence). The EDK provides three encryption modes (PlaintextMode for dev, PlatformEncryptedMode AEAD under a tenant KEK, ClientBoundMode additionally bound to the session's correlationId) and pluggable session stores. See Persistence.
Tenant-aware verifier paths. The IDK protocol endpoints can optionally be reached via a leading tenant slug (/{tenantSlug}/oid4vci/...) through the EDK rest-tenant module. The IDK host-based tenant resolution still works; the slug path is an additive option.
How a Developer Builds an Issuer
In the EDK, building an OID4VCI issuer is a declarative exercise more than a coding one:
-
Define your credential designs (the credential design system is the source of truth for what credentials your issuer offers and the claim policy each one carries). The OID4VCI issuer reads designs to build the
credential_configurations_supportedmetadata at startup. -
Pick the attribute sources that supply your claim values. For each one, configure it under its config prefix and decide which phases it runs in. A typical "employee credential" pipeline might use
invitation-context(SESSION_INIT) to pre-seed anemployee_id,auth-session-claim(AUTHORIZATION) to pick up the authenticated user's email,identity-resolver(TOKEN) to resolve email to an internal identity,database(CREDENTIAL_REQUEST) to read the HR record for that identity, andvault(POST_ISSUANCE) to retain the assembled attributes for re-issuance. -
Register the
PipelineConfigurationwith the engine. The configuration lists the source bindings (phase + required/optional + sync window), the credential bindings (which OID4VCI configurations the pipeline can produce), and the initial lookup keys the caller is expected to supply atInit. -
The IDK protocol handlers run the pipeline phases automatically at the right protocol moments. There is no per-flow integration code to write.
You only write code when you need a new source type (an SPI implementation of AttributeSource), or when you need a custom claim mapper (the CredentialClaimsAssembler extension point on the IDK side). For everything else, the work is configuration plus pipeline composition.
Where Things Live
| Concern | Module |
|---|---|
| Issuer protocol service, DSL, handlers | com.sphereon.idk:lib-openid-oid4vci-issuer-public / -impl (IDK) |
| Issuer REST endpoints (protocol + pipeline) | com.sphereon.idk:services-oid4vci-issuer-rest (IDK) |
| Pipeline session and command types (Init, Contribute, Approve, ...) | com.sphereon.idk:lib-credential-issuance-pipeline-public (IDK) |
| Attribute and source SPI types (AttributeBag, AttributeSource, LookupKey, PipelinePhase) | com.sphereon.idk:lib-attribute-flow-public and lib-attribute-pipeline-public (IDK) |
| Pipeline engine + command implementations | com.sphereon.edk:lib-credential-issuance-pipeline-impl |
PipelineEngine, DAG planner, base sources (config, rest-api) | com.sphereon.edk:lib-attribute-pipeline-impl |
| Built-in attribute sources | com.sphereon.edk:lib-attribute-pipeline-source-* (one module per source) |
| Tenant-aware path adapter | com.sphereon.edk:lib-openid-oid4vci-issuer-rest-tenant |
| Service contracts (compliance, resource targets, config keys) | com.sphereon.edk:lib-openid-oid4vci-issuer-contract |
| Deployable container | com.sphereon.edk:services-oid4vci-issuer |
Next Steps
- Attribute Pipeline: phases, the source binding model, session lifecycle, deferral, approval
- Attribute Sources: catalogue of every built-in source with configuration and when to use each
- REST API: the pipeline session and async-callback endpoints
- Persistence: the session store and the three encryption modes
- IDK OpenID4VCI Issuer Guide: the protocol layer, metadata DSL, offers, the handler extension points