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

Verifier Container

sphereon/enterprise-verifier is the OpenID4VP verifier. It accepts presentation requests from relying-party-side callers, hosts the protocol endpoints the wallet interacts with, validates incoming presentations against per-tenant trust frames, and fires callbacks to tenant-configured webhooks when a presentation completes.

Like the issuer, the verifier inherits the protocol layer from the IDK and adds the EDK layers around it: the DCQL versioned store with multi-binding per verifier, per-tenant trust-frame binding over the IDK trust engine, presentation session admin, and the same webhook infrastructure the issuer uses.

Verifier container layout

Public Protocol Surface

The wallet-facing endpoints are the OID4VP set plus the universal-verifier integration:

  • Oid4vpVerifierHttpAdapter: request_uri, direct_post, ready. Standard OID4VP protocol mechanics.
  • UniversalOid4vpHttpAdapter: the EDK universal-verifier surface for create-auth-request, returning the request URI for the wallet to fetch, and the response surface.
  • TenantAwareOid4vpVerifierHttpAdapter: the tenant-aware adapter contributed by lib-openid-oid4vp-verifier-rest-tenant. Mounts the verifier under /{tenant-slug}/oid4vp/... and consults the public-endpoint binding for outbound URLs.

The IDK verifier descriptor provider publishes a TenantPathPolicy.LeadingSlug(maxDepth = 1), which is what makes the /{tenant-slug}/oid4vp/... path form route into the verifier through catalog dispatch.

The integration with the OID4VP authorization-server bridge (Oid4vpAuthHttpAdapter) lets a tenant use OID4VP as the authentication step for an OAuth flow. This is the same wallet-as-authenticator pattern the EDK auth bridge implements, but exposed at the verifier rather than as a separate service.

Internal Admin Surface

The verifier admin REST under /api/v1/... covers:

  • Per-tenant verifier configuration. /api/v1/verifier/oid4vp is the typed CRUD for the verifier's per-tenant settings: display metadata, response_uri base, signing key alias for request objects, supported credential types.
  • DCQL versioned store. Two admin surfaces. DcqlQueryAdminHttpAdapter (from the IDK) handles the latest-version-only CRUD for development convenience. DcqlQueryVersionHttpAdapter (the EDK versioned overlay) handles versioned authoring: a header is stable, each save produces a new version, previous versions stay resolvable. A single verifier can have multiple DCQL queries bound concurrently and route between them by query id.
  • Per-tenant trust-frame admin. /api/v1/verifier/oid4vp/integrations/trust is the per-tenant CRUD over trust sources. Each trust source binding identifies a kind (X.509, OIDF, ETSI TSL, DID) and a source (PEM bundle, federation endpoint, TSL URL). The operational trust engine itself ships in the IDK trust modules (lib-trust-core-impl, lib-trust-x509, lib-trust-did, lib-trust-etsi, lib-trust-oidfed); the verifier overlay is a per-tenant binding layer over it.
  • Webhook configuration. Same Phase-4b webhook subsystem the issuer uses, configured for verifier event types (presentation status, callback success, callback failure).
  • Presentation session admin. List, inspect, and cancel presentation sessions for debugging. Adapts the existing generic session persistence at lib-data-store-session/persistence-postgresql rather than creating a new session table family.
  • Tenant admin. Shared TenantAdminHttpAdapter for tenant CRUD, domain registration, public-endpoint bindings.

For the protocol-level reference and the DCQL authoring guide, see the OpenID4VP section: overview, integration, DCQL store, DCQL REST API, DCQL authoring, verifier bindings.

Tenant-Aware URL Generation

The lib-openid-oid4vp-verifier-rest-tenant overlay replaces the IDK default Universal OID4VP create-auth-request command with an EDK implementation that consults tenant_public_endpoint. The effect:

  • request_uri_base in a returned authorization request points at the tenant's public OID4VP base.
  • For signed request objects using direct_post, the response_uri is the tenant's public OID4VP response endpoint.
  • status_uri in the returned status is rooted at the tenant's public OID4VP backend base.

The fail-closed default applies: if no tenant_public_endpoint binding for OID4VP_VERIFIER is configured for the resolved tenant, the verifier refuses to generate URLs against the request host. Tenant administrators set the binding through the tenant admin REST before enabling the verifier for live wallet traffic.

Trust Frames

