diff --git a/apps/shared/src/events/didEvents.ts b/apps/shared/src/events/didEvents.ts index e300545be74213c4172a34a216ecdd7eaf50c21a..9a9ef396a706eb62dd1f6ae1dd4534b01237222c 100644 --- a/apps/shared/src/events/didEvents.ts +++ b/apps/shared/src/events/didEvents.ts @@ -37,3 +37,28 @@ export class EventDidsRegisterIndyFromSeed extends BaseEvent<Array<string>> { ); } } + +export type DidConfiguration = { + entries: Array<{ did: string; jwt: string }>; +}; +export type EventDidsDidConfigurationInput = BaseEventInput<{ + domain: string; + expiryTime: number; +}>; +export class EventDidsDidConfiguration extends BaseEvent<DidConfiguration> { + public static token = 'dids.didConfiguration'; + + public get instance() { + return this.data; + } + + public static fromEvent(e: EventDidsDidConfiguration) { + return new EventDidsDidConfiguration( + e.data, + e.tenantId, + e.id, + e.type, + e.timestamp, + ); + } +} diff --git a/apps/ssi-abstraction/src/agent/dids/dids.controller.ts b/apps/ssi-abstraction/src/agent/dids/dids.controller.ts index 688d68862c41553b2e9d83fc60d16aad11a741a1..2556483468aee2e342aa404e2de312904dff46c2 100644 --- a/apps/ssi-abstraction/src/agent/dids/dids.controller.ts +++ b/apps/ssi-abstraction/src/agent/dids/dids.controller.ts @@ -1,6 +1,8 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { + EventDidsDidConfiguration, + EventDidsDidConfigurationInput, EventDidsRegisterIndyFromSeed, EventDidsRegisterIndyFromSeedInput, EventDidsResolve, @@ -21,6 +23,14 @@ export class DidsController { ); } + @MessagePattern(EventDidsDidConfiguration.token) + public async getDidConfiguration(options: EventDidsDidConfigurationInput) { + return new EventDidsDidConfiguration( + await this.didsService.getDidConfiguration(options), + options.tenantId, + ); + } + @MessagePattern(EventDidsResolve.token) public async resolve(options: EventDidsResolveInput) { return new EventDidsResolve( diff --git a/apps/ssi-abstraction/src/agent/dids/dids.service.ts b/apps/ssi-abstraction/src/agent/dids/dids.service.ts index 7b0ad1ec1682b95964a3e21d9ae619bca4787478..12f81517ae9289e77e654ba1a83ff9c6d94c48e5 100644 --- a/apps/ssi-abstraction/src/agent/dids/dids.service.ts +++ b/apps/ssi-abstraction/src/agent/dids/dids.service.ts @@ -1,6 +1,18 @@ -import type { EventDidsResolveInput } from '@ocm/shared'; +import type { + DidConfiguration, + EventDidsDidConfiguration, + EventDidsDidConfigurationInput, + EventDidsResolveInput, +} from '@ocm/shared'; -import { KeyType, TypedArrayEncoder } from '@aries-framework/core'; +import { + JsonEncoder, + JwaSignatureAlgorithm, + JwsService, + KeyType, + TypedArrayEncoder, + getKeyFromVerificationMethod, +} from '@aries-framework/core'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; @@ -33,6 +45,61 @@ export class DidsService { }); } + public async getDidConfiguration({ + domain, + expiryTime, + tenantId, + }: EventDidsDidConfigurationInput): Promise< + EventDidsDidConfiguration['data'] + > { + return this.withTenantService.invoke(tenantId, async (t) => { + const indyDids = t.dids.getCreatedDids({method: 'indy'}); + const sovDids = t.dids.getCreatedDids({method: 'sov'}); + const webDids = t.dids.getCreatedDids({method: 'web'}); + const dids = (await Promise.all([indyDids, sovDids, webDids])).flatMap((d) => d) + + const jwtEntries: DidConfiguration['entries'] = []; + const jwsService = t.dependencyManager.resolve(JwsService); + + for (const { did, didDocument } of dids) { + const payload = { + iss: did, + exp: expiryTime, + domain, + }; + + const encodedPayload = TypedArrayEncoder.fromString( + JsonEncoder.toString(payload), + ); + + if ( + !didDocument || + !didDocument.verificationMethod || + !didDocument.verificationMethod[0] + ) { + continue; + } + + const vm = didDocument.verificationMethod[0]; + + const key = getKeyFromVerificationMethod(vm); + + const jws = await jwsService.createJwsCompact(t.context, { + key, + payload: encodedPayload, + protectedHeaderOptions: { + alg: JwaSignatureAlgorithm.EdDSA, + kid: did, + }, + }); + + jwtEntries.push({ did, jwt: jws }); + } + + return { entries: jwtEntries }; + }); + } + public async registerDidIndyFromSeed({ tenantId, seed, diff --git a/apps/ssi-abstraction/test/dids.e2e-spec.ts b/apps/ssi-abstraction/test/dids.e2e-spec.ts index 52c8a7b66caaba8d59e7d2c64d98ce725e977532..383f514438dc0594de873082f7bd7f873878f161 100644 --- a/apps/ssi-abstraction/test/dids.e2e-spec.ts +++ b/apps/ssi-abstraction/test/dids.e2e-spec.ts @@ -1,13 +1,18 @@ import type { INestApplication } from '@nestjs/common'; import type { ClientProxy } from '@nestjs/microservices'; import type { + EventDidsDidConfigurationInput, EventDidsRegisterIndyFromSeedInput, EventDidsResolveInput, } from '@ocm/shared'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { EventDidsRegisterIndyFromSeed, EventDidsResolve } from '@ocm/shared'; +import { + EventDidsDidConfiguration, + EventDidsRegisterIndyFromSeed, + EventDidsResolve, +} from '@ocm/shared'; import { firstValueFrom } from 'rxjs'; import { AgentModule } from '../src/agent/agent.module.js'; @@ -70,6 +75,29 @@ describe('Dids', () => { ); }); + it(EventDidsDidConfiguration.token, async () => { + const response$ = client.send< + EventDidsDidConfiguration, + EventDidsDidConfigurationInput + >(EventDidsDidConfiguration.token, { + domain: 'https://example.org', + expiryTime: new Date().getTime() / 1000 + 3600, + tenantId, + }); + + const response = await firstValueFrom(response$); + const eventInstance = EventDidsDidConfiguration.fromEvent(response); + + expect(eventInstance.instance).toMatchObject({ + entries: expect.arrayContaining([ + expect.objectContaining({ + did: 'did:indy:bcovrin:test:9MMeff63VnCpogD2FWfKnJ', + jwt: expect.any(String), + }), + ]), + }); + }); + it(EventDidsResolve.token, async () => { const response$ = client.send<EventDidsResolve, EventDidsResolveInput>( EventDidsResolve.token,