Skip to content

Commit 84c1ebb

Browse files
authored
Merge pull request #314 from openwallet-foundation-labs/feat/verify-mdoc
feat: add mdoc validation approach
2 parents 2ca97d0 + 1cc9682 commit 84c1ebb

File tree

87 files changed

+7377
-10562
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+7377
-10562
lines changed

.codecov.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ coverage:
99
patch:
1010
default:
1111
target: 10%
12+
paths:
13+
- apps/backend/**
1214

1315
comment:
1416
layout: "reach,diff,flags,tree"
@@ -25,5 +27,11 @@ ignore:
2527
- "apps/client/**"
2628
- "apps/dcapi/**"
2729
- "apps/webhook/**"
30+
- "apps/verifier-app/**"
2831
- "apps/tmp/**"
2932
- "monitor/**"
33+
- "packages/**"
34+
- "deployment/**"
35+
- "scripts/**"
36+
- "schemas/**"
37+
- "assets/**"

.github/workflows/ci-and-release.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,8 @@ jobs:
219219
steps:
220220
- uses: actions/checkout@v6
221221

222-
# - name: Add entry to /etc/hosts
223-
# run: echo "127.0.0.1 host.testcontainers.internal" | sudo tee
224-
# -a /etc/hosts
222+
- name: Add entry to /etc/hosts
223+
run: echo "127.0.0.1 host.testcontainers.internal" | sudo tee -a /etc/hosts
225224

226225
- uses: pnpm/action-setup@v4
227226
name: Install pnpm
@@ -246,6 +245,8 @@ jobs:
246245
247246
- name: Run E2E tests
248247
run: pnpm run --filter @eudiplo/backend test:e2e
248+
env:
249+
TESTCONTAINERS_HOST_OVERRIDE: host.testcontainers.internal
249250

250251
- name: Upload coverage to Codecov
251252
uses: codecov/codecov-action@v5

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ management, scalable database support, and clean API boundaries.
3737

3838
## 🧩 Features
3939

40-
- ✅ Supports **OID4VCI**, **OID4VP**, **SD-JWT VC**, and **OAuth Token Status
40+
- ✅ Supports **OID4VCI**, **OID4VP**, **SD-JWT VC**, **mDOC (ISO 18013-5)**, and **OAuth Token Status
4141
List**
4242
-**OIDF conformance tested** for OID4VCI and OID4VP protocols
4343
- ✅ JSON-based credential configuration

apps/backend/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@
2424
"start:debug": "nest start --debug --watch",
2525
"start:prod": "node dist/main",
2626
"lint": "biome lint",
27-
"lint:fix": "biome check --fix",
27+
"lint:fix": "biome check --fix --unsafe",
2828
"test": "vitest run",
2929
"test:watch": "vitest",
3030
"test:debug": "vitest --inspect-brk --inspect --logHeapUsage --threads=false",
31-
"test:e2e": "VITEST_SKIP_OIDF=true vitest run --coverage --config ./test/vitest.config.ts",
31+
"test:e2e": "vitest run --coverage --config ./test/vitest.config.ts",
3232
"test:e2e:watch": "vitest --coverage --config ./test/vitest.config.ts",
3333
"registrar": "npx @hey-api/openapi-ts -i https://funke-wallet.de/-json -o ./src/registrar/generated -c @hey-api/client-fetch"
3434
},
3535
"dependencies": {
36+
"@animo-id/mdoc": "0.6.0-alpha-20251209053057",
3637
"@aws-sdk/client-s3": "^3.943.0",
3738
"@aws-sdk/s3-request-presigner": "^3.943.0",
3839
"@badgateway/oauth2-client": "^3.3.1",
@@ -49,6 +50,8 @@
4950
"@nestjs/swagger": "^11.2.3",
5051
"@nestjs/terminus": "^11.0.0",
5152
"@nestjs/typeorm": "^11.0.0",
53+
"@noble/hashes": "^2.0.1",
54+
"@noble/curves": "^2.0.1",
5255
"@openid4vc/oauth2": "0.4.3",
5356
"@openid4vc/openid4vci": "0.4.3",
5457
"@openid4vc/openid4vp": "0.4.3",
@@ -82,7 +85,8 @@
8285
"rxjs": "^7.8.2",
8386
"sqlite3": "^5.1.7",
8487
"typeorm": "^0.3.28",
85-
"uuid": "^13.0.0"
88+
"uuid": "^13.0.0",
89+
"@panva/hkdf": "^1.2.1"
8690
},
8791
"devDependencies": {
8892
"@biomejs/biome": "2.3.8",

apps/backend/src/crypto/key/cert/cert.service.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { v4 } from "uuid";
1111
import { TenantEntity } from "../../../auth/tenant/entitites/tenant.entity";
1212
import { ConfigImportService } from "../../../shared/utils/config-import/config-import.service";
1313
import { CertImportDto } from "../dto/cert-import.dto";
14-
import { CertSelfSignedDto } from "../dto/cert-self-signed.dto";
1514
import { CertUpdateDto } from "../dto/cert-update.dto";
1615
import { UpdateKeyDto } from "../dto/key-update.dto";
1716
import { CertEntity } from "../entities/cert.entity";
@@ -154,7 +153,7 @@ export class CertService {
154153
async addSelfSignedCert(
155154
tenant: TenantEntity,
156155
keyId: string,
157-
dto: CertSelfSignedDto,
156+
dto: CertImportDto,
158157
) {
159158
// === Inputs/parameters (subject + SAN hostname) ===
160159
const subjectCN = tenant.name;
@@ -185,7 +184,7 @@ export class CertService {
185184
const selfSignedCert =
186185
await x509.X509CertificateGenerator.createSelfSigned({
187186
serialNumber: "01",
188-
name: `CN=${subjectCN}`,
187+
name: `C=DE, CN=${subjectCN}`,
189188
notBefore: now,
190189
notAfter: inOneYear,
191190
signingAlgorithm: ECDSA_P256,
@@ -208,7 +207,9 @@ export class CertService {
208207
return this.addCertificate(tenant.id, keyId, {
209208
crt: crtPem,
210209
certUsageTypes: dto.certUsageTypes,
211-
description: `Self-signed certificate (${dto.certUsageTypes.join(", ")}) for tenant ${tenant.name}`,
210+
description:
211+
dto.description ??
212+
`Self-signed certificate (${dto.certUsageTypes.join(", ")}) for tenant ${tenant.name}`,
212213
keyId,
213214
});
214215
}
@@ -300,7 +301,7 @@ export class CertService {
300301
// Create a new key
301302
const keyId = await this.keyService.create(value.tenantId);
302303

303-
const dto: CertSelfSignedDto = {
304+
const dto: CertImportDto = {
304305
certUsageTypes: [value.type],
305306
keyId,
306307
};

apps/backend/src/crypto/key/dto/cert-self-signed.dto.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

apps/backend/src/issuer/configuration/configuration.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { CredentialConfigService } from "./credentials/credential-config/credent
99
import { CredentialConfigController } from "./credentials/credential-config.controller";
1010
import { CredentialsService } from "./credentials/credentials.service";
1111
import { CredentialConfig } from "./credentials/entities/credential.entity";
12+
import { MdocIssuerService } from "./credentials/issuer/mdoc-issuer/mdoc-issuer.service";
13+
import { SdjwtvcIssuerService } from "./credentials/issuer/sdjwtvc-issuer/sdjwtvc-issuer.service";
1214
import { IssuanceConfig } from "./issuance/entities/issuance-config.entity";
1315
import { IssuanceService } from "./issuance/issuance.service";
1416
import { IssuanceConfigController } from "./issuance/issuance-config.controller";
@@ -35,6 +37,8 @@ import { IssuanceConfigController } from "./issuance/issuance-config.controller"
3537
CredentialsService,
3638
CredentialConfigService,
3739
WebhookService,
40+
SdjwtvcIssuerService,
41+
MdocIssuerService,
3842
],
3943
exports: [IssuanceService, CredentialConfigService, CredentialsService],
4044
})

apps/backend/src/issuer/configuration/credentials/credential-config.controller.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { Body, Controller, Delete, Get, Param, Post } from "@nestjs/common";
1+
import {
2+
Body,
3+
Controller,
4+
Delete,
5+
Get,
6+
Param,
7+
Patch,
8+
Post,
9+
} from "@nestjs/common";
210
import { ApiTags } from "@nestjs/swagger";
311
import { Role } from "../../../auth/roles/role.enum";
412
import { Secured } from "../../../auth/secure.decorator";
513
import { Token, TokenPayload } from "../../../auth/token.decorator";
614
import { CredentialConfigService } from "./credential-config/credential-config.service";
715
import { CredentialConfigCreate } from "./dto/credential-config-create.dto";
16+
import { CredentialConfigUpdate } from "./dto/credential-config-update.dto";
817

918
/**
1019
* Controller for managing credential configurations.
@@ -49,6 +58,22 @@ export class CredentialConfigController {
4958
return this.credentialsService.store(user.entity!.id, config);
5059
}
5160

61+
/**
62+
* Updates a credential configuration by ID.
63+
* @param id
64+
* @param config
65+
* @param user
66+
* @returns
67+
*/
68+
@Patch(":id")
69+
updateCredentialConfiguration(
70+
@Param("id") id: string,
71+
@Body() config: CredentialConfigUpdate,
72+
@Token() user: TokenPayload,
73+
) {
74+
return this.credentialsService.update(user.entity!.id, id, config);
75+
}
76+
5277
/**
5378
* Deletes an credential configuration.
5479
* @param id

apps/backend/src/issuer/configuration/credentials/credential-config/credential-config.service.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ConfigImportService } from "../../../../shared/utils/config-import/conf
1010
import { FilesService } from "../../../../storage/files.service";
1111
import { StatusListService } from "../../../lifecycle/status/status-list.service";
1212
import { CredentialConfigCreate } from "../dto/credential-config-create.dto";
13+
import { CredentialConfigUpdate } from "../dto/credential-config-update.dto";
1314
import { CredentialConfig } from "../entities/credential.entity";
1415

1516
/**
@@ -168,6 +169,25 @@ export class CredentialConfigService {
168169
});
169170
}
170171

172+
/**
173+
* Updates a credential configuration for a given tenant.
174+
* Only updates fields that are provided in the config.
175+
* Set fields to null to clear them.
176+
* @param tenantId - The ID of the tenant.
177+
* @param id - The ID of the CredentialConfig entity to update.
178+
* @param config - The partial CredentialConfig to update.
179+
* @returns A promise that resolves to the updated CredentialConfig entity.
180+
*/
181+
async update(tenantId: string, id: string, config: CredentialConfigUpdate) {
182+
const existing = await this.getById(tenantId, id);
183+
return this.credentialConfigRepository.save({
184+
...existing,
185+
...config,
186+
id,
187+
tenantId,
188+
});
189+
}
190+
171191
/**
172192
* Deletes a credential configuration for a given tenant.
173193
* @param tenantId - The ID of the tenant.

0 commit comments

Comments
 (0)