diff --git a/services/identity-service/docker-compose-test.yml b/services/identity-service/docker-compose-test.yml index d35d1586d..19c605ae7 100644 --- a/services/identity-service/docker-compose-test.yml +++ b/services/identity-service/docker-compose-test.yml @@ -1,4 +1,4 @@ -version: '3' +version: '2.4' services: vault-test: image: vault:1.13.3 diff --git a/services/identity-service/docker-compose.yml b/services/identity-service/docker-compose.yml index dd47cf716..32dd8dfd0 100644 --- a/services/identity-service/docker-compose.yml +++ b/services/identity-service/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3' +version: '2.4' services: vault: image: vault:1.13.3 diff --git a/services/identity-service/src/did/did.controller.ts b/services/identity-service/src/did/did.controller.ts index 4b3e53dd6..1a5bb8e20 100644 --- a/services/identity-service/src/did/did.controller.ts +++ b/services/identity-service/src/did/did.controller.ts @@ -21,7 +21,7 @@ import { const { DIDDocument } = require('did-resolver'); type DIDDocument = typeof DIDDocument; import { DidService } from './did.service'; -import { GenerateDidDTO } from './dtos/GenerateDid.dto'; +import { GenerateDidRequestDTO } from './dtos/GenerateDidRequest.dto'; const pLimit = require('p-limit'); const limit = pLimit(100); @@ -33,10 +33,10 @@ export class DidController { @ApiOperation({ summary: 'Generate a new DID' }) @ApiOkResponse({ description: 'DID Generated', isArray: true }) @ApiBadRequestResponse({ description: 'Bad request' }) - @ApiBody({ type: GenerateDidDTO, isArray: true }) + @ApiBody({ type: GenerateDidRequestDTO, isArray: false }) @Post('/did/generate') async generateDID( - @Body() generateRequest: { content: GenerateDidDTO[] }, + @Body() generateRequest: GenerateDidRequestDTO, ): Promise { const promises = generateRequest.content.map((generateDidDTO) => { return limit(() => this.didService.generateDID(generateDidDTO)); @@ -45,7 +45,7 @@ export class DidController { return await Promise.all(promises); } catch (err) { Logger.error(err); - throw new InternalServerErrorException(`Error generating one or more DIDs`); + throw new InternalServerErrorException(err?.message); } } diff --git a/services/identity-service/src/did/did.service.spec.ts b/services/identity-service/src/did/did.service.spec.ts index 1d297b8aa..c154129cb 100644 --- a/services/identity-service/src/did/did.service.spec.ts +++ b/services/identity-service/src/did/did.service.spec.ts @@ -2,7 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { DidService } from './did.service'; import { PrismaService } from '../utils/prisma.service'; import { VaultService } from '../utils/vault.service'; -import { GenerateDidDTO } from './dtos/GenerateDid.dto'; +import { GenerateDidDTO } from './dtos/GenerateDidRequest.dto'; import { ConfigService } from '@nestjs/config'; describe('DidService', () => { @@ -74,20 +74,20 @@ describe('DidService', () => { }); it("generate web did id test", () => { - service.webDidBaseUrl = "did:web:example.com:identity:"; + service.webDidPrefix = "did:web:example.com:identity:"; const didId = service.generateDidUri("web"); expect(didId).toBeDefined(); expect(didId).toContain("did:web:example.com:identity"); }); it("get web did id for id test", () => { - service.webDidBaseUrl = "did:web:example.com:identity:"; + service.webDidPrefix = "did:web:example.com:identity:"; const didId = service.getWebDidIdForId("abc"); expect(didId).toBeDefined(); expect(didId).toEqual("did:web:example.com:identity:abc"); }); it('should generate a DID with a web method', async () => { - service.webDidBaseUrl = "did:web:example.com:identity:"; + service.webDidPrefix = "did:web:example.com:identity:"; const result = await service.generateDID({ alsoKnownAs: [], services: [], @@ -95,13 +95,12 @@ describe('DidService', () => { }); expect(result).toBeDefined(); expect(result.verificationMethod).toBeDefined(); - expect(result.verificationMethod[0].publicKeyJwk).toBeDefined(); expect(result.id.split(':')[1]).toEqual('web'); expect(result.id).toContain("did:web:example.com:identity"); }); it("throw exception when web did base url is not set", () => { - service.webDidBaseUrl = undefined; + service.webDidPrefix = undefined; expect(() => service.getWebDidIdForId("abc")) .toThrow("Web did base url not found"); }); diff --git a/services/identity-service/src/did/did.service.ts b/services/identity-service/src/did/did.service.ts index 3f7ea9636..7bfc34311 100644 --- a/services/identity-service/src/did/did.service.ts +++ b/services/identity-service/src/did/did.service.ts @@ -1,26 +1,23 @@ import { Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../utils/prisma.service'; -const { DIDDocument } = require('did-resolver'); -type DIDDocument = typeof DIDDocument; import { v4 as uuid } from 'uuid'; -import { GenerateDidDTO } from './dtos/GenerateDid.dto'; import { VaultService } from '../utils/vault.service'; import { Identity } from '@prisma/client'; -import { RSAKeyPair } from "crypto-ld"; +import { RSAKeyPair } from 'crypto-ld'; +import { GenerateDidDTO } from './dtos/GenerateDidRequest.dto'; + +const { DIDDocument } = require('did-resolver'); +type DIDDocument = typeof DIDDocument; + @Injectable() export class DidService { keys = {} - webDidBaseUrl: string; + webDidPrefix: string; signingAlgorithm: string; didResolver: any; constructor(private prisma: PrismaService, private vault: VaultService) { let baseUrl: string = process.env.WEB_DID_BASE_URL; - if(baseUrl && typeof baseUrl === "string") { - baseUrl = baseUrl.replace("https://", "") - .replace(/:/g, "%3A") - .replace(/\//g, ":"); - this.webDidBaseUrl = `did:web:${baseUrl}:`; - } + this.webDidPrefix = this.getDidPrefixForBaseUrl(baseUrl); this.signingAlgorithm = process.env.SIGNING_ALGORITHM; this.init(); } @@ -40,7 +37,16 @@ export class DidService { }); } - generateDidUri(method: string, id: string): string { + getDidPrefixForBaseUrl(baseUrl: string): string { + if(!baseUrl || typeof baseUrl !== "string") return ''; + baseUrl = baseUrl.split("?")[0] + .replace("https://", "") + .replace(/:/g, "%3A") + .replace(/\//g, ":"); + return `did:web:${baseUrl}:`; + } + + generateDidUri(method: string, id?: string): string { if(id) return id; if (method === 'web') { return this.getWebDidIdForId(uuid()); @@ -48,9 +54,13 @@ export class DidService { return `did:${(method && method.trim() !== '') ? method.trim() : 'rcw'}:${uuid()}`; } - getWebDidIdForId(id: string): string { - if(!this.webDidBaseUrl) throw new NotFoundException("Web did base url not found"); - return `${this.webDidBaseUrl}${id}`; + getWebDidIdForId(id: string, webDidBaseUrl?: string): string { + if(!this.webDidPrefix && !webDidBaseUrl) throw new NotFoundException("Web did base url not found"); + if(webDidBaseUrl) { + const webDidBasePrefix = this.getDidPrefixForBaseUrl(webDidBaseUrl); + return `${webDidBasePrefix}${id}`; + } + return `${this.webDidPrefix}${id}`; } async generateDID(doc: GenerateDidDTO): Promise { @@ -149,7 +159,7 @@ export class DidService { throw new InternalServerErrorException(`Error fetching DID: ${id} from db`); } - if(!artifact && id?.startsWith("did:web") && !id?.startsWith(this.webDidBaseUrl)) { + if(!artifact && id?.startsWith("did:web") && !id?.startsWith(this.webDidPrefix)) { try { return (await this.didResolver.resolve(id)).didDocument; } catch (err) { diff --git a/services/identity-service/src/did/dtos/GenerateDid.dto.ts b/services/identity-service/src/did/dtos/GenerateDidRequest.dto.ts similarity index 63% rename from services/identity-service/src/did/dtos/GenerateDid.dto.ts rename to services/identity-service/src/did/dtos/GenerateDidRequest.dto.ts index b58a24df7..52497f235 100644 --- a/services/identity-service/src/did/dtos/GenerateDid.dto.ts +++ b/services/identity-service/src/did/dtos/GenerateDidRequest.dto.ts @@ -8,6 +8,7 @@ export class GenerateDidDTO { 'AlsoKnownAs property is a unique combination aadhaar and username.', type: String, isArray: true, + required: false, }) alsoKnownAs?: string[]; @@ -15,11 +16,26 @@ export class GenerateDidDTO { description: 'An array of services that are used, for example a user registration service.', isArray: true, + required: false, + type: Service }) services?: Service[]; @ApiProperty({ description: 'The method of DID.', }) method: string; + @ApiProperty({ + required: false, + description: 'Specific ID to generate DID document with.', + }) id?: string; } + +export class GenerateDidRequestDTO { + @ApiProperty({ + type: GenerateDidDTO, + description: 'List of generate did requests', + isArray: true + }) + content: GenerateDidDTO[] +}