Open ID for Verifiable Credentials (OID4VCI)
What it is
oid4vc-service is a protocol façade: it speaks the wallet-facing OpenID4VC protocols (OpenID4VCI 1.0 for issuance, OpenID4VP 1.0/draft-23 for presentation) on the outside, and delegates everything else to Sunbird RC's existing services on the inside:
credentials-service (
/credentials/issue,/credentials/verify) — builds and signs the verifiable credential, stores itidentity-service (
/utils/sign,/utils/sign-jwt,/utils/sign-sd-jwt,/utils/sign-mdoc,/did/resolve,/.well-known/jwks.json) — all key operations; keys stay in Vaultcredential-schema (
/credential-schema/oid4vci-configs) — drives the published issuer metadata
oid4vc-service itself holds no credential keys and no credential storage — only its own short-lived session state and (optionally) its own OAuth/protocol signing key, delegated to identity-service.
Supported credential formats
Chosen per credential type via the schema's oid4vciConfig.oid4vciFormats:
ldp_vc
/utils/sign (Ed25519 linked-data proof)
No
W3C credentialSubject
jwt_vc_json
/utils/sign-jwt (ES256 JWS, W3C VC-JWT convention)
No
W3C credentialSubject, nested under a vc claim
vc+sd-jwt
/utils/sign-sd-jwt (ES256, IETF SD-JWT VC)
Yes
Flat top-level claims, digests in _sd
mso_mdoc
/utils/sign-mdoc (ES256 COSE_Sign1, ISO/IEC 18013-5)
Yes (per-element)
{namespace: {elementIdentifier: value}}
Credentials can also carry a W3C VC Render Method entry (inline SVG or a hosted URL) for visual rendering by wallets.
OpenID4VCI — credential issuance flow
Discovery — the wallet calls
GET /.well-known/openid-credential-issuer;oid4vc-servicelooks up opted-in schemas live from credential-schema and returns onecredential_configurations_supportedentry per<schemaId>_<format>combination.Offer creation (issuer-side) — the registry or another issuer system calls
POST /oid4vc/offerwith{credential_configuration_id, format, claims}and gets back{offer_id, credential_offer_uri, credential_offer, qr_data}.Wallet dereferences the offer —
GET /oid4vc/offer/:idreturns thecredential_offer, including a pre-authorized_code grant.Token exchange — the wallet calls
POST /oid4vc/tokenwith the pre-authorized code (single-use — a replay returns400 invalid_grant) and receives a short-lived access token plus a single-usec_nonce.Credential request (proof-of-possession) — the wallet calls
POST /oid4vc/credentialwith a PoP JWT proving control of its holder key (DID-bound viakid, or an inlinejwk/did:jwk).oid4vc-serviceverifies the proof, then delegates to credentials-service (which delegates the actual signing to identity-service) and returns the signed credential.
OpenID4VP — presentation verification flow
Verifier creates a request —
POST /vp/requestwith a DCQL query returns{transaction_id, request_uri, qr_data}. By default the request object is a signed JAR (client_idis adid:...); an unsigned mode and a legacy pre-draft-22 shape are available for wallets on older drafts (see Configuration below).Wallet fetches and answers —
GET /vp/request-object/:idreturns the request (JWS or plain JSON depending on mode); the wallet responds viaPOST /vp/response(direct_post) with itsvp_token.Verification chain —
oid4vc-servicechecks, in order: holder/device signature, nonce/session-transcript freshness, each embedded credential's signature and revocation status, holder binding, and finally DCQL satisfaction against the disclosed claims.Verifier polls the result —
GET /vp/status/:idreturns{verified, checks: {holderSignature, nonce, credentialSignatures, holderBinding, revocation, dcql}, claims, holderDid}. All six checks reportingOKis the full proof that the presentation is genuine, unrevoked, holder-bound, and satisfies the verifier's query.
Key configuration
PUBLIC_URL
http://localhost:3400
must be the externally-reachable HTTPS URL in production — embedded in minted tokens and request objects
ISSUER_DID
(auto-generated on boot if blank)
fallback issuer DID; should be pinned in production
OID4VP_ENABLED
true
set false to run issuance-only
DRAFT13_COMPAT_MODE
false
emit OpenID4VCI draft-13 shapes for older wallet implementations
OID4VP_SIGN_REQUEST
true
sign OID4VP request objects as a JAR; requires a wallet-resolvable VERIFIER_DID (a did:web, not a did:rcw)
OID4VP_LEGACY_CLIENT_ID_SCHEME
false
emit the pre-draft-22 unsigned request shape for wallets that don't parse the prefixed client_id convention
SESSION_STORE
memory
set to redis for any multi-replica deployment
ENABLE_AUTH
false
require a Keycloak bearer token on POST /oid4vc/offer (the only internal, non-wallet-facing route)
See services/oid4vc-service/README.md §4 in the sunbird-rc-core repository for the complete reference, including TTL settings, the Keycloak auth setup, and per-schema issuer DID behavior.
Getting started
For the full local deployment walkthrough (including Apple Silicon build notes), manual testing guide, and production hardening checklist, see services/oid4vc-service/README.md §5–§8 in the repository.
Verified wallet interop
Beyond the self-driven test harness (all four formats issue and verify end-to-end with a verified: true result), jwt_vc_json and vc+sd-jwt issuance and presentation have been confirmed against independent third-party wallet implementations during interop testing.
Last updated