Skip to content

Commit

Permalink
Implement accredit verify [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
DaevMithran committed Sep 24, 2024
1 parent 2522d16 commit 9c61a7e
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 2 deletions.
82 changes: 80 additions & 2 deletions src/controllers/api/did.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ import type { KeyImport } from '../../types/key.js';
import { eventTracker } from '../../services/track/tracker.js';
import { OperationCategoryNameEnum, OperationNameEnum } from '../../types/constants.js';
import type { ICredentialTrack, IDIDTrack, ITrackOperation } from '../../types/track.js';
import { arePublicKeyHexsInWallet } from '../../services/helpers.js';
import { arePublicKeyHexsInWallet, isCredentialIssuerDidDeactivated } from '../../services/helpers.js';
import { CheqdProviderErrorCodes } from '@cheqd/did-provider-cheqd';
import type { CheqdProviderError } from '@cheqd/did-provider-cheqd';
import { validate } from '../validator/decorator.js';
import { Credentials } from '../../services/api/credentials.js';
import { CredentialConnectors, type CredentialRequest } from '../../types/credential.js';
import { CredentialConnectors, VerifyCredentialRequestQuery, type CredentialRequest } from '../../types/credential.js';
import { CheqdW3CVerifiableCredential } from '../../services/w3c-credential.js';
import { DIDService } from '../../services/api/did.js';

export class DIDController {
public static createDIDValidator = [
Expand Down Expand Up @@ -936,4 +938,80 @@ export class DIDController {
} satisfies UnsuccessfulResolveDidResponseBody);
}
}

/**
* @openapi
*
* /did/accredit/verify/{:did}:
* post:
* tags: [ DID ]
* summary: Verify a verifiable accreditation for a DID.
* description: Generate and publish a verifiable accreditation for a subject DID accrediting schema's as a DID Linked resource.
* operationId: accredit
* parameters:
* - in: path
* name: did
* description: Accreditation identifier.
* schema:
* type: string
* required: true
* requestBody:
* content:
* application/x-www-form-urlencoded:
* schema:
* $ref: '#/components/schemas/DIDAccreditationRequest'
* application/json:
* schema:
* $ref: '#/components/schemas/DIDAccreditationRequest'
* responses:
* 200:
* description: The request was successful.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Credential'
* 400:
* $ref: '#/components/schemas/InvalidRequest'
* 401:
* $ref: '#/components/schemas/UnauthorizedError'
* 500:
* $ref: '#/components/schemas/InternalError'
*/
public async accreditVerify(request: Request, response: Response) {
// Extract did from params
const { did } = request.params as DIDAccreditationRequestParams;
const { verifyStatus, allowDeactivatedDid } = request.query as VerifyCredentialRequestQuery;

try {
const didUrl = request.params.did + getQueryParams(request.query);

const result = await DIDService.instance.verify_accreditation(
didUrl,
verifyStatus || true,
allowDeactivatedDid || false,
response.locals.customer
);
// Track operation
const trackInfo = {
category: OperationCategoryNameEnum.CREDENTIAL,
name: OperationNameEnum.CREDENTIAL_VERIFY,
customer: response.locals.customer,
user: response.locals.user,
data: {
did,
} satisfies ICredentialTrack,
} as ITrackOperation;

eventTracker.emit('track', trackInfo);
if (result.success) {
return response.status(StatusCodes.OK).json(result.data);
} else {
return response.status(result.status).json(result.error);
}
} catch (error) {
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: `Internal error: ${(error as Error)?.message || error}`,
} satisfies UnsuccessfulResolveDidResponseBody);
}
}
}
1 change: 1 addition & 0 deletions src/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { LitCompatibleCosmosChains, type DkgOptions, LitNetworks } from '@cheqd/
import { fromString } from 'uint8arrays';

import { config } from 'dotenv';
import type { SafeAPIResponse } from '../types/common.js';

config();

Expand Down
121 changes: 121 additions & 0 deletions src/services/api/did.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import type { CustomerEntity } from '../../database/entities/customer.entity';
import type { SafeAPIResponse } from '../../types/common';
import { DIDAccreditationTypes } from '../../types/did';
import { isCredentialIssuerDidDeactivated } from '../helpers';
import { IdentityServiceStrategySetup } from '../identity/index.js';
import { CheqdW3CVerifiableCredential } from '../w3c-credential';

export class DIDService {
public static instance = new DIDService();

async verify_accreditation(
didUrl: string,
verifyStatus: boolean,
allowDeactivatedDid: boolean,
customer: CustomerEntity
): Promise<SafeAPIResponse<{ verified: boolean; parentAccreditation?: boolean; rootAuthorisation?: boolean }>> {
// Get strategy e.g. postgres or local
const identityServiceStrategySetup = new IdentityServiceStrategySetup();

const res = await identityServiceStrategySetup.agent.resolve(didUrl);

const result = await res.json();

if (result.dereferencingMetadata) {
return {
success: false,
status: 404,
error: `DID Url ${didUrl} is not found`,
};
}

// Create credential object
const accreditation = new CheqdW3CVerifiableCredential(result);

if (!allowDeactivatedDid && (await isCredentialIssuerDidDeactivated(accreditation))) {
return {
success: false,
status: 400,
error: `Credential issuer DID is deactivated`,
};
}

const verifyResult = await identityServiceStrategySetup.agent.verifyCredential(
accreditation,
{
verifyStatus,
},
customer
);

if (verifyResult.error) {
return {
success: false,
status: 200,
error: `verify: ${verifyResult.error.message}`,
};
}

if (!Array.isArray(accreditation.type)) {
return {
success: false,
status: 200,
error: `invalid accreditation type`,
};
}

const accreditationTypes = Object.keys(DIDAccreditationTypes);
const isTypeAccreditation = accreditation.type.find((x) => accreditationTypes.includes(x));

if (!isTypeAccreditation) {
return {
success: false,
status: 200,
error: `invalid accreditation type`,
};
}

if (
isTypeAccreditation === DIDAccreditationTypes.VerifiableAccreditationToAccredit ||
isTypeAccreditation === DIDAccreditationTypes.VerifiableAccreditationToAttest
) {
const termsOfUse = accreditation.termsOfUse;
if (!termsOfUse || !termsOfUse.parentAccreditation || !termsOfUse.rootAuthorisation) {
return {
success: false,
status: 200,
error: `Invalid termsOfUse`,
};
}

const parentAccreditation = (
await this.verify_accreditation(
termsOfUse.parentAccreditation,
verifyStatus,
allowDeactivatedDid,
customer
)
).success;
const rootAuthorisation = (
await this.verify_accreditation(
termsOfUse.rootAuthorisation,
verifyStatus,
allowDeactivatedDid,
customer
)
).success;

return {
status: 200,
success: true,
data: { ...verifyResult, parentAccreditation, rootAuthorisation },
};
}

return {
status: 200,
success: true,
data: verifyResult,
};
}
}
1 change: 1 addition & 0 deletions src/services/w3c-credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class CheqdW3CVerifiableCredential extends CommonReturn implements ICheqd
proof: ProofType;
statusList?: StatusList2021Revocation | StatusList2021Suspension;
resourceId?: string;
termsOfUse?: Record<string, string>;

constructor(w3Credential: W3CVerifiableCredential) {
super();
Expand Down

0 comments on commit 9c61a7e

Please sign in to comment.