Openid4vc initial merge#1038
Conversation
…ntime into poc-credo-holder-in-app-runtime
There was a problem hiding this comment.
Pull request overview
This PR introduces an initial OpenID4VC (OID4VCI/OID4VP) integration across the monorepo, wiring new consumption controllers, runtime use cases/facades, request item types, app-runtime URL handling, and end-to-end tests using an Eudiplo test container.
Changes:
- Add OpenID4VC consumption module (holder agent, key management/storage, credential offer & authorization request flows) and expose it via runtime use cases/facades.
- Extend content/runtime schemas with VerifiableCredential + new request items (ShareCredentialOffer / ShareAuthorizationRequest) and related DVO expansion/events.
- Update test tooling (Jest transforms for ESM deps, allowJs), add integration tests + CI steps to pull GHCR images, and apply a patch-package fix.
Reviewed changes
Copilot reviewed 88 out of 90 changed files in this pull request and generated 18 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.eslint.json | Include local dev directory in ESLint TS project. |
| patches/@openid4vc+openid4vp+0.4.0+001+initial.patch | Patch openid4vp hostname comparison to be case-insensitive. |
| packages/tsconfig.base.json | Adjust shared TS compiler options (incl. skipLibCheck). |
| packages/runtime/test/tsconfig.json | Allow JS compilation for Jest/ESM handling. |
| packages/runtime/test/lib/RuntimeServiceProvider.ts | Inject optional fetch implementation into test runtime. |
| packages/runtime/test/customMatchers.ts | Improve failure messages for Result matcher. |
| packages/runtime/test/consumption/openid4vc.test.ts | Add OID4VC issuance/presentation integration tests (Eudiplo). |
| packages/runtime/src/useCases/consumption/openid4vc/index.ts | Export OpenID4VC consumption use cases. |
| packages/runtime/src/useCases/consumption/openid4vc/VerifyPresentationToken.ts | New use case: verify presentation token content. |
| packages/runtime/src/useCases/consumption/openid4vc/StoreCredentials.ts | New use case: store issued credentials. |
| packages/runtime/src/useCases/consumption/openid4vc/ResolveCredentialOffer.ts | New use case: resolve credential offer URL. |
| packages/runtime/src/useCases/consumption/openid4vc/ResolveAuthorizationRequest.ts | New use case: resolve OID4VP authorization request URL. |
| packages/runtime/src/useCases/consumption/openid4vc/RequestCredentials.ts | New use case: request credentials (pin/access token). |
| packages/runtime/src/useCases/consumption/openid4vc/CreatePresentationToken.ts | New use case: create verifiable presentation token via TokenController. |
| packages/runtime/src/useCases/consumption/openid4vc/AcceptAuthorizationRequest.ts | New use case: submit OID4VP presentation. |
| packages/runtime/src/useCases/consumption/index.ts | Export openid4vc use cases at consumption level. |
| packages/runtime/src/useCases/common/UseCase.ts | Map OAuth2 client errors into runtime ApplicationError codes. |
| packages/runtime/src/useCases/common/Schemas.ts | Regenerate schemas to include new requests/items/types. |
| packages/runtime/src/extensibility/facades/consumption/index.ts | Export new OpenId4VcFacade. |
| packages/runtime/src/extensibility/facades/consumption/OpenId4VcFacade.ts | Facade exposing OpenID4VC use cases to consumers. |
| packages/runtime/src/extensibility/ConsumptionServices.ts | Add openId4Vc facade to consumption services. |
| packages/runtime/src/events/EventProxy.ts | Proxy new ShareCredentialOffer processed event into runtime-types event bus. |
| packages/runtime/src/dataViews/content/RequestItemDVOs.ts | Add DVO interfaces for new request items. |
| packages/runtime/src/dataViews/DataViewExpander.ts | Expand new request items (credential offer + authorization request). |
| packages/runtime/src/Runtime.ts | Bind OpenId4VcController in IoC container. |
| packages/runtime/package.json | Update Jest transforms + add OID4VC-related deps/devDeps. |
| packages/runtime-types/src/events/consumption/index.ts | Export new consumption event type. |
| packages/runtime-types/src/events/consumption/ShareCredentialOfferRequestItemProcessedByRecipientEvent.ts | Add runtime-types event DTO for processed credential-offer item. |
| packages/runtime-types/src/dtos/consumption/index.ts | Export new consumption DTO. |
| packages/runtime-types/src/dtos/consumption/CredentialOfferDTO.ts | Add DTO for credential offer URL. |
| packages/content/src/tokens/index.ts | Export token content type(s). |
| packages/content/src/tokens/TokenContentVerifiablePresentation.ts | Add token content for verifiable presentation. |
| packages/content/src/requests/items/shareCredentialOffer/ShareCredentialOfferRequestItem.ts | Add request item type for sharing credential offers. |
| packages/content/src/requests/items/shareAuthorizationRequest/ShareAuthorizationRequestRequestItem.ts | Add request item type for sharing auth requests. |
| packages/content/src/requests/items/index.ts | Export new request item implementations. |
| packages/content/src/requests/RequestItem.ts | Register new request item derivations/type guards. |
| packages/content/src/index.ts | Export tokens module from content package. |
| packages/content/src/attributes/types/proprietary/index.ts | Re-export ProprietaryAttributeValue from barrel. |
| packages/content/src/attributes/types/index.ts | Export VerifiableCredential attribute type. |
| packages/content/src/attributes/types/VerifiableCredential.ts | Add VerifiableCredential attribute value type. |
| packages/content/src/attributes/RelationshipAttributeQuery.ts | Adjust import to use proprietary barrel export. |
| packages/content/src/attributes/AttributeValueTypes.ts | Register VerifiableCredential in attribute value unions. |
| packages/consumption/test/tsconfig.json | Allow JS compilation for Jest/ESM handling. |
| packages/consumption/src/modules/requests/outgoing/OutgoingRequestsController.ts | Publish optional event returned by item processor on response application. |
| packages/consumption/src/modules/requests/itemProcessors/shareCredentialOffer/ShareCredentialOfferRequestItemProcessor.ts | Add processor for credential-offer request item + event emission. |
| packages/consumption/src/modules/requests/itemProcessors/shareAuthorizationRequest/ShareAuthorizationRequestRequestItemProcessor.ts | Add processor for authorization-request request item. |
| packages/consumption/src/modules/requests/itemProcessors/shareAuthorizationRequest/AcceptShareAuthorizationRequestRequestItemParameters.ts | Add accept parameters for selecting attribute to present. |
| packages/consumption/src/modules/requests/index.ts | Export new processors/parameter types. |
| packages/consumption/src/modules/requests/events/index.ts | Export new request event. |
| packages/consumption/src/modules/requests/events/ShareCredentialOfferRequestItemProcessedByRecipientEvent.ts | Add consumption-layer transport event. |
| packages/consumption/src/modules/openid4vc/local/RequestedCredentialCache.ts | Add local cache persisted in synchronized collection. |
| packages/consumption/src/modules/openid4vc/local/OpenId4VciCredentialResponseJSON.ts | Define JSON shape for requested credential responses. |
| packages/consumption/src/modules/openid4vc/local/KeyStorage.ts | Add key storage wrapper around synchronized collection. |
| packages/consumption/src/modules/openid4vc/local/Holder.ts | Implement holder flows (resolve offer, request/store creds, VP accept, token present/verify). |
| packages/consumption/src/modules/openid4vc/local/EnmeshedStorageService.ts | Implement Credo StorageService backed by Enmeshed attributes. |
| packages/consumption/src/modules/openid4vc/local/EnmeshedHolderKeyManagmentService.ts | Implement Credo KMS backend (create/sign/verify/encrypt). |
| packages/consumption/src/modules/openid4vc/local/EnmeshedHolderFileSystem.ts | Stub Credo filesystem implementation. |
| packages/consumption/src/modules/openid4vc/local/BaseAgent.ts | Base Credo agent setup with custom deps and KMS backend. |
| packages/consumption/src/modules/openid4vc/index.ts | Export openid4vc module entry points. |
| packages/consumption/src/modules/openid4vc/OpenId4VcController.ts | Add OpenId4VcController coordinating holder & cache. |
| packages/consumption/src/modules/index.ts | Export openid4vc module from consumption. |
| packages/consumption/src/consumption/ConsumptionIds.ts | Add ID helper for requested credential cache entry. |
| packages/consumption/src/consumption/ConsumptionControllerName.ts | Add controller name for OpenId4VcController. |
| packages/consumption/src/consumption/ConsumptionController.ts | Initialize OpenId4VcController + register new request item processors. |
| packages/consumption/src/consumption/ConsumptionConfig.ts | Add optional fetchInstance to consumption config. |
| packages/consumption/package.json | Add Credo/openid4vc + crypto/ws deps and Jest transform updates. |
| packages/app-runtime/test/tsconfig.json | Allow JS compilation for Jest/ESM handling. |
| packages/app-runtime/test/runtime/AppStringProcessor.test.ts | Add test covering VP token processing via URL + Eudiplo. |
| packages/app-runtime/test/lib/MockUIBridge.ts | Extend mock UI bridge with OID4VC UI methods. |
| packages/app-runtime/test/lib/MockUIBridge.matchers.ts | Add matchers for new UI bridge methods. |
| packages/app-runtime/test/lib/FakeUIBridge.ts | Add no-op implementations for new UI bridge methods. |
| packages/app-runtime/src/extensibility/ui/IUIBridge.ts | Extend UI bridge contract for OID4VC flows + OAuth auth. |
| packages/app-runtime/src/AppStringProcessor.ts | Add URL processing for openid-credential-offer: and openid4vp: + VP token UI flow. |
| packages/app-runtime/src/AppRuntimeErrors.ts | Add OID4VC-specific scanner errors. |
| packages/app-runtime/package.json | Update Jest transforms for ESM deps. |
| package.json | Add patch-package postinstall + docker script to run Eudiplo locally. |
| .github/workflows/test.yml | Add GHCR login step for pulling test container images. |
| .dev/oid4vc-service-config.json | Add local service config for OID4VC service/dev. |
| .dev/eudiplo/startEudiplo.ts | Add shared helper to start Eudiplo container for tests/dev. |
| .dev/eudiplo/config/test/presentation/test.json | Add Eudiplo test tenant presentation config. |
| .dev/eudiplo/config/test/key-chains/attestation-key.json | Add Eudiplo test tenant key material. |
| .dev/eudiplo/config/test/key-chains/access-key.json | Add Eudiplo test tenant key material. |
| .dev/eudiplo/config/test/issuance/issuance.json | Add Eudiplo test tenant issuance config. |
| .dev/eudiplo/config/test/issuance/credentials/test.json | Add Eudiplo test credential definition. |
| .dev/eudiplo/config/test/info.json | Add Eudiplo test tenant info. |
| .dev/eudiplo/config/test/images/jsFrog.png | Add Eudiplo test tenant logo asset. |
| .dev/eudiplo/config/test/clients/test-admin.json | Add Eudiplo test client config. |
| .dev/eudiplo/config/test/certs/certificate.json | Add Eudiplo test certificate config. |
| .ci/runChecks.sh | Exclude a specific audit advisory in CI checks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@Milena-Czierlinski Vorm Mergen würde ich die runtime bauen und in die App einbauen, um zu prüfen, ob die Demo-Strecke vollständig funktioniert. |
Co-authored-by: Copilot <[email protected]>
| const preAuthorizedCodeGrant = credentialOffer.credentialOfferPayload.grants!["urn:ietf:params:oauth:grant-type:pre-authorized_code"]; | ||
| const credentialConfigurationId = credentialOffer.credentialOfferPayload.credential_configuration_ids; | ||
|
|
||
| let requestCredentialsResult; | ||
| if (preAuthorizedCodeGrant?.tx_code) { | ||
| requestCredentialsResult = await this._requestCredentialsWithPasswordProtection( | ||
| services, | ||
| credentialOffer, | ||
| credentialConfigurationId, | ||
| preAuthorizedCodeGrant.tx_code.input_mode === "text" ? "pw" : `pin${preAuthorizedCodeGrant.tx_code.length ?? 4}` | ||
| ); | ||
| } else { | ||
| requestCredentialsResult = await services.consumptionServices.openId4Vc.requestCredentials({ | ||
| credentialOffer: credentialOffer, | ||
| credentialConfigurationIds: credentialConfigurationId | ||
| }); | ||
| } |
There was a problem hiding this comment.
I was actually thinking of a broader scope for the function. Now we still have this
let foo;
if (condition) {foo = x} else {foo = y}
It would be cleaner to have
const foo = _getFoo()
_getFoo() {
if (condition) return x
return y
}
In this case it could look like this:
| const preAuthorizedCodeGrant = credentialOffer.credentialOfferPayload.grants!["urn:ietf:params:oauth:grant-type:pre-authorized_code"]; | |
| const credentialConfigurationId = credentialOffer.credentialOfferPayload.credential_configuration_ids; | |
| let requestCredentialsResult; | |
| if (preAuthorizedCodeGrant?.tx_code) { | |
| requestCredentialsResult = await this._requestCredentialsWithPasswordProtection( | |
| services, | |
| credentialOffer, | |
| credentialConfigurationId, | |
| preAuthorizedCodeGrant.tx_code.input_mode === "text" ? "pw" : `pin${preAuthorizedCodeGrant.tx_code.length ?? 4}` | |
| ); | |
| } else { | |
| requestCredentialsResult = await services.consumptionServices.openId4Vc.requestCredentials({ | |
| credentialOffer: credentialOffer, | |
| credentialConfigurationIds: credentialConfigurationId | |
| }); | |
| } | |
| const requestCredentialsResult = await this._requestPreAuthorizedCredentials(services, credentialOffer); | |
| <...> | |
| private async _requestPreAuthorizedCredentials(services: RuntimeServices, credentialOffer: OpenId4VciResolvedCredentialOffer): Promise<Result<RequestCredentialsResponse>> { | |
| const preAuthorizedCodeGrant = credentialOffer.credentialOfferPayload.grants!["urn:ietf:params:oauth:grant-type:pre-authorized_code"]; | |
| const credentialConfigurationIds = credentialOffer.credentialOfferPayload.credential_configuration_ids; | |
| if (preAuthorizedCodeGrant?.tx_code) { | |
| const passwordType = (preAuthorizedCodeGrant.tx_code.input_mode === "text" ? "pw" : `pin${preAuthorizedCodeGrant.tx_code.length ?? 4}`) as "pw" | `pin${number}`; | |
| const fetchResult = await this._fetchPasswordProtectedItemWithRetry( | |
| (password) => | |
| services.consumptionServices.openId4Vc.requestCredentials({ | |
| credentialOffer, | |
| credentialConfigurationIds, | |
| pinCode: password | |
| }), | |
| { passwordType }, | |
| "error.runtime.openid4vc.oauth.invalid_grant" | |
| ); | |
| return fetchResult.result; | |
| } | |
| return await services.consumptionServices.openId4Vc.requestCredentials({ credentialOffer, credentialConfigurationIds }); | |
| } |
There was a problem hiding this comment.
Thanks for the detailed explanation @Milena-Czierlinski 🫶
Readiness checklist