Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve /did/create API [DEV-3197] #388

Merged
merged 45 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e3e87aa
Update package-lock.json.
abdulla-ashurov Sep 18, 2023
6473638
Remove assertionMethod from form based request
abdulla-ashurov Sep 18, 2023
0f18da3
Remove didDocument from form based request body in
abdulla-ashurov Sep 18, 2023
fe98497
Rename /did/create API request body section name
abdulla-ashurov Sep 18, 2023
c89a51d
Update DidCreateRequest type.
abdulla-ashurov Sep 18, 2023
1b22b73
Remove an unused assertionMethod variable for a
abdulla-ashurov Sep 18, 2023
b9200e1
Add service section for form based request body
abdulla-ashurov Sep 18, 2023
73ace30
Update DidCreateRequestFormBased request type.
abdulla-ashurov Sep 18, 2023
eaafd20
Add key section for form based request body in
abdulla-ashurov Sep 19, 2023
b169d99
Update swagger (OpenAPI).
abdulla-ashurov Sep 19, 2023
0579eef
Bump '@cheqd/sdk' version.
abdulla-ashurov Sep 21, 2023
d692f1c
Add options section for json based request body in
abdulla-ashurov Sep 21, 2023
86b53a1
Remove verificationMethod from json based request
abdulla-ashurov Sep 21, 2023
1f9302a
Add example for key field in json based request
abdulla-ashurov Sep 21, 2023
bc2d433
Merge branch 'develop' into DEV-3197
abdulla-ashurov Sep 21, 2023
6135b4f
Remove an unused expression:
abdulla-ashurov Sep 21, 2023
b1cef44
Remove an incorrect description from
abdulla-ashurov Sep 21, 2023
000f795
Add '@context' section for form based request body
abdulla-ashurov Sep 21, 2023
f7c2b48
Use an exist createDidVerificationMethod function
abdulla-ashurov Sep 25, 2023
85e8991
Create CreateDidRequestBody type for asserting
abdulla-ashurov Sep 25, 2023
647ba9d
Add integration tests for testing form-based request body in
abdulla-ashurov Sep 26, 2023
170eb2d
Add integration tests for testing JSON based
abdulla-ashurov Sep 27, 2023
7c061c5
Fix problem with handling multiple services
abdulla-ashurov Sep 27, 2023
bbd3067
Fix problem with handling multiple "context"
abdulla-ashurov Sep 27, 2023
162c0a1
Remove indirect imports in issuer.ts.
abdulla-ashurov Sep 27, 2023
8324e77
Remove an unused cast.
abdulla-ashurov Sep 27, 2023
f98cc77
Add negative integration tests for testing
abdulla-ashurov Sep 28, 2023
5149640
Merge branch 'DEV-3197' into DEV-3198
abdulla-ashurov Sep 28, 2023
9765d7e
Re-arch integration tests for did/create API.
abdulla-ashurov Sep 28, 2023
7911717
Refactor positive integration tests for
abdulla-ashurov Sep 28, 2023
38f72be
Update positive integration tests.
abdulla-ashurov Sep 28, 2023
1f6d1ef
Re-arch integration tests for /did/create API.
abdulla-ashurov Sep 28, 2023
c0f5fc9
Update negative integration tests.
abdulla-ashurov Sep 29, 2023
ebb999c
Update integration tests.
abdulla-ashurov Sep 29, 2023
dd18fc9
Update integration tests.
abdulla-ashurov Sep 29, 2023
0dfcf93
Refactor err msgs in negative integration tests.
abdulla-ashurov Sep 29, 2023
7d7f8b0
Update err messages in integration tests.
abdulla-ashurov Sep 29, 2023
d6b33eb
Merge branch 'develop' into DEV-3197
abdulla-ashurov Sep 29, 2023
e1368a9
Rename from DidCreateRequest to DidCreateRequestJson.
abdulla-ashurov Sep 29, 2023
94095c7
Add check to checking statusCodes is in 200...299
abdulla-ashurov Sep 29, 2023
2c01c1e
Update validation for DID.
abdulla-ashurov Sep 29, 2023
39c8bc0
Merge branch 'develop' into DEV-3197
abdulla-ashurov Sep 29, 2023
44d1228
Revert validation for DID.
abdulla-ashurov Sep 29, 2023
8d990bd
Fix validation for DidCreatePayload
Sep 29, 2023
dc3db0c
Update err messages in integration tests.
abdulla-ashurov Sep 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
],
"dependencies": {
"@cheqd/did-provider-cheqd": "^3.6.8",
"@cheqd/sdk": "^3.7.0",
"@cheqd/sdk": "^3.7.1",
"@cheqd/ts-proto": "^3.3.1",
"@cosmjs/amino": "^0.31.1",
"@cosmjs/encoding": "^0.31.1",
Expand Down
109 changes: 74 additions & 35 deletions src/controllers/issuer.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import type { Request, Response } from 'express';
import { check, param, validationResult } from 'express-validator';
import { fromString } from 'uint8arrays';
import type { DIDDocument, Service, VerificationMethod } from 'did-resolver';
import { fromString, toString } from 'uint8arrays';
import { v4 } from 'uuid';
import { MethodSpecificIdAlgo, VerificationMethods, CheqdNetwork } from '@cheqd/sdk';
import { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, Service, VerificationMethod, VerificationMethods, createDidVerificationMethod } from '@cheqd/sdk';
import type { MsgCreateResourcePayload } from '@cheqd/ts-proto/cheqd/resource/v2/index.js';
import { StatusCodes } from 'http-status-codes';

import { IdentityServiceStrategySetup } from '../services/identity/index.js';
import { generateDidDoc, getQueryParams, validateSpecCompliantPayload } from '../helpers/helpers.js';
import { generateDidDoc, getQueryParams, validateDidCreatePayload, validateSpecCompliantPayload } from '../helpers/helpers.js';
import { DIDMetadataDereferencingResult, DefaultResolverUrl } from '@cheqd/did-provider-cheqd';
import { bases } from "multiformats/basics";
import { base64ToBytes } from "did-jwt";
import type { CreateDidRequestBody } from '../types/shared.js';

export class IssuerController {
public static createValidator = [
check('didDocument')
.optional()
.isObject()
.custom((value) => {
const { valid } = validateSpecCompliantPayload(value);
const { valid } = validateDidCreatePayload(value);
return valid;
})
.withMessage('Invalid didDocument'),
Expand All @@ -26,11 +27,11 @@ export class IssuerController {
.isString()
.isIn([VerificationMethods.Ed255192020, VerificationMethods.Ed255192018, VerificationMethods.JWK])
.withMessage('Invalid verificationMethod'),
check('methodSpecificIdAlgo')
check('identifierFormatType')
.optional()
.isString()
.isIn([MethodSpecificIdAlgo.Base58, MethodSpecificIdAlgo.Uuid])
.withMessage('Invalid methodSpecificIdAlgo'),
.withMessage('Invalid identifierFormatType'),
check('network')
.optional()
.isString()
Eengineer1 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -187,10 +188,10 @@ export class IssuerController {
* content:
* application/x-www-form-urlencoded:
* schema:
* $ref: '#/components/schemas/DidCreateRequest'
* $ref: '#/components/schemas/DidCreateRequestFormBased'
* application/json:
* schema:
* $ref: '#/components/schemas/DidCreateRequest'
* $ref: '#/components/schemas/DidCreateRequestJson'
* responses:
* 200:
* description: The request was successful.
Expand Down Expand Up @@ -226,45 +227,83 @@ export class IssuerController {
}

const {
methodSpecificIdAlgo,
identifierFormatType,
network,
verificationMethodType,
assertionMethod = true,
serviceEndpoint,
} = request.body;
service,
key,
options,
} = request.body satisfies CreateDidRequestBody;
let didDocument: DIDDocument;
try {
if (request.body.didDocument) {
Eengineer1 marked this conversation as resolved.
Show resolved Hide resolved
didDocument = request.body.didDocument;
if (options) {
const publicKeyHex = options.key || (await new IdentityServiceStrategySetup(response.locals.customerId).agent.createKey(
'Ed25519',
response.locals.customerId
)).publicKeyHex;
const pkBase64 = publicKeyHex.length == 43 ? publicKeyHex : toString(fromString(publicKeyHex, 'hex'), 'base64');

didDocument.verificationMethod = createDidVerificationMethod([options.verificationMethodType], [{
methodSpecificId: bases['base58btc'].encode(base64ToBytes(pkBase64)),
didUrl: didDocument.id,
keyId: `${didDocument.id}#key-1`,
publicKey: pkBase64
}]);
} else {
return response.status(StatusCodes.BAD_REQUEST).json({
error: 'Provide options section to create a DID',
});
}
} else if (verificationMethodType) {
const key = await new IdentityServiceStrategySetup(response.locals.customerId).agent.createKey(
const publicKeyHex = key || (await new IdentityServiceStrategySetup(response.locals.customerId).agent.createKey(
'Ed25519',
response.locals.customerId
);
)).publicKeyHex;
didDocument = generateDidDoc({
verificationMethod: verificationMethodType || VerificationMethods.Ed255192018,
verificationMethod: verificationMethodType,
verificationMethodId: 'key-1',
methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid,
methodSpecificIdAlgo: identifierFormatType || MethodSpecificIdAlgo.Uuid,
network,
publicKey: key.publicKeyHex,
publicKey: publicKeyHex,
});

if (assertionMethod) {
didDocument.assertionMethod = didDocument.authentication;
if (Array.isArray(request.body['@context'])) {
didDocument['@context'] = request.body['@context'];
}
if (typeof request.body['@context'] === 'string') {
didDocument['@context'] = [request.body['@context']];
}

if (serviceEndpoint) {
didDocument.service = [
{
id: `${didDocument.id}#service-1`,
type: 'service-1',
serviceEndpoint: [serviceEndpoint],
},
];
if (service) {
if (Array.isArray(service)) {
try {
const services = JSON.parse(`[${service.toString()}]`);
didDocument.service = [];
for (const service of services) {
didDocument.service.push({
id: `${didDocument.id}#${service.idFragment}`,
type: service.type,
serviceEndpoint: service.serviceEndpoint,
})
}
} catch (e) {
return response.status(StatusCodes.BAD_REQUEST).json({
error: 'Provide the correct service section to create a DID',
});
};
} else {
didDocument.service = [{
id: `${didDocument.id}#${service.idFragment}`,
type: service.type,
serviceEndpoint: service.serviceEndpoint,
}];
}
}
} else {
return response.status(StatusCodes.BAD_REQUEST).json({
error: 'Provide a DID Document or the network type to create a DID',
error: 'Provide a DID Document or the VerificationMethodType to create a DID',
});
}

Expand Down Expand Up @@ -507,7 +546,7 @@ export class IssuerController {
if (result) {
const url = new URL(
`${process.env.RESOLVER_URL || DefaultResolverUrl}${did}?` +
`resourceId=${resourcePayload.id}&resourceMetadata=true`
`resourceId=${resourcePayload.id}&resourceMetadata=true`
);
const didDereferencing = (await (await fetch(url)).json()) as DIDMetadataDereferencingResult;

Expand Down Expand Up @@ -651,11 +690,11 @@ export class IssuerController {
try {
const did = request.params.did
? await new IdentityServiceStrategySetup(response.locals.customerId).agent.resolveDid(
request.params.did
)
request.params.did
)
: await new IdentityServiceStrategySetup(response.locals.customerId).agent.listDids(
response.locals.customerId
);
response.locals.customerId
);

return response.status(StatusCodes.OK).json(did);
} catch (error) {
Expand Down
14 changes: 12 additions & 2 deletions src/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ export function toDefaultDkg(did: string): DkgOptions {
}
}

export function validateDidCreatePayload(didDocument: DIDDocument): SpecValidationResult {
if (!didDocument) return { valid: true };

// id is required, validated on both compile and runtime
if (!didDocument.id || !didDocument.id.startsWith('did:cheqd:')) return { valid: false, error: 'id is required' };

if (!isValidService(didDocument)) return { valid: false, error: 'Service is Invalid' };
return { valid: true } as SpecValidationResult;
}

export function validateSpecCompliantPayload(didDocument: DIDDocument): SpecValidationResult {
// id is required, validated on both compile and runtime
if (!didDocument.id && !didDocument.id.startsWith('did:cheqd:')) return { valid: false, error: 'id is required' };
Expand All @@ -92,8 +102,8 @@ export function validateSpecCompliantPayload(didDocument: DIDDocument): SpecVali
export function isValidService(didDocument: DIDDocument): boolean {
return didDocument.service
? didDocument?.service?.every((s) => {
return s?.serviceEndpoint && s?.id && s?.type;
})
return s?.serviceEndpoint && s?.id && s?.type;
})
: true;
}

Expand Down
Loading
Loading