A trust frame describes what credentials a verifier is willing to accept for a given verification job. Trust sources within a frame can be:

  • X.509. A PEM bundle of trust anchors. The IDK X.509 trust engine validates the certificate chain on incoming presentations against this set.
  • DID. A list of trusted issuer DIDs. The IDK DID trust engine resolves the credential issuer's DID, walks the verification methods, and matches against the trusted set.
  • ETSI TSL. A trusted list URL conforming to the ETSI Trust Service List format. The engine fetches and refreshes the TSL on the configured schedule and validates issuers against entries in the list.
  • OIDF. An OpenID Federation entity statement chain. The engine walks the federation and validates issuers against the federation policy.

Frames compose: a verifier can use multiple trust sources in one frame, and any source's positive result is sufficient unless the per-tenant policy requires more specific behaviour.

The trust admin REST is a CRUD over the per-tenant binding. The underlying trust engine and trust-anchor refresh logic ship in the IDK; the verifier container only adds the per-tenant configuration and the integration with the verifier's presentation validation path.

DCQL Versioning

DCQL queries are the verifier's declaration of what it wants the wallet to present. Each query has a stable header (an identifier and a label) and a sequence of versions (each carrying the query body itself). When a verifier evolves its DCQL query, it produces a new version against the same header; previous versions stay resolvable so in-flight presentations against the previous version still validate.

The header lives in dcql_query_header. Versions live in dcql_query_version. The DcqlQueryVersionHttpAdapter allows clients to fetch the current version, fetch by version id, list versions, and create a new version against an existing header. The Postgres impl ships in lib-openid-oid4vp-dcql-store-versioned-persistence-postgresql.

A verifier can also bind multiple distinct DCQL queries simultaneously, addressed by the query header id. Different presentation flows can each reference the appropriate query.

Signing Keys

The verifier's request-object signing key alias defaults to (tenant, verifier, request-object-signing) on the KMS. Used when the verifier signs the authorization request object for request_uri flows.

The webhook signing key alias defaults to (tenant, verifier, webhook-signing). Used to HMAC outbound webhook calls so consumers can verify authenticity.

Both aliases are per-tenant; the tenant admin can override either through the signing-key admin REST.

Persistence

The verifier overlay adds the following Postgres tables:

  • The DCQL versioned store tables (dcql_query_header, dcql_query_version).
  • The integration registry tables (shared with the issuer).
  • The webhook configuration and dispatch queue tables (shared with the issuer).
  • The OID4VP-as-OAuth session store (PostgreSqlOid4vpAuthSessionRepository), backed through DatabaseOid4vpAuthSessionStore.
  • The presentation session admin uses the existing generic session persistence at lib-data-store-session/persistence-postgresql.

Building and Running

The Dockerfile follows the standard EDK pattern. The fat JAR is built from services-oid4vp-verifier-rest with ./gradlew :services-oid4vp-verifier-rest:buildFatJar; runtime image is eclipse-temurin:21-jre with appuser (uid 10001) on port 8080.

The entry point starts EnterpriseOid4vpVerifierKtorServer, which installs the tenant resolution plugin first and then mounts the universal HTTP adapters. The Metro graph for the enterprise verifier composes in services/oid4vp-verifier/rest/ and pulls in lib-openid-oid4vp-verifier-rest-tenant, services-tenant-rest, lib-tenant-resolution-impl, lib-tenant-persistence-postgresql, the DCQL versioned store with its Postgres impl, the IDK trust engine modules (lib-trust-core-impl, lib-trust-x509), and the core events implementation that the session emitter uses to dispatch callbacks.

A production deployment overrides the shipped application.yaml to require admin-scoped bearer JWTs on /api/v1/..., points at the shared Postgres, registers the KMS endpoint, registers trust sources per tenant, and binds tenant public endpoints through the tenant admin REST.

Operational Notes

  • Trust source refresh. ETSI TSL and OIDF federation entries refresh on a schedule. The default schedule is conservative; high-throughput deployments tune it through the per-tenant trust-source admin or via global configuration. A refresh failure does not invalidate the cached trust set, the engine continues to validate against the last successful refresh until it succeeds again.
  • Presentation session retention. Successful presentation sessions are retained for the audit window (configurable per tenant), then archived. The presentation-session.list admin endpoint reads from the live table; archived sessions are accessible through the audit pipeline.
  • Multi-DCQL routing. When a verifier is bound to multiple DCQL queries, the create-auth-request call carries the query header id. The dispatch logic looks up the current version of that header and embeds the query body in the authorization request.
  • Webhook reliability. Same Phase-4b dispatcher as the issuer. The verifier emits presentation-completed and presentation-failed event types; tenants subscribe through the webhook admin REST.