Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ca7c54a
test: update eudiplo
Magnus-Kuhn Feb 4, 2026
13d25e5
test: try another tenant
Magnus-Kuhn Feb 4, 2026
1219fca
fix: allow AES128GCM
Magnus-Kuhn Feb 4, 2026
0feb6b0
test: fix eudiplo tests
Magnus-Kuhn Feb 5, 2026
03e8498
Merge branch 'release/openid4vc' into update-eudiplo
mergify[bot] Feb 5, 2026
c43ccf5
Merge branch 'release/openid4vc' into update-eudiplo
mergify[bot] Feb 6, 2026
a4dee56
test: check reading of enmeshed data
Magnus-Kuhn Feb 6, 2026
c896467
test: make tests work
Magnus-Kuhn Feb 6, 2026
14d3f3d
test: add eudiplo pin test
Magnus-Kuhn Feb 7, 2026
fca803c
test: check display information
Magnus-Kuhn Feb 9, 2026
edbaeeb
test: update eudiplo assets and test to eudiplo
Magnus-Kuhn Feb 16, 2026
a808b53
test: update compose openid4vc
Magnus-Kuhn Feb 16, 2026
141dfa4
chore: disable lint
Magnus-Kuhn Feb 16, 2026
2959a18
chore: update dependencies
Magnus-Kuhn Feb 16, 2026
6085847
chore: updates in compose
Magnus-Kuhn Feb 16, 2026
03e0c17
chore: fix eudiplo config property names
tnotheis Feb 17, 2026
8caf24b
chore: fix eudiplo base url
tnotheis Feb 17, 2026
80f8c7e
chore: log error in ShareAuthorizationRequestRequestItemProcessor.accept
tnotheis Feb 17, 2026
e31534f
chore: remove obsolete configs
Magnus-Kuhn Feb 17, 2026
71c062a
chore: remove mongodb dep
Magnus-Kuhn Feb 17, 2026
6043619
refactor: rename to oid4vc service config
Magnus-Kuhn Feb 17, 2026
e8877ed
chore: remove unnecessary port mapping
Magnus-Kuhn Feb 17, 2026
a914816
test: remove eudiploContainer
tnotheis Feb 17, 2026
2e2bb4b
chore: add docker compose dependencies to oid4vc-service
tnotheis Feb 17, 2026
f02ec74
chore: move environment variables to compose file
tnotheis Feb 17, 2026
74bb306
test: remove unnecessary test service
Magnus-Kuhn Feb 17, 2026
3c44244
Merge branch 'update-eudiplo' of https://github.com/nmshd/runtime int…
Magnus-Kuhn Feb 17, 2026
e6a52a5
fix: correct healthcheck
Magnus-Kuhn Feb 17, 2026
f8e7d52
test: correctly set env vars
tnotheis Feb 17, 2026
1d65954
chore: upgrade oid4vc-service to 3.0.1
tnotheis Feb 17, 2026
202eb36
test: make connector auto accept pending relationships
tnotheis Feb 17, 2026
49130ff
fix: switch to localhost in tests
Magnus-Kuhn Feb 17, 2026
29101a0
refactor: add comment
Magnus-Kuhn Feb 17, 2026
7b5796f
test: disable eslint
Magnus-Kuhn Feb 18, 2026
4f6ee9e
fix: don't change every test behavior
Magnus-Kuhn Feb 18, 2026
79f1f64
chore: update dependencies
Magnus-Kuhn Feb 18, 2026
67847b2
chore: clean npm install
Magnus-Kuhn Feb 18, 2026
096de50
refactor: satisfy linter
Magnus-Kuhn Feb 18, 2026
0e415b7
ci: exclude ajv vulnerability
Magnus-Kuhn Feb 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ci/runChecks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ npm run lint:eslint
npm run lint:prettier
npm run --workspaces cdep
npx --workspaces license-check
npx better-npm-audit audit --exclude=1112030
npx better-npm-audit audit --exclude=1112030,1113214
37 changes: 27 additions & 10 deletions .dev/compose.openid4vc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,33 @@ name: runtime-oid4vc-tests

services:
oid4vc-service:
image: ghcr.io/js-soft/openid4vc-service:1.3.0@sha256:0ec8d7e1479d168c4760cc0fb3f7d9273985bc2d74759f09cd39e0b50fa45f6b
image: ghcr.io/js-soft/openid4vc-service:3.0.1@sha256:17d35ee82a5c65c700e5b6f7922ea833717f1c16a515f60cf1ee7b36b524b32b
ports:
- "9000:9000"
platform: linux/amd64
environment:
authServer__baseUrl: "https://kc-openid4vc.is.enmeshed.eu/realms/enmeshed-openid4vci"
volumes:
- ./service-config.json:/usr/app/config.json
- ./oid4vc-service-config.json:/usr/app/config.json
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
- mongodb
- connector
- eudiplo
networks:
- default

