Skip to content

Commit

Permalink
split oid4vc services
Browse files Browse the repository at this point in the history
  • Loading branch information
eike-hass committed Apr 25, 2024
1 parent 0ce4099 commit e5a0f3c
Show file tree
Hide file tree
Showing 27 changed files with 453 additions and 109 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@

Profile
- DID IOTA
- DID Key / DID JWK
- [Self-Issued OpenID Provider v2 - draft 13](https://openid.net/specs/openid-connect-self-issued-v2-1_0.html)
- Non-Pre-Registered Relying Party/Decentralized Identifiers
- [OpenID for Verifiable Presentations - draft 20](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)
- [https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
- [OpenID for Verifiable Credential Issuance - draft 13](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
- Pre-Authorized Code Flow


Expand Down
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"proto:generate": "protoc --experimental_allow_proto3_optional --ts_proto_out=./src --proto_path=../proto identity/credentials.proto user/user.proto oid4vc/siopv2.proto --ts_proto_opt=esModuleInterop=true --ts_proto_opt=nestJs=true"
"proto:generate": "protoc --experimental_allow_proto3_optional --ts_proto_out=./src --proto_path=../proto identity/credentials.proto user/user.proto oid4vc/siopv2.proto oid4vc/oid4vci.proto oid4vc/oid4vp.proto --ts_proto_opt=esModuleInterop=true --ts_proto_opt=nestJs=true"
},
"dependencies": {
"@grpc/grpc-js": "^1.9.13",
Expand Down
4 changes: 2 additions & 2 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { AppService } from './app.service';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
import { WebAppModule } from './webapp/webapp.module';
import { UsersModule } from './users/user.module';
import { UserModule } from './user/user.module';

@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', '..', '..', '..', 'web', 'build'),
}),
WebAppModule,
UsersModule,
UserModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
26 changes: 19 additions & 7 deletions backend/src/oid4vc/oid4vc.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Module } from '@nestjs/common';
import { join } from 'path';

import { ClientProxyFactory, Transport } from '@nestjs/microservices';
import { SIOPV2Service } from './oid4vc.service';
import { OID4VCIService, OID4VPService, SIOPV2Service } from './oid4vc.service';
import { OID4VC_PACKAGE_NAME } from './siopv2';
import { ConfigModule, ConfigService } from '@nestjs/config';
import configuration from './configuration';
Expand All @@ -13,23 +13,35 @@ import configuration from './configuration';
controllers: [],
providers: [
SIOPV2Service,
OID4VPService,
OID4VCIService,
{
provide: OID4VC_PACKAGE_NAME,
useFactory: (configService: ConfigService) =>
ClientProxyFactory.create({
transport: Transport.GRPC,
options: {
package: 'oid4vc',
package: ['oid4vc'],
url: configService.getOrThrow('oid4vc_grpc_service_url'),
protoPath: join(
configService.getOrThrow('oid4vc_grpc_service_protopath'),
'siopv2.proto',
),
protoPath: [
join(
configService.getOrThrow('oid4vc_grpc_service_protopath'),
'siopv2.proto',
),
join(
configService.getOrThrow('oid4vc_grpc_service_protopath'),
'oid4vp.proto',
),
join(
configService.getOrThrow('oid4vc_grpc_service_protopath'),
'oid4vci.proto',
),
],
},
}),
inject: [ConfigService],
},
],
exports: [SIOPV2Service],
exports: [SIOPV2Service, OID4VPService, OID4VCIService],
})
export class OID4VCModule {}
107 changes: 104 additions & 3 deletions backend/src/oid4vc/oid4vc.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,28 @@ import { ClientGrpc } from '@nestjs/microservices';
import { lastValueFrom, timeout } from 'rxjs';
import {
OID4VC_PACKAGE_NAME,
RequestConfig,
Request,
SIOPV2RequestConfig,
SIOPV2Request,
SIOPV2Client,
S_IO_PV2_SERVICE_NAME,
} from './siopv2';
import { ConfigService } from '@nestjs/config';
import {
OID4VPClient,
OID4VPRequest,
OID4VPRequestConfig,
O_ID4_VP_SERVICE_NAME,
} from './oid4vp';
import {
OID4VCIClient,
O_ID4_VC_I_SERVICE_NAME,
Offer,
OfferConfig,
} from './oid4vci';

