Inital merge of demo-relevant oid4vc features into main#978
Inital merge of demo-relevant oid4vc features into main#978Magnus-Kuhn wants to merge 20 commits intomainfrom
Conversation
… processor (#984) * chore: process token with verifiable presentation content * chore: run prettier * chore: add isTechnicallyValid param * chore: run audit fix * chore: pr comments
…ePresentation (#992) * chore: npm audit fix * test: displayInformation is transferred in PresentationToken * fix: explicitly hand over type and displayInformation when creating VerifiablePresentation
…993) * refactor: make VerifiablePresentation to TokenContentVerifiablePresentation * test: adjust expected type of token content * test: app-runtime test * chore: re-add TokenContentVerifiablePresentation * chore: type * refactor: add ProprietaryAttributeValue to barrel file
* feat: use token id as nonce * feat: use ephemeral for empty token * fix: use string * chore: npm audit fix * chore: exclude vulnerabilities --------- Co-authored-by: Timo Notheisen <timo.notheien@js-soft.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR merges a set of demo-focused OID4VC/OID4VP capabilities into the main codebase, including OpenID4VC consumption flows, a new request item type for sharing credential offers, runtime/app-runtime UI support, and test/docker tooling to exercise end-to-end demo scenarios.
Changes:
- Introduces an OpenID4VC consumption controller + runtime use-cases/facade for resolving/accepting authorization requests and creating presentation tokens.
- Adds
ShareCredentialOfferRequestItemend-to-end (content model, consumption processor + eventing, runtime DVO expansion, schemas). - Updates Jest/TS configs and adds dev docker-compose + assets to run OID4VC demo tests locally/CI, plus a patch-package fix for case-insensitive hostname matching in OID4VP.
Reviewed changes
Copilot reviewed 74 out of 76 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| patches/@openid4vc+openid4vp+0.4.0+001+initial.patch | Patch-package tweak: case-insensitive hostname comparison for OID4VP client id validation |
| packages/tsconfig.base.json | Enables skipLibCheck globally (flagged as PoC) |
| packages/runtime/test/tsconfig.json | Allows JS compilation in tests (ESM/CJS interop for Jest) |
| packages/runtime/test/lib/RuntimeServiceProvider.ts | Adds injectable fetchInstance for tests |
| packages/runtime/test/customMatchers.ts | Improves toBeSuccessful failure messages with error code + optional data |
| packages/runtime/test/consumption/openid4vc.test.ts | Adds OID4VC end-to-end tests using Docker Compose + Eudiplo |
| packages/runtime/src/useCases/consumption/openid4vc/index.ts | Exports new OID4VC consumption use-cases |
| packages/runtime/src/useCases/consumption/openid4vc/ResolveAuthorizationRequest.ts | Use-case to resolve OID4VP auth requests and map matching credentials |
| packages/runtime/src/useCases/consumption/openid4vc/CreatePresentationToken.ts | Use-case to create token content for verifiable presentation tokens |
| packages/runtime/src/useCases/consumption/openid4vc/AcceptAuthorizationRequest.ts | Use-case to submit an authorization request response using a selected credential |
| packages/runtime/src/useCases/consumption/index.ts | Re-exports new openid4vc use-cases |
| packages/runtime/src/useCases/common/Schemas.ts | Adds schemas for new requests/types (generated output) |
| packages/runtime/src/extensibility/facades/consumption/index.ts | Exports the new OpenId4VcFacade |
| packages/runtime/src/extensibility/facades/consumption/OpenId4VcFacade.ts | Facade for new OID4VC-related use-cases |
| packages/runtime/src/extensibility/ConsumptionServices.ts | Adds openId4Vc facade to consumption services |
| packages/runtime/src/events/EventProxy.ts | Proxies new “credential offer item processed” consumption event to runtime-types event |
| packages/runtime/src/dataViews/content/RequestItemDVOs.ts | Adds DVO type for ShareCredentialOfferRequestItem including credential responses |
| packages/runtime/src/dataViews/DataViewExpander.ts | Expands ShareCredentialOfferRequestItem by fetching/caching credential responses |
| packages/runtime/src/Runtime.ts | Binds OpenId4VcController into the IoC container |
| packages/runtime/package.json | Jest transforms updated; adds dependencies/devDependencies for OID4VC demo tooling |
| packages/runtime-types/src/events/consumption/index.ts | Exports new runtime-types consumption event |
| packages/runtime-types/src/events/consumption/ShareCredentialOfferRequestItemProcessedByRecipientEvent.ts | Adds runtime-types event for “credential offer request item processed” |
| packages/content/src/tokens/index.ts | Exports token content type for verifiable presentations |
| packages/content/src/tokens/TokenContentVerifiablePresentation.ts | Adds token content type used for verifiable presentation tokens |
| packages/content/src/requests/items/shareCredentialOffer/ShareCredentialOfferRequestItem.ts | Adds new request item type for sharing a credential offer URL |
| packages/content/src/requests/items/index.ts | Exports the new request item |
| packages/content/src/requests/RequestItem.ts | Adds the new request item to request item derivation unions/guards |
| packages/content/src/index.ts | Re-exports tokens module |
| packages/content/src/attributes/types/proprietary/index.ts | Re-exports ProprietaryAttributeValue from directory index |
| packages/content/src/attributes/types/index.ts | Exports new VerifiableCredential attribute value |
| packages/content/src/attributes/types/VerifiableCredential.ts | Adds VerifiableCredential attribute value type (for VC storage/display) |
| packages/content/src/attributes/RelationshipAttributeQuery.ts | Adjusts proprietary constants import path |
| packages/content/src/attributes/AttributeValueTypes.ts | Adds VerifiableCredential to AttributeValues unions + class registry |
| packages/consumption/test/tsconfig.json | Allows JS compilation in tests (ESM/CJS interop for Jest) |
| packages/consumption/src/modules/requests/outgoing/OutgoingRequestsController.ts | Publishes an event returned by item processors when applying incoming responses |
| packages/consumption/src/modules/requests/itemProcessors/shareCredentialOffer/ShareCredentialOfferRequestItemProcessor.ts | Implements request item validation, acceptance, storage, and event emission |
| packages/consumption/src/modules/requests/index.ts | Exports the new request item processor |
| packages/consumption/src/modules/requests/events/index.ts | Exports new consumption event |
| packages/consumption/src/modules/requests/events/ShareCredentialOfferRequestItemProcessedByRecipientEvent.ts | Adds consumption-layer event for processed credential offer item |
| packages/consumption/src/modules/openid4vc/local/RequestedCredentialCache.ts | Adds synchronized cache for credential responses keyed by offer URL |
| packages/consumption/src/modules/openid4vc/local/OpenId4VciCredentialResponseJSON.ts | Adds local JSON type for credential responses |
| packages/consumption/src/modules/openid4vc/local/KeyStorage.ts | Adds synchronized key storage for the local KMS backend |
| packages/consumption/src/modules/openid4vc/local/Holder.ts | Adds Credo-based holder agent wrapper (resolve offers, request/store creds, accept auth requests, create VP token content) |
| packages/consumption/src/modules/openid4vc/local/EnmeshedStorageService.ts | Storage bridge: stores resolved credentials as OwnIdentityAttributes; query/load mapping |
| packages/consumption/src/modules/openid4vc/local/EnmeshedHolderKeyManagmentService.ts | Adds custom KMS backend for signing/encryption (EdDSA/ES256 + ECDH-ES/AES-GCM) |
| packages/consumption/src/modules/openid4vc/local/EnmeshedHolderFileSystem.ts | Stub FileSystem implementation for Credo dependencies |
| packages/consumption/src/modules/openid4vc/local/BaseAgent.ts | Base agent wrapper configuring Credo dependencies (storage/KMS/ws/fetch) |
| packages/consumption/src/modules/openid4vc/index.ts | Exports the new openid4vc module/controller pieces |
| packages/consumption/src/modules/openid4vc/OpenId4VcController.ts | Adds consumption controller for OID4VC/OID4VP flows and credential matching |
| packages/consumption/src/modules/index.ts | Re-exports openid4vc module |
| packages/consumption/src/consumption/ConsumptionIds.ts | Adds ID helper for requested credential cache entries |
| packages/consumption/src/consumption/ConsumptionControllerName.ts | Adds controller name enum for OpenId4VcController |
| packages/consumption/src/consumption/ConsumptionController.ts | Initializes OpenId4VcController and registers new request item processor |
| packages/consumption/src/consumption/ConsumptionConfig.ts | Adds optional fetchInstance override |
| packages/consumption/package.json | Updates Jest transforms; adds OID4VC dependencies (but currently incomplete) |
| packages/app-runtime/test/tsconfig.json | Allows JS compilation in tests (ESM/CJS interop for Jest) |
| packages/app-runtime/test/runtime/AppStringProcessor.test.ts | Adds test for processing a token containing verifiable presentation content |
| packages/app-runtime/test/lib/MockUIBridge.ts | Adds UI bridge methods for showing VP tokens and resolved auth requests |
| packages/app-runtime/test/lib/MockUIBridge.matchers.ts | Adds matchers for new UI bridge calls (plus extra declared-but-missing matchers) |
| packages/app-runtime/test/lib/FakeUIBridge.ts | Adds stub methods for new UI bridge calls |
| packages/app-runtime/src/extensibility/ui/IUIBridge.ts | Extends UI bridge with VP display + resolved OID4VP request display |
| packages/app-runtime/src/AppStringProcessor.ts | Adds openid4vp: URL support + VP token content handling |
| packages/app-runtime/package.json | Updates Jest transforms/ignore patterns similar to other packages |
| package.json | Adds postinstall patch-package and docker scripts for OID4VC stack |
| .github/workflows/test.yml | Logs into GHCR before running tests to pull OID4VC demo images |
| .dev/oid4vc-service-config.json | Adds local OID4VC service config for the dev compose stack |
| .dev/eudiplo-assets/** | Adds Eudiplo demo assets (tenant config, keys, certs, logo, presentation/issuance configs) |
| .dev/compose.openid4vc.yml | Adds docker-compose stack for OID4VC service + connector + eudiplo + mongodb |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public override async init(): Promise<this> { | ||
| const keyCollection = await this.parent.accountController.getSynchronizedCollection("openid4vc-keys"); | ||
| const keyStorage = new KeyStorage(keyCollection, this._log); | ||
|
|
||
| this.holder = new Holder(keyStorage, this.parent.accountController, this.parent.attributes, this.fetchInstance); | ||
| await this.holder.initializeAgent("96213c3d7fc8d4d6754c7a0fd969598e"); | ||
|
|
||
| const requestedCredentialsCacheCollection = await this.parent.accountController.getSynchronizedCollection("openid4vc-requested-credentials-cache"); |
There was a problem hiding this comment.
A fixed private key string is hard-coded into initializeAgent(...). This effectively makes every runtime instance share the same long-lived key material and leaks it to anyone reading the source. Please load this from configuration/secure storage (or derive per-account keys) and fail initialization if no key material is provided.
| @@ -67,15 +85,20 @@ | |||
| "@nmshd/core-types": "*", | |||
| "@nmshd/iql": "^1.0.5", | |||
| "@nmshd/transport": "*", | |||
| "@noble/ciphers": "^2.0.1", | |||
| "jose": "^6.1.1", | |||
| "lodash": "^4.18.1", | |||
| "ts-simple-nameof": "^1.3.3" | |||
| "sjcl": "^1.0.8", | |||
| "ts-simple-nameof": "^1.3.3", | |||
| "ws": "^8.18.3" | |||
| }, | |||
| "devDependencies": { | |||
| "@js-soft/docdb-access-loki": "1.4.1", | |||
| "@js-soft/docdb-access-mongo": "1.4.1", | |||
| "@js-soft/node-logger": "1.2.4", | |||
| "@nmshd/crypto": "2.1.3", | |||
| "@types/lodash": "^4.17.24", | |||
| "@types/sjcl": "^1.0.34", | |||
| "ts-mockito": "^2.6.1" | |||
| }, | |||
There was a problem hiding this comment.
This file is used by the @nmshd/consumption runtime code, but elliptic is not listed in dependencies even though it is imported by EnmeshedHolderKeyManagmentService. Add elliptic (and move @nmshd/crypto from devDependencies to dependencies, since it is also imported at runtime) to avoid "module not found" for consumers installing @nmshd/consumption directly.
| const server = URL.parse("https://kc-openid4vc.is.enmeshed.eu/realms/enmeshed-openid4vci")!; | ||
| const clientId = "wallet"; | ||
| const config: client.Configuration = await client.discovery(server, clientId); | ||
| const grantReq = await client.genericGrantRequest(config, "password", { |
There was a problem hiding this comment.
URL.parse(...) is not part of the WHATWG URL API and will throw at runtime (and usually fails type-checking). Use new URL("…") (or import parse from node:url if you need legacy parsing) before passing the issuer URL to openid-client discovery.
| // the length correspondes to 50MB - maybe this needs to be restricted further in the future | ||
| if (string.length > 52428800) { |
There was a problem hiding this comment.
Typo in comment: "correspondes" should be "corresponds".
|
|
||
| this._settings = await new SettingsController(this).init(); | ||
|
|
||
| this._openId4Vc = await new OpenId4VcController(this).init(); |
There was a problem hiding this comment.
OpenId4VcController is initialized unconditionally during ConsumptionController.init(). Given that initialization spins up an agent, KMS backend, key storage collections, etc., this adds startup cost and risk for runtimes that don't need OID4VC. Consider guarding this behind an explicit feature flag in ConsumptionConfig (and/or lazy-initializing on first use).
| this._openId4Vc = await new OpenId4VcController(this).init(); | |
| const openId4VcEnabled = (this.consumptionConfig as ConsumptionConfig & { enableOpenId4Vc?: boolean }).enableOpenId4Vc !== false; | |
| if (openId4VcEnabled) { | |
| this._openId4Vc = await new OpenId4VcController(this).init(); | |
| } |
| @serialize() | ||
| @validate({ nullable: true }) | ||
| public type: string; | ||
|
|
There was a problem hiding this comment.
The type property is required by IVerifiableCredential/VerifiableCredentialJSON, but the validator allows it to be null (@validate({ nullable: true })). This can let invalid credential objects pass validation and diverges from the schema expectations. Consider removing nullable: true (or making type optional everywhere if it truly can be missing).
| try { | ||
| await this.consumptionController.openId4Vc.requestAllCredentialsFromCredentialOfferUrl(requestItem.credentialOfferUrl); | ||
| return ValidationResult.success(); | ||
| } catch (error) { | ||
| return ValidationResult.error( | ||
| ConsumptionCoreErrors.requests.invalidRequestItem(`The credential offer at URL '${requestItem.credentialOfferUrl}' could not be processed. Cause: ${error}`) | ||
| ); | ||
| } |
There was a problem hiding this comment.
The error message interpolates the caught error object directly (Cause: ${error}), which often results in [object Object] and may accidentally include sensitive data. Prefer extracting a safe message (e.g., error instanceof Error ? error.message : String(error)) and consider logging the full error separately.
| const result = await this.openId4VcController.acceptAuthorizationRequest(request.authorizationRequest, credential); | ||
| return Result.ok({ status: result.status, message: JSON.stringify(result.message) }); | ||
| } |
There was a problem hiding this comment.
JSON.stringify(result.message) will wrap an already-string message in quotes (e.g. "ok"), which is probably not what API consumers expect in AcceptAuthorizationRequestResponse.message. Consider only stringifying when result.message is not already a string.
| showResolvedCredentialOfferCalled(): R; | ||
| showResolvedCredentialOfferNotCalled(): R; |
There was a problem hiding this comment.
The Jest matcher typings declare showResolvedCredentialOfferCalled/NotCalled, but there are no corresponding implementations in expect.extend(...). This will compile but fail at runtime if used. Either implement these matchers or remove them from the Matchers interface until they exist.
| showResolvedCredentialOfferCalled(): R; | |
| showResolvedCredentialOfferNotCalled(): R; |
| @serialize() | ||
| @validate({ nullable: true }) | ||
| public type: string; | ||
|
|
There was a problem hiding this comment.
The type field is required by TokenContentVerifiablePresentationJSON, but the validator currently allows null (@validate({ nullable: true })). This can create tokens that serialize as invalid JSON for consumers expecting type: string. Consider making it non-nullable (or consistently optional across the type + schema).
Readiness checklist
Description
Transferred are
The presentation token is not transferred.