connector:
image: ghcr.io/nmshd/connector:7.3.0-openid4vc.1@sha256:4be31417d10d67454d7732949601a2136417fefc78107e3751eccea7946a7aca
image: ghcr.io/nmshd/connector:7.4.0-openid4vc.2@sha256:d121f97ce2e4506a7ff58de0012b3445f6d0aeb8dd4d765c09da7f34d8a22b5b
environment:
CUSTOM_CONFIG_LOCATION: "/config.json"
transportLibrary__baseUrl: "http://consumer-api:8080"
transportLibrary__platformClientId: test
transportLibrary__platformClientSecret: test
transportLibrary__addressGenerationHostnameOverride: localhost
transportLibrary__baseUrl: ${NMSHD_TEST_BASEURL}
transportLibrary__platformClientId: ${NMSHD_TEST_CLIENTID}
transportLibrary__platformClientSecret: ${NMSHD_TEST_CLIENTSECRET}
transportLibrary__addressGenerationHostnameOverride: ${NMSHD_TEST_ADDRESSGENERATIONHOSTNAMEOVERRIDE}
database__connectionString: mongodb://root:example@mongodb:27017
infrastructure__httpServer__authentication__apiKey__keys__default__key: aVeryCoolApiKeyWith30CharactersOr+
modules__autoAcceptPendingRelationships__enabled: true
modules__sync__enabled: true
modules__sync__interval: 1
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
Expand All @@ -43,6 +45,21 @@ services:
networks:
- default

eudiplo:
image: ghcr.io/openwallet-foundation-labs/eudiplo:1.16.0
environment:
- PUBLIC_URL=http://localhost:3000
- JWT_SECRET="OgwrDcgVQQ2yZwcFt7kPxQm3nUF+X3etF6MdLTstZAY="
- AUTH_CLIENT_ID="root"
- AUTH_CLIENT_SECRET="test"
- PORT=3000
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "3000:3000"
volumes:
- ./eudiplo-assets:/app/config