//
// SIOP
//
@Injectable()
export class SIOPV2Service implements OnModuleInit {
private siopV2Service: SIOPV2Client;
Expand All @@ -27,7 +42,9 @@ export class SIOPV2Service implements OnModuleInit {
);
}

async createRequest(request: RequestConfig): Promise<Request> {
async createSIOPV2Request(
request: SIOPV2RequestConfig,
): Promise<SIOPV2Request> {
this.logger.debug('Received create request', request);
try {
const buildRequest = await lastValueFrom(
Expand All @@ -47,3 +64,87 @@ export class SIOPV2Service implements OnModuleInit {
}
}
}

//
// OID4VP
//
@Injectable()
export class OID4VPService implements OnModuleInit {
private oid4vpService: OID4VPClient;

private readonly logger = new Logger(OID4VPService.name);

constructor(
@Inject(OID4VC_PACKAGE_NAME) private client: ClientGrpc,
private configService: ConfigService,
) {}

onModuleInit() {
this.oid4vpService = this.client.getService<OID4VPClient>(
O_ID4_VP_SERVICE_NAME,
);
}

async createOID4VPRequest(
request: OID4VPRequestConfig,
): Promise<OID4VPRequest> {
this.logger.debug('Received create request', request);
try {
const buildRequest = await lastValueFrom(
this.oid4vpService
.createRequest(request)
.pipe(
timeout(
this.configService.get<number>('oid4vc_grpc_service_timeout'),
),
),
);
this.logger.debug('build request', buildRequest);
return buildRequest;
} catch (error) {
this.logger.error(error);
throw error;
}
}
}

//
// OID4VCI
//
@Injectable()
export class OID4VCIService implements OnModuleInit {
private oid4vciService: OID4VCIClient;

private readonly logger = new Logger(OID4VCIService.name);

constructor(
@Inject(OID4VC_PACKAGE_NAME) private client: ClientGrpc,
private configService: ConfigService,
) {}

onModuleInit() {
this.oid4vciService = this.client.getService<OID4VCIClient>(
O_ID4_VC_I_SERVICE_NAME,
);
}

async createOID4VPRequest(request: OfferConfig): Promise<Offer> {
this.logger.debug('Received create request', request);
try {
const buildRequest = await lastValueFrom(
this.oid4vciService
.createOffer(request)
.pipe(
timeout(
this.configService.get<number>('oid4vc_grpc_service_timeout'),
),
),
);
this.logger.debug('build request', buildRequest);
return buildRequest;
} catch (error) {
this.logger.error(error);
throw error;
}
}
}
41 changes: 41 additions & 0 deletions backend/src/oid4vc/oid4vci.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint-disable */
import { GrpcMethod, GrpcStreamMethod } from "@nestjs/microservices";
import { Observable } from "rxjs";

export const protobufPackage = "oid4vc";

export interface OfferConfig {
credentials: string[];
}

export interface Offer {
uri: string;
offer: string;
}

export const OID4VC_PACKAGE_NAME = "oid4vc";

export interface OID4VCIClient {
createOffer(request: OfferConfig): Observable<Offer>;
}

export interface OID4VCIController {
createOffer(request: OfferConfig): Promise<Offer> | Observable<Offer> | Offer;
}

export function OID4VCIControllerMethods() {
return function (constructor: Function) {
const grpcMethods: string[] = ["createOffer"];
for (const method of grpcMethods) {
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
GrpcMethod("OID4VCI", method)(constructor.prototype[method], method, descriptor);
}
const grpcStreamMethods: string[] = [];
for (const method of grpcStreamMethods) {
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
GrpcStreamMethod("OID4VCI", method)(constructor.prototype[method], method, descriptor);
}
};
}

export const O_ID4_VC_I_SERVICE_NAME = "OID4VCI";
44 changes: 44 additions & 0 deletions backend/src/oid4vc/oid4vp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* eslint-disable */
import { GrpcMethod, GrpcStreamMethod } from "@nestjs/microservices";
import { Observable } from "rxjs";
import { Any } from "../google/protobuf/any";

export const protobufPackage = "oid4vc";

export interface OID4VPRequestConfig {
presentationDefinition: Any | undefined;
nonce?: string | undefined;
state?: string | undefined;
}

export interface OID4VPRequest {
uri: string;
request: string;
}

export const OID4VC_PACKAGE_NAME = "oid4vc";

export interface OID4VPClient {
createRequest(request: OID4VPRequestConfig): Observable<OID4VPRequest>;
}

export interface OID4VPController {
createRequest(request: OID4VPRequestConfig): Promise<OID4VPRequest> | Observable<OID4VPRequest> | OID4VPRequest;
}

export function OID4VPControllerMethods() {
return function (constructor: Function) {
const grpcMethods: string[] = ["createRequest"];
for (const method of grpcMethods) {
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
GrpcMethod("OID4VP", method)(constructor.prototype[method], method, descriptor);
}
const grpcStreamMethods: string[] = [];
for (const method of grpcStreamMethods) {
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
GrpcStreamMethod("OID4VP", method)(constructor.prototype[method], method, descriptor);
}
};
}

export const O_ID4_VP_SERVICE_NAME = "OID4VP";
36 changes: 5 additions & 31 deletions backend/src/oid4vc/siopv2.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,32 @@
/* eslint-disable */
import { GrpcMethod, GrpcStreamMethod } from "@nestjs/microservices";
import { Observable } from "rxjs";
import { Any } from "../google/protobuf/any";
import { Empty } from "../google/protobuf/empty";

export const protobufPackage = "oid4vc";

export interface RequestConfig {
presentationDefinition: Any | undefined;
export interface SIOPV2RequestConfig {
nonce?: string | undefined;
state?: string | undefined;
}

export interface Request {
export interface SIOPV2Request {
uri: string;
request: string;
}

export interface AuthRequest {
idToken?: string | undefined;
vpToken?: string | undefined;
issuerState?: string | undefined;
presentationSubmission?: Any | undefined;
}

export interface AuthTokenRequest {
did: string;
}

export interface Token {
token: string;
}

export const OID4VC_PACKAGE_NAME = "oid4vc";

export interface SIOPV2Client {
createRequest(request: RequestConfig): Observable<Request>;

verifyAuthResponse(request: AuthRequest): Observable<Empty>;

createAuthToken(request: AuthTokenRequest): Observable<Token>;
createRequest(request: SIOPV2RequestConfig): Observable<SIOPV2Request>;
}

export interface SIOPV2Controller {
createRequest(request: RequestConfig): Promise<Request> | Observable<Request> | Request;

verifyAuthResponse(request: AuthRequest): void;

createAuthToken(request: AuthTokenRequest): Promise<Token> | Observable<Token> | Token;
createRequest(request: SIOPV2RequestConfig): Promise<SIOPV2Request> | Observable<SIOPV2Request> | SIOPV2Request;
}

export function SIOPV2ControllerMethods() {
return function (constructor: Function) {
const grpcMethods: string[] = ["createRequest", "verifyAuthResponse", "createAuthToken"];
const grpcMethods: string[] = ["createRequest"];
for (const method of grpcMethods) {
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
GrpcMethod("SIOPV2", method)(constructor.prototype[method], method, descriptor);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { GrpcMethod } from '@nestjs/microservices';
import { User } from './interfaces/User';
import { WebAppService } from 'src/webapp/webapp.service';
import { Metadata, ServerUnaryCall } from '@grpc/grpc-js';
import { UsersService } from './users.service';
import { UserService } from './user.service';

@Injectable()
@Controller('user')
export class UsersController {
private readonly logger = new Logger(UsersController.name);
constructor(private readonly usersService: UsersService) {}
export class UserController {
private readonly logger = new Logger(UserController.name);
constructor(private readonly usersService: UserService) {}

@GrpcMethod('UsersService', 'ConnectUser')
async connectUser(
Expand Down

0 comments on commit e5a0f3c

Please sign in to comment.