Skip to content

Commit

Permalink
added support for generating web did with multiple hosts
Browse files Browse the repository at this point in the history
  • Loading branch information
holashchand committed May 30, 2024
1 parent d1a5b26 commit b9dbe34
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 30 deletions.
2 changes: 1 addition & 1 deletion services/identity-service/docker-compose-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3'
version: '2.4'
services:
vault-test:
image: vault:1.13.3
Expand Down
2 changes: 1 addition & 1 deletion services/identity-service/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3'
version: '2.4'
services:
vault:
image: vault:1.13.3
Expand Down
8 changes: 4 additions & 4 deletions services/identity-service/src/did/did.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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<DIDDocument[]> {
const promises = generateRequest.content.map((generateDidDTO) => {
return limit(() => this.didService.generateDID(generateDidDTO));
Expand All @@ -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);
}
}

Expand Down
11 changes: 5 additions & 6 deletions services/identity-service/src/did/did.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -74,34 +74,33 @@ 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: [],
method: "web"
});
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");
});
Expand Down
46 changes: 28 additions & 18 deletions services/identity-service/src/did/did.service.ts
Original file line number Diff line number Diff line change
@@ -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();
}
Expand All @@ -40,22 +37,35 @@ 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, webDidBaseUrl?: string): string {
if(id) return id;
if (method === 'web') {
return this.getWebDidIdForId(uuid());
return this.getWebDidIdForId(uuid(), webDidBaseUrl);
}
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<DIDDocument> {
// Create a UUID for the DID using uuidv4
const didUri: string = this.generateDidUri(doc?.method, doc?.id);
const didUri: string = this.generateDidUri(doc?.method, doc?.id, doc?.webDidBaseUrl);

// Create private/public key pair
let authnKeys;
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,39 @@ export class GenerateDidDTO {
'AlsoKnownAs property is a unique combination aadhaar and username.',
type: String,
isArray: true,
required: false,
})
alsoKnownAs?: string[];

@ApiProperty({
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;
@ApiProperty({
required: false,
description: 'In case of method "web" the web url path to access the did document. It would be appended by generated uuid',
})
webDidBaseUrl?: string;
}

export class GenerateDidRequestDTO {
@ApiProperty({
type: GenerateDidDTO,
description: 'List of generate did requests',
isArray: true
})
content: GenerateDidDTO[]
}

0 comments on commit b9dbe34

Please sign in to comment.