From 62da621f6c164362ecdb63a11d30d28172d4009c Mon Sep 17 00:00:00 2001
From: Berend Sliedrecht <berend@animo.id>
Date: Thu, 4 Jan 2024 15:42:58 +0100
Subject: [PATCH] feat(ssi): added did configuration for all dids

Signed-off-by: Berend Sliedrecht <berend@animo.id>
---
 apps/shared/src/events/didEvents.ts           | 25 +++++++
 .../src/agent/dids/dids.controller.ts         | 10 +++
 .../src/agent/dids/dids.service.ts            | 71 ++++++++++++++++++-
 apps/ssi-abstraction/test/dids.e2e-spec.ts    | 30 +++++++-
 4 files changed, 133 insertions(+), 3 deletions(-)

diff --git a/apps/shared/src/events/didEvents.ts b/apps/shared/src/events/didEvents.ts
index e300545..9a9ef39 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 688d688..2556483 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 7b0ad1e..12f8151 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 52c8a7b..383f514 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,
-- 
GitLab