Skip to content

Commit

Permalink
fix: use jwks instead of dids
Browse files Browse the repository at this point in the history
Signed-off-by: Mirko Mollik <mirko.mollik@fit.fraunhofer.de>
  • Loading branch information
cre8 committed Apr 22, 2024
1 parent 4b196b3 commit 23ed0dc
Show file tree
Hide file tree
Showing 28 changed files with 713 additions and 178 deletions.
1 change: 1 addition & 0 deletions apps/backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/dist
/node_modules
/build
db.sqlite

# Logs
logs
Expand Down
24 changes: 24 additions & 0 deletions apps/backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Backend

## Database
The backend supports multiple types of databases. The type has to be set via the environment variable `DB_TYPE`. The following types are supported:

`postgres`: default database
If used, the following values have to be set:
```bash
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=nestjs
DB_USERNAME=user
DB_PASSWORD=pass
```

`sqlite`
If used, the following values have to be set:
```bash
DB_TYPE=sqlite
DB_NAME=db.sqlite
```

There are no specific requirements for the used database, so using other types like `mysql` are possible to be added later.
11 changes: 9 additions & 2 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"pg": "^8.11.3",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"sqlite3": "^5.1.7",
"typeorm": "^0.3.20",
"uuid": "^9.0.1"
},
Expand All @@ -68,13 +69,19 @@
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": ["js", "json", "ts"],
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": ["**/*.(t|j)s"],
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
Expand Down
62 changes: 48 additions & 14 deletions apps/backend/src/db/db.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,34 @@ import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import * as Joi from 'joi';
import { DatabaseType } from 'typeorm';

export const DB_VALIDATION_SCHEMA = {
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().required(),
DB_USERNAME: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_TYPE: Joi.string().default('postgres'),
DB_HOST: Joi.string().when('DB_TYPE', {
is: 'postgres',
// biome-ignore lint/suspicious/noThenProperty: <explanation>
then: Joi.required(),
otherwise: Joi.optional(),
}),
DB_PORT: Joi.number().when('DB_TYPE', {
is: 'postgres',
// biome-ignore lint/suspicious/noThenProperty: <explanation>
then: Joi.required(),
otherwise: Joi.optional(),
}),
DB_USERNAME: Joi.string().when('DB_TYPE', {
is: 'postgres',
// biome-ignore lint/suspicious/noThenProperty: <explanation>
then: Joi.required(),
otherwise: Joi.optional(),
}),
DB_PASSWORD: Joi.string().when('DB_TYPE', {
is: 'postgres',
// biome-ignore lint/suspicious/noThenProperty: <explanation>
then: Joi.required(),
otherwise: Joi.optional(),
}),
DB_NAME: Joi.string().required(),
};

Expand All @@ -16,16 +38,28 @@ export const DB_VALIDATION_SCHEMA = {
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get('DB_HOST'),
port: configService.get('DB_PORT'),
username: configService.get('DB_USERNAME'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_NAME'),
synchronize: true,
autoLoadEntities: true,
}),
useFactory: (configService: ConfigService) => {
switch (configService.get('DB_TYPE') as DatabaseType) {
case 'sqlite':
return {
type: 'sqlite',
database: configService.get('DB_NAME'),
synchronize: true,
autoLoadEntities: true,
};
case 'postgres':
return {
type: 'postgres',
host: configService.get('DB_HOST'),
port: configService.get('DB_PORT'),
username: configService.get('DB_USERNAME'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_NAME'),
synchronize: true,
autoLoadEntities: true,
};
}
},
}),
],
})
Expand Down
3 changes: 0 additions & 3 deletions apps/backend/src/keys/dto/proof-request.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ export class ProofRequest {
@IsOptional()
kid?: string;

@IsString()
aud: string;

@IsObject()
payload: JWTPayload;
}
13 changes: 7 additions & 6 deletions apps/backend/src/keys/keys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,18 @@ export class KeysService {
where: { id: newKey.id },
});
}
//TODO: add the key id when the key is interset into the database. For this the primary get has to be generated first
key.publicKey.kid = key.id;

const jwk = await importJWK<JoseKeyLike>(key.privateKey, 'ES256');
key.publicKey.kid = key.id;
const kid = this.encodeDidJWK(key.publicKey);
return {
jwt: await new SignJWT({ ...value.payload })
.setProtectedHeader({ alg: 'ES256', kid, typ: 'openid4vci-proof+jwt' })
.setProtectedHeader({
alg: 'ES256',
typ: 'openid4vci-proof+jwt',
jwk: key.publicKey,
})
.setIssuedAt()
.setIssuer(kid)
.setSubject(kid)
// .setAudience(value.payload.payload.aud!)
.setExpirationTime('2h')
.sign(jwk),
};
Expand Down
29 changes: 14 additions & 15 deletions apps/backend/src/oid4vc/oid4vci/oid4vci.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SDJwtVcInstance } from '@sd-jwt/sd-jwt-vc';
import { OpenID4VCIClient } from '@sphereon/oid4vci-client';
import {
Alg,
JwtVerifyResult,
type CredentialSupported,
type CredentialSupportedSdJwtVc,
type Jwt,
Expand All @@ -17,6 +18,7 @@ import { KeysService } from 'src/keys/keys.service';
import { v4 as uuid } from 'uuid';
import { Oid4vciParseRepsonse } from './dto/parse-response.dto';
import { Oid4vciParseRequest } from './dto/parse-request.dto';
import { decodeJwt } from 'jose';

type Session = {
//instead of storing the client, we could also generate it on demand. In this case we need to store the uri
Expand Down Expand Up @@ -92,25 +94,22 @@ export class Oid4vciService {
key = keys[0];
}
const proofCallbacks: ProofOfPossessionCallbacks<DIDDocument> = {
// verifyCallback: async (args: {
// jwt: string;
// kid?: string;
// }): Promise<JwtVerifyResult<DIDDocument>> => {
// console.log(args);
// return Promise.resolve({
// jwt: JSON.parse(args.jwt) as Jwt,
// alg: 'ES256',
// });
// },
signCallback: async (args: Jwt): Promise<string> => {
return this.keysService
verifyCallback: async (args: {
jwt: string;
}): Promise<JwtVerifyResult<DIDDocument>> =>
Promise.resolve({
jwt: decodeJwt(args.jwt),
alg: Alg.ES256,
//instead of using the key referene, we could extract the key from the jwt
jwk: key.publicKey,
}),
signCallback: async (args: Jwt): Promise<string> =>
this.keysService
.proof(user, {
payload: args.payload,
kid: key.id,
aud: '',
})
.then((response) => response.jwt);
},
.then((response) => response.jwt),
};
await data.client.acquireAccessToken();
for (const credential of data.credentials) {
Expand Down
5 changes: 2 additions & 3 deletions apps/backend/src/oid4vc/oid4vp/oid4vp.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,13 @@ export class Oid4vpService {
args.presentation as SdJwtDecodedVerifiableCredentialWithKbJwtInput
).kbJwt;
args.selectedCredentials[0];
//TODO: set the correct value for aud
const aud =
session.verifiedAuthReqWithJWT.authorizationRequest.payload.client_id;
const cnf = args.presentation.decodedPayload.cnf;
const kid = this.keysService.decodeDidJWK(cnf.kid).kid as string;
//const kid = this.keysService.decodeDidJWK(cnf.kid).kid as string;
const signwedKbJwt = await this.keysService.signkbJwt(
user,
kid,
cnf.kid,
kbJwt,
aud
);
Expand Down
3 changes: 2 additions & 1 deletion apps/holder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"ng": "ng",
"start:pwa": "ng serve pwa",
"build": "ng build --project pwa",
"start:extension": "concurrently --kill-others \"ng build --project browser-extension --watch\" \"nodemon\"",
"start:extension": "concurrently --kill-others \"ng build --project browser-extension --configuration development --watch\" \"nodemon\"",
"start:extension-prod": "concurrently --kill-others \"ng build --project browser-extension --watch\" \"nodemon\"",
"api": "npx @openapitools/openapi-generator-cli generate -g typescript-angular -i http://localhost:3000/api-json -o projects/shared/api/kms --api-name-suffix=Kms --additional-properties=supportsES6=true,enumPropertyNaming=original,serviceSuffix=ApiService",
"test": "ng test",
"lint": "ng lint",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
>
<div fxLayout="column" fxLayoutAlign=" center">
<a mat-icon-button routerLink="/scan" routerLinkActive="active-link"
><mat-icon>qr_code</mat-icon></a
><mat-icon>qr_code_scanner</mat-icon></a
>
<span class="info">scan</span>
</div>
<div fxLayout="column" fxLayoutAlign=" center">
<a mat-icon-button routerLink="/credentials" routerLinkActive="active-link"
><mat-icon>badge</mat-icon></a
><mat-icon>account_balance_wallet</mat-icon></a
>
<span class="info">credentials</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
background-color: #D7E3FF;
padding: 10px 40px;
height: 84px;
position: fixed;
}

.content {
height: calc(100vh - 84px);
overflow: auto;
width: 100%;
padding-bottom: 84px;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ function getConfiguration() {
basePath: environment.backendUrl,
credentials: {
oauth2: () => {
console.log(localStorage.getItem('accessToken'));
return localStorage.getItem('accessToken') as string;
},
},
Expand Down
8 changes: 4 additions & 4 deletions apps/holder/projects/pwa/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
</div>
<mat-toolbar fxLayout="row" fxLayoutAlign="space-between center">
<div fxLayout="column" fxLayoutAlign=" center">
<a mat-icon-button routerLink="/scan" routerLinkActive="active-link"
><mat-icon>qr_code</mat-icon></a
<a mat-fab routerLink="/scan" routerLinkActive="active-link"
><mat-icon>qr_code_scanner</mat-icon></a
>
<span class="info">scan</span>
<!-- <span class="info">scan</span> -->
</div>
<div fxLayout="column" fxLayoutAlign=" center">
<a mat-icon-button routerLink="/credentials" routerLinkActive="active-link"
><mat-icon>badge</mat-icon></a
><mat-icon>account_balance_wallet</mat-icon></a
>
<span class="info">credentials</span>
</div>
Expand Down
9 changes: 8 additions & 1 deletion apps/holder/projects/pwa/src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@
background-color: #D7E3FF;
padding: 10px 40px;
height: 84px;
position: absolute;
position: fixed;
bottom: 0px;
}

.info {
font-size: 14px;
}

.content {
height: calc(100vh - 84px);
overflow: auto;
width: 100%;
padding-bottom: 84px;
}
14 changes: 8 additions & 6 deletions apps/holder/projects/pwa/src/app/scanner/scanner.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { CameraDevice, Html5Qrcode } from 'html5-qrcode';
import { Html5QrcodeError } from 'html5-qrcode/esm/core';
import { MatMenuModule } from '@angular/material/menu';
Expand Down Expand Up @@ -29,7 +29,7 @@ type Status = 'scanning' | 'showRequest' | 'showVerificationRequest';
VerifyRequestComponent,
],
})
export class ScannerComponent implements OnInit {
export class ScannerComponent implements OnInit, OnDestroy {
scanner?: Html5Qrcode;
devices: CameraDevice[] = [];
selectedDevice?: string;
Expand All @@ -44,7 +44,6 @@ export class ScannerComponent implements OnInit {

ngOnInit(): void {
const fragment = this.route.snapshot.fragment;
console.log(fragment);
if (fragment === 'issue') {
this.getCredential();
return;
Expand Down Expand Up @@ -79,6 +78,12 @@ export class ScannerComponent implements OnInit {
});
}

async ngOnDestroy(): Promise<void> {
if (this.scanner) {
await this.scanner.stop();
}
}

async startCamera() {
this.scanner = new Html5Qrcode('reader');
//TODO: we need to set the correct dimensions.
Expand Down Expand Up @@ -116,9 +121,6 @@ export class ScannerComponent implements OnInit {

onScanFailure(errorMessage: string, error: Html5QrcodeError) {
// handle scan failure, usually better to ignore and keep scanning.
// for example:
// console.log(error.errorMessage);
// console.warn(`Code scan error = ${error}`);
}

getCredential() {
Expand Down
Loading

0 comments on commit 23ed0dc

Please sign in to comment.