networks:
default:
name: runtime-oid4vc-tests-network
Expand Down
Binary file modified .dev/eudiplo-assets/service.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
12 changes: 4 additions & 8 deletions .dev/service-config.json → .dev/oid4vc-service-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@
"baseUrl": "http://connector:80",
"apiKey": "aVeryCoolApiKeyWith30CharactersOr+"
},
"customizing": {
"organization": {
"displayName": "js-soft AG",
"logoUrl": "http://127.0.0.1:9000/ui/img/js_soft.png"
}
},
"cors": {
"origin": "*"
"eudiplo": {
"baseUrl": "http://eudiplo:3000",
"clientId": "test-admin",
"clientSecret": "57c9cd444bf402b2cc1f5a0d2dafd3955bd9042c0372db17a4ede2d5fbda88e5"
}
}
2,679 changes: 1,349 additions & 1,330 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,8 @@
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.9.3"
},
"overrides": {
"tar": "^7.5.9"
}
Comment on lines +52 to 54
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? It's dangerous to override peer dependencies, because it might introduce breaking changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To fix the vulnerability GHSA-83g3-92jg-28cx

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class EnmshedHolderKeyManagmentService implements Kms.KeyManagementServic
if (operation.operation === "deleteKey") {
return true;
}
if (operation.operation === "encrypt") {
if (operation.operation === "encrypt" && ["A128GCM", "A256GCM"].includes(operation.encryption.algorithm)) {
return true;
}
return false;
Expand Down Expand Up @@ -342,7 +342,7 @@ export class EnmshedHolderKeyManagmentService implements Kms.KeyManagementServic

public async encrypt(agentContext: AgentContext, options: Kms.KmsEncryptOptions): Promise<Kms.KmsEncryptReturn> {
try {
// encryption via A-256-GCM
// encryption via A-128-GCM/A-256-GCM
// we will call the services side bob and the incoming side alice
if (options.key.keyAgreement === undefined) {
throw new Error("Key agreement is undefined");
Expand All @@ -351,11 +351,14 @@ export class EnmshedHolderKeyManagmentService implements Kms.KeyManagementServic
throw new Error("Key agreement keyId is undefined");
}

const algorithm = options.encryption.algorithm;
const keyLength = options.encryption.algorithm === "A128GCM" ? 128 : 256;

// 1. derive the shared secret via ECDH-ES
const sharedSecret = await this.ecdhEs(options.key.keyAgreement.keyId, options.key.keyAgreement.externalPublicJwk);
agentContext.config.logger.debug(`EKM: Derived shared secret for encryption using ECDH-ES`);
// 2. Concat KDF to form the final key
const derivedKey = this.concatKdf(sharedSecret, 256, "A256GCM", options.key.keyAgreement);
const derivedKey = this.concatKdf(sharedSecret, keyLength, algorithm, options.key.keyAgreement);
// 3. Encrypt the data via AES-256-GCM using libsodium

// create nonce
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AcceptResponseItem, Request, ResponseItemResult, ShareAuthorizationRequestRequestItem } from "@nmshd/content";
import { CoreAddress } from "@nmshd/core-types";
import { TransportCoreErrors } from "@nmshd/transport";
import { TransportCoreErrors, TransportLoggerFactory } from "@nmshd/transport";
import { ConsumptionCoreErrors } from "../../../../consumption/ConsumptionCoreErrors";
import { LocalAttribute, OwnIdentityAttribute } from "../../../attributes";
import { ValidationResult } from "../../../common/ValidationResult";
Expand Down Expand Up @@ -60,7 +60,15 @@ export class ShareAuthorizationRequestRequestItemProcessor extends GenericReques
const attribute = (await this.consumptionController.attributes.getLocalAttribute(parsedParams.attributeId)) as OwnIdentityAttribute | undefined;
if (!attribute) throw TransportCoreErrors.general.recordNotFound(LocalAttribute, parsedParams.attributeId.toString());

await this.consumptionController.openId4Vc.acceptAuthorizationRequest(resolvedAuthorizationRequest.authorizationRequest, attribute);
const acceptResult = await this.consumptionController.openId4Vc.acceptAuthorizationRequest(resolvedAuthorizationRequest.authorizationRequest, attribute);
if (acceptResult.status !== 200) {
TransportLoggerFactory.getLogger(ShareAuthorizationRequestRequestItemProcessor).error(
"Failed to accept ShareAuthorizationRequestRequestItem. Error message:",
JSON.stringify(acceptResult.message)
);

throw ConsumptionCoreErrors.requests.invalidAcceptParameters("The presentation was not successful. Try again later or select a different credential.");
}

return AcceptResponseItem.from({ result: ResponseItemResult.Accepted });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { LocalRequestInfo } from "../IRequestItemProcessor";

export class ShareCredentialOfferRequestItemProcessor extends GenericRequestItemProcessor<ShareCredentialOfferRequestItem> {
public override async canCreateOutgoingRequestItem(requestItem: ShareCredentialOfferRequestItem, _request: Request, _recipient?: CoreAddress): Promise<ValidationResult> {
if (process.env.TEST_ENVIRONMENT === "container") return ValidationResult.success(); // for the test scenario that this runs inside a container which can't resolve a localhost credential offer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use the network mode "host". See https://docs.docker.com/engine/network/drivers/host/


const offer = await this.consumptionController.openId4Vc.resolveCredentialOffer(requestItem.credentialOfferUrl);

const preAuthorizedCodeGrant = offer.credentialOfferPayload.grants?.["urn:ietf:params:oauth:grant-type:pre-authorized_code"];
Expand Down
4 changes: 3 additions & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"@nmshd/runtime-types": "*",
"@nmshd/transport": "*",
"@nmshd/typescript-ioc": "3.2.5",
"ajv": "^8.17.1",
"ajv": "^8.18.0",
"ajv-errors": "^3.0.0",
"ajv-formats": "^3.0.1",
"elliptic": "^6.6.1",
Expand All @@ -102,11 +102,13 @@
"@js-soft/docdb-access-loki": "1.4.0",
"@js-soft/docdb-access-mongo": "1.4.0",
"@js-soft/node-logger": "1.2.1",
"@nmshd/connector-sdk": "^7.3.0",
"@types/elliptic": "^6.4.18",
"@types/json-stringify-safe": "^5.0.3",
"@types/lodash": "^4.17.23",
"@types/luxon": "^3.7.1",
"jwt-decode": "^4.0.0",
"@eudiplo/sdk-core": "^1.16.0",
"openid-client": "^6.8.1",
"ts-json-schema-generator": "2.5.0",
"ts-mockito": "^2.6.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/dataViews/DataViewExpander.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ export class DataViewExpander {
}

let proposedValueOverruled = false;
if (responseItemDVO && responseItemDVO.result === ResponseItemResult.Accepted) {
if (responseItemDVO?.result === ResponseItemResult.Accepted) {
if (responseItemDVO.type === "AttributeSuccessionAcceptResponseItemDVO") {
const attributeSuccessionResponseItem = responseItemDVO as AttributeSuccessionAcceptResponseItemDVO;
proposedValueOverruled = !_.isEqual(attributeSuccessionResponseItem.successor.content.value, proposeAttributeRequestItem.attribute.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ export class SendMessageUseCase extends UseCase<SendMessageRequest, MessageDTO>
for (const recipient of recipients) {
const relationship = await this.relationshipsController.getRelationshipToIdentity(CoreAddress.from(recipient));

if (!relationship || relationship.status !== RelationshipStatus.Active) {
if (relationship?.status !== RelationshipStatus.Active) {
peersWithNoActiveRelationship.push(recipient);

if (!relationship || relationship.status !== RelationshipStatus.Terminated) {
if (relationship?.status !== RelationshipStatus.Terminated) {
peersWithNeitherActiveNorTerminatedRelationship.push(recipient);
}

Expand Down
Loading