From ff55663888c2b155d6eef555b452c3fc404b6a70 Mon Sep 17 00:00:00 2001 From: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Date: Fri, 25 Aug 2023 17:39:50 +0530 Subject: [PATCH] feat: Support out of band verification (#59) * feat: Support Out-Of-Band verification Signed-off-by: KulkarniShashank * Added the qrcode package in package.json Signed-off-by: KulkarniShashank * Added the QR code for the out-of-band verification Signed-off-by: KulkarniShashank * change the error message on the dto Signed-off-by: KulkarniShashank --------- Signed-off-by: KulkarniShashank --- .../src/agent-service.controller.ts | 5 + .../src/agent-service.service.ts | 22 +- .../src/interface/agent-service.interface.ts | 2 +- .../src/verification/dto/request-proof.dto.ts | 66 +++- .../verification/verification.controller.ts | 34 +- .../src/verification/verification.service.ts | 13 +- .../src/interfaces/verification.interface.ts | 5 +- .../repositories/verification.repository.ts | 35 +- .../src/verification.controller.ts | 5 + apps/verification/src/verification.service.ts | 330 ++++++++++++++---- .../out-of-band-verification.template.ts | 61 ++++ libs/common/src/common.constant.ts | 2 + libs/common/src/dtos/email.dto.ts | 10 + libs/common/src/response-messages/index.ts | 7 +- libs/common/src/send-grid-helper-file.ts | 5 +- package.json | 2 +- 16 files changed, 507 insertions(+), 97 deletions(-) create mode 100644 apps/verification/templates/out-of-band-verification.template.ts diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 3b53fdb1c..42c140202 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -96,4 +96,9 @@ export class AgentServiceController { async getAgentHealth(payload: { user: user, orgId: number }): Promise { return this.agentServiceService.getAgentHealthDetails(payload.orgId); } + + @MessagePattern({ cmd: 'agent-send-out-of-band-proof-request' }) + async sendOutOfBandProofRequest(payload: { proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string }): Promise { + return this.agentServiceService.sendOutOfBandProofRequest(payload.proofRequestPayload, payload.url, payload.apiKey); + } } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index b76fd1faf..0af735840 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -851,17 +851,29 @@ export class AgentServiceService { } if (orgAgentDetails.agentEndPoint) { const data = await this.commonService - .httpGet(`${orgAgentDetails.agentEndPoint}/agent`, { headers: { 'x-api-key': '' } }) - .then(async response => response); + .httpGet(`${orgAgentDetails.agentEndPoint}/agent`, { headers: { 'x-api-key': '' } }) + .then(async response => response); return data; } else { - throw new NotFoundException(ResponseMessages.agent.error.agentUrl); - } - + throw new NotFoundException(ResponseMessages.agent.error.agentUrl); + } + } catch (error) { this.logger.error(`Agent health details : ${JSON.stringify(error)}`); throw new RpcException(error); } } + + async sendOutOfBandProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string): Promise { + try { + const sendProofRequest = await this.commonService + .httpPost(url, proofRequestPayload, { headers: { 'x-api-key': apiKey } }) + .then(async response => response); + return sendProofRequest; + } catch (error) { + this.logger.error(`Error in send out of band proof request in agent service : ${JSON.stringify(error)}`); + throw new RpcException(error); + } + } } diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index e2b76d098..81ed67264 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -237,7 +237,7 @@ export interface IAttributes { } export interface ISendProofRequestPayload { comment: string; - connectionId: string; + connectionId?: string; proofFormats: IProofFormats; autoAcceptProof: string; } diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index aa6712f73..4a5ee350e 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,4 +1,4 @@ -import { IsArray, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString, MaxLength } from 'class-validator'; +import { IsArray, IsEmail, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString, MaxLength } from 'class-validator'; import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; @@ -7,19 +7,19 @@ import { Transform } from 'class-transformer'; class IProofRequestAttribute { @IsString() attributeName: string; - + @IsString() condition?: string; - + @IsString() - value?: string; - + value?: string; + @IsString() credDefId?: string; - + @IsString() schemaId: string; - } +} export class RequestProof { @ApiProperty() @@ -54,13 +54,59 @@ export class RequestProof { @IsNotEmpty({ message: 'please provide orgId' }) orgId: number; - @IsString({ message: 'autoAcceptProof must be in string' }) - @IsNotEmpty({ message: 'please provide valid autoAcceptProof' }) + @IsString({ message: 'auto accept proof must be in string' }) + @IsNotEmpty({ message: 'please provide valid auto accept proof' }) @IsOptional() autoAcceptProof: string; @IsString({ message: 'protocolVersion must be in string' }) - @IsNotEmpty({ message: 'please provide valid protocolVersion' }) + @IsNotEmpty({ message: 'please provide valid protocol version' }) + @IsOptional() + protocolVersion: string; +} + +export class OutOfBandRequestProof { + @ApiProperty({ + 'example': [ + { + attributeName: 'attributeName', + condition: '>=', + value: 'predicates', + credDefId: '', + schemaId: '' + } + ] + }) + @IsArray({ message: 'attributes must be in array' }) + @IsObject({ each: true }) + @IsNotEmpty({ message: 'please provide valid attributes' }) + attributes: IProofRequestAttribute[]; + + @ApiProperty({ example: 'string' }) + @IsNotEmpty({ message: 'Please provide valid emailId' }) + @Transform(({ value }) => trim(value)) + @Transform(({ value }) => toLowerCase(value)) + @IsNotEmpty({ message: 'Email is required.' }) + @MaxLength(256, { message: 'Email must be at most 256 character.' }) + @IsEmail() + emailId: string; + + @ApiProperty() + @IsOptional() + comment: string; + + @ApiProperty() + @IsNumber() + @IsNotEmpty({ message: 'please provide orgId' }) + orgId: number; + + @IsString({ message: 'autoAcceptProof must be in string' }) + @IsNotEmpty({ message: 'please provide valid auto accept proof' }) + @IsOptional() + autoAcceptProof: string; + + @IsString({ message: 'protocol version must be in string' }) + @IsNotEmpty({ message: 'please provide valid protocol version' }) @IsOptional() protocolVersion: string; } diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 6d1e7fa8a..e010ba506 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -16,7 +16,7 @@ import { Controller, Logger, Post, Body, Get, Query, HttpStatus, Res, UseGuards, import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; -import { RequestProof } from './dto/request-proof.dto'; +import { OutOfBandRequestProof, RequestProof } from './dto/request-proof.dto'; import { GetUser } from '../authz/decorators/get-user.decorator'; import { VerificationService } from './verification.service'; import IResponseType from '@credebl/common/interfaces/response.interface'; @@ -205,5 +205,37 @@ export class VerificationController { }; return res.status(HttpStatus.CREATED).json(finalResponse); } + + /** + * Out-Of-Band Proof Presentation + * @param user + * @param outOfBandRequestProof + * @returns Get out-of-band requested proof presentation details + */ + @Post('/proofs/create-request-oob') + @ApiTags('verifications') + @ApiOperation({ + summary: `Sends a out-of-band proof request`, + description: `Sends a out-of-band proof request` + }) + @ApiResponse({ status: 201, description: 'Success', type: ApiResponseDto }) + @ApiUnauthorizedResponse({ status: 401, description: 'Unauthorized', type: UnauthorizedErrorDto }) + @ApiForbiddenResponse({ status: 403, description: 'Forbidden', type: ForbiddenErrorDto }) + @ApiBody({ type: OutOfBandRequestProof }) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + async sendOutOfBandPresentationRequest( + @Res() res: Response, + @GetUser() user: IUserRequest, + @Body() outOfBandRequestProof: OutOfBandRequestProof + ): Promise { + const sendProofRequest = await this.verificationService.sendOutOfBandPresentationRequest(outOfBandRequestProof, user); + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.verification.success.fetch, + data: sendProofRequest.response + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } } diff --git a/apps/api-gateway/src/verification/verification.service.ts b/apps/api-gateway/src/verification/verification.service.ts index 3dcc8ec8a..c8b6ed973 100644 --- a/apps/api-gateway/src/verification/verification.service.ts +++ b/apps/api-gateway/src/verification/verification.service.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; -import { RequestProof } from './dto/request-proof.dto'; +import { OutOfBandRequestProof, RequestProof } from './dto/request-proof.dto'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { WebhookPresentationProof } from './dto/webhook-proof.dto'; @@ -64,4 +64,15 @@ export class VerificationService extends BaseService { const payload = { id, proofPresentationPayload }; return this.sendNats(this.verificationServiceProxy, 'webhook-proof-presentation', payload); } + + /** + * Out-Of-Band Proof Presentation + * @param user + * @param outOfBandRequestProof + * @returns Get out-of-band requested proof presentation details + */ + sendOutOfBandPresentationRequest(outOfBandRequestProof: OutOfBandRequestProof, user: IUserRequest): Promise<{ response: object }> { + const payload = { outOfBandRequestProof, user }; + return this.sendNats(this.verificationServiceProxy, 'send-out-of-band-proof-request', payload); + } } diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 3771ad415..45b7dad1b 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -10,11 +10,12 @@ interface IProofRequestAttribute { export interface IRequestProof { orgId: number; - connectionId: string; + connectionId?: string; attributes: IProofRequestAttribute[]; comment: string; autoAcceptProof: string; protocolVersion: string; + emailId?: string } export interface IGetAllProofPresentations { @@ -68,7 +69,7 @@ interface IRequestedRestriction { export interface ISendProofRequestPayload { protocolVersion: string; comment: string; - connectionId: string; + connectionId?: string; proofFormats: IProofFormats; autoAcceptProof: string; } diff --git a/apps/verification/src/repositories/verification.repository.ts b/apps/verification/src/repositories/verification.repository.ts index 4780f8ba6..e911f7cf0 100644 --- a/apps/verification/src/repositories/verification.repository.ts +++ b/apps/verification/src/repositories/verification.repository.ts @@ -1,8 +1,8 @@ import { ResponseMessages } from "@credebl/common/response-messages"; import { PrismaService } from "@credebl/prisma-service"; -import { Injectable, Logger, NotFoundException } from "@nestjs/common"; +import { Injectable, InternalServerErrorException, Logger, NotFoundException } from "@nestjs/common"; // eslint-disable-next-line camelcase -import { org_agents, presentations } from "@prisma/client"; +import { org_agents, organisation, platform_config, presentations } from "@prisma/client"; import { IWebhookProofPresentation } from "../interfaces/verification.interface"; @@ -63,4 +63,35 @@ export class VerificationRepository { throw error; } } + + /** + * Get platform config details + * @returns + */ + // eslint-disable-next-line camelcase + async getPlatformConfigDetails(): Promise { + try { + + return this.prisma.platform_config.findFirst(); + + } catch (error) { + this.logger.error(`[getPlatformConfigDetails] - error: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } + + /** + * Get organization details + * @returns + */ + async getOrganization(orgId: number): Promise { + try { + + return this.prisma.organisation.findFirst({ where: { id: orgId } }); + + } catch (error) { + this.logger.error(`[getOrganization] - error: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } } \ No newline at end of file diff --git a/apps/verification/src/verification.controller.ts b/apps/verification/src/verification.controller.ts index 565d91fc3..65dd1d852 100644 --- a/apps/verification/src/verification.controller.ts +++ b/apps/verification/src/verification.controller.ts @@ -53,4 +53,9 @@ export class VerificationController { async webhookProofPresentation(payload: { id: string, proofPresentationPayload: IWebhookProofPresentation }): Promise { return this.verificationService.webhookProofPresentation(payload.id, payload.proofPresentationPayload); } + + @MessagePattern({ cmd: 'send-out-of-band-proof-request' }) + async sendOutOfBandPresentationRequest(payload: { outOfBandRequestProof: IRequestProof, user: IUserRequest }): Promise { + return this.verificationService.sendOutOfBandPresentationRequest(payload.outOfBandRequestProof); + } } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 7984d84dc..024d1e0df 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -import { BadRequestException, HttpException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { BadRequestException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs/operators'; import { IGetAllProofPresentations, IGetProofPresentationById, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IWebhookProofPresentation } from './interfaces/verification.interface'; @@ -8,6 +8,11 @@ import { CommonConstants } from '@credebl/common/common.constant'; import { presentations } from '@prisma/client'; import { OrgAgentType } from '@credebl/enum/enum'; import { ResponseMessages } from '@credebl/common/response-messages'; +import * as QRCode from 'qrcode'; +import { OutOfBandVerification } from '../templates/out-of-band-verification.template'; +import { EmailDto } from '@credebl/common/dtos/email.dto'; +import { sendEmail } from '@credebl/common/send-grid-helper-file'; +import * as uuid from 'uuid'; @Injectable() export class VerificationService { @@ -152,8 +157,6 @@ export class VerificationService { */ async sendProofRequest(requestProof: IRequestProof): Promise { try { - let requestedAttributes = {}; - const requestedPredicates = {}; const comment = requestProof.comment ? requestProof.comment : ''; let proofRequestPayload: ISendProofRequestPayload = { @@ -171,73 +174,7 @@ export class VerificationService { autoAcceptProof: '' }; - const attributeWithSchemaIdExists = requestProof.attributes.some(attribute => attribute.schemaId); - if (attributeWithSchemaIdExists) { - requestedAttributes = Object.fromEntries(requestProof.attributes.map((attribute, index) => { - - const attributeElement = attribute.attributeName; - const attributeReferent = `additionalProp${index + 1}`; - - if (!attribute.condition && !attribute.value) { - const keys = Object.keys(requestedAttributes); - - if (0 < keys.length) { - let attributeFound = false; - - for (const attr of keys) { - if ( - requestedAttributes[attr].restrictions.some(res => res.schema_id) === - requestProof.attributes[index].schemaId - ) { - requestedAttributes[attr].name.push(attributeElement); - attributeFound = true; - } - - if (attr === keys[keys.length - 1] && !attributeFound) { - requestedAttributes[attributeReferent] = { - name: attributeElement, - restrictions: [ - { - cred_def_id: requestProof.attributes[index].credDefId ? requestProof.attributes[index].credDefId : undefined, - schema_id: requestProof.attributes[index].schemaId - } - ] - }; - } - } - } else { - return [ - attributeReferent, - { - name: attributeElement, - restrictions: [ - { - cred_def_id: requestProof.attributes[index].credDefId ? requestProof.attributes[index].credDefId : undefined, - schema_id: requestProof.attributes[index].schemaId - } - ] - } - ]; - } - } else { - requestedPredicates[attributeReferent] = { - p_type: attribute.condition, - restrictions: [ - { - cred_def_id: requestProof.attributes[index].credDefId ? requestProof.attributes[index].credDefId : undefined, - schema_id: requestProof.attributes[index].schemaId - } - ], - name: attributeElement, - p_value: parseInt(attribute.value) - }; - } - - return [attributeReferent, null]; - })); - } else { - throw new BadRequestException(ResponseMessages.verification.error.schemaIdNotFound); - } + const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(requestProof); proofRequestPayload = { protocolVersion: requestProof.protocolVersion ? requestProof.protocolVersion : 'v1', @@ -302,7 +239,7 @@ export class VerificationService { }, error.error); }); } catch (error) { - this.logger.error(`[_verifyPresentation] - error in verify presentation : ${JSON.stringify(error)}`); + this.logger.error(`[_sendProofRequest] - error in verify presentation : ${JSON.stringify(error)}`); throw error; } } @@ -377,6 +314,248 @@ export class VerificationService { } } + /** + * Request out-of-band proof presentation + * @param outOfBandRequestProof + * @returns Get requested proof presentation details + */ + async sendOutOfBandPresentationRequest(outOfBandRequestProof: IRequestProof): Promise { + try { + const comment = outOfBandRequestProof.comment ? outOfBandRequestProof.comment : ''; + + let proofRequestPayload: ISendProofRequestPayload = { + protocolVersion: '', + comment: '', + proofFormats: { + indy: { + name: '', + requested_attributes: {}, + requested_predicates: {}, + version: '' + } + }, + autoAcceptProof: '' + }; + + const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); + + proofRequestPayload = { + protocolVersion: outOfBandRequestProof.protocolVersion ? outOfBandRequestProof.protocolVersion : 'v1', + comment, + proofFormats: { + indy: { + name: 'Proof Request', + version: '1.0', + // eslint-disable-next-line camelcase + requested_attributes: requestedAttributes, + // eslint-disable-next-line camelcase + requested_predicates: requestedPredicates + } + }, + autoAcceptProof: outOfBandRequestProof.autoAcceptProof ? outOfBandRequestProof.autoAcceptProof : 'never' + }; + + const getAgentDetails = await this.verificationRepository.getAgentEndPoint(outOfBandRequestProof.orgId); + const organizationDetails = await this.verificationRepository.getOrganization(outOfBandRequestProof.orgId); + + const verificationMethodLabel = 'create-request-out-of-band'; + const url = await this.getAgentUrl(verificationMethodLabel, getAgentDetails?.orgAgentTypeId, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); + + const payload = { apiKey: '', url, proofRequestPayload }; + + const getProofPresentation = await this._sendOutOfBandProofRequest(payload); + + if (!getProofPresentation) { + throw new NotFoundException(ResponseMessages.verification.error.proofPresentationNotFound); + } + + const invitationId = getProofPresentation?.response?.invitation['@id']; + + if (!invitationId) { + throw new NotFoundException(ResponseMessages.verification.error.invitationNotFound); + } + + let shortenedUrl; + if (getAgentDetails?.tenantId) { + shortenedUrl = `${getAgentDetails?.agentEndPoint}/multi-tenancy/url/${getAgentDetails?.tenantId}/${invitationId}`; + } else { + shortenedUrl = `${getAgentDetails?.agentEndPoint}/url/${invitationId}`; + } + + const uniqueCID = uuid.v4(); + + const qrCodeOptions: QRCode.QRCodeToDataURLOptions = { + type: 'image/png' + }; + + const outOfBandIssuanceQrCode = await QRCode.toDataURL(shortenedUrl, qrCodeOptions); + const platformConfigData = await this.verificationRepository.getPlatformConfigDetails(); + + if (!platformConfigData) { + throw new NotFoundException(ResponseMessages.verification.error.platformConfigNotFound); + } + + const outOfBandVerification = new OutOfBandVerification(); + const emailData = new EmailDto(); + emailData.emailFrom = platformConfigData.emailFrom; + emailData.emailTo = outOfBandRequestProof.emailId; + emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Verification of Your Credentials Required`; + emailData.emailHtml = await outOfBandVerification.outOfBandVerification(outOfBandRequestProof.emailId, uniqueCID, organizationDetails.name); + emailData.emailAttachments = [ + { + filename: 'qrcode.png', + content: outOfBandIssuanceQrCode.split(';base64,')[1], + contentType: 'image/png', + disposition: 'attachment' + } + ]; + const isEmailSent = await sendEmail(emailData); + + if (isEmailSent) { + return isEmailSent; + } else { + throw new InternalServerErrorException(ResponseMessages.verification.error.emailSend); + } + + } catch (error) { + this.logger.error(`[sendOutOfBandPresentationRequest] - error in out of band proof request : ${JSON.stringify(error)}`); + throw new RpcException(error); + } + } + + /** + * Consume agent API for request out-of-band proof presentation + * @param payload + * @returns Get requested proof presentation details + */ + async _sendOutOfBandProofRequest(payload: IProofRequestPayload): Promise<{ + response; + }> { + try { + + const pattern = { + cmd: 'agent-send-out-of-band-proof-request' + }; + + return this.verificationServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ).toPromise() + .catch(error => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.statusCode, + error: error.message + }, error.error); + }); + } catch (error) { + this.logger.error(`[_sendOutOfBandProofRequest] - error in Out Of Band Presentation : ${JSON.stringify(error)}`); + throw error; + } + } + + async _proofRequestPayload(proofRequestpayload: IRequestProof): Promise<{ + requestedAttributes; + requestedPredicates; + }> { + try { + let requestedAttributes = {}; + const requestedPredicates = {}; + + const attributeWithSchemaIdExists = proofRequestpayload.attributes.some(attribute => attribute.schemaId); + if (attributeWithSchemaIdExists) { + + requestedAttributes = {}; + for (const [index, attribute] of proofRequestpayload.attributes.entries()) { + const attributeElement = attribute.attributeName; + const attributeReferent = `additionalProp${index + 1}`; + + if (!attribute.condition && !attribute.value) { + const keys = Object.keys(requestedAttributes); + + if (0 < keys.length) { + let attributeFound = false; + + for (const attr of keys) { + if ( + requestedAttributes[attr].restrictions.some( + res => res.schema_id === proofRequestpayload.attributes[index].schemaId + ) + ) { + requestedAttributes[attr].name.push(attributeElement); + attributeFound = true; + } + + if (attr === keys[keys.length - 1] && !attributeFound) { + requestedAttributes[attributeReferent] = { + name: attributeElement, + restrictions: [ + { + cred_def_id: proofRequestpayload.attributes[index].credDefId + ? proofRequestpayload.attributes[index].credDefId + : undefined, + schema_id: proofRequestpayload.attributes[index].schemaId + } + ] + }; + } + } + } else { + requestedAttributes[attributeReferent] = { + name: attributeElement, + restrictions: [ + { + cred_def_id: proofRequestpayload.attributes[index].credDefId + ? proofRequestpayload.attributes[index].credDefId + : undefined, + schema_id: proofRequestpayload.attributes[index].schemaId + } + ] + }; + } + } else { + if (isNaN(parseInt(attribute.value))) { + throw new BadRequestException( + ResponseMessages.verification.error.predicatesValueNotNumber + ); + } + + requestedPredicates[attributeReferent] = { + p_type: attribute.condition, + restrictions: [ + { + cred_def_id: proofRequestpayload.attributes[index].credDefId + ? proofRequestpayload.attributes[index].credDefId + : undefined, + schema_id: proofRequestpayload.attributes[index].schemaId + } + ], + name: attributeElement, + p_value: parseInt(attribute.value) + }; + } + } + + return { + requestedAttributes, + requestedPredicates + }; + } else { + throw new BadRequestException( + ResponseMessages.verification.error.schemaIdNotFound + ); + } + } catch (error) { + this.logger.error(`[proofRequestPayload] - error in proof request payload : ${JSON.stringify(error)}`); + throw new RpcException(error); + } + } + /** * Description: Fetch agent url * @param referenceId @@ -434,6 +613,15 @@ export class VerificationService { break; } + case 'create-request-out-of-band': { + url = orgAgentTypeId === OrgAgentType.DEDICATED + ? `${agentEndPoint}${CommonConstants.URL_SEND_OUT_OF_BAND_CREATE_REQUEST}` + : orgAgentTypeId === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_OUT_OF_BAND_CREATE_REQUEST}`.replace('#', tenantId) + : null; + break; + } + default: { break; } diff --git a/apps/verification/templates/out-of-band-verification.template.ts b/apps/verification/templates/out-of-band-verification.template.ts new file mode 100644 index 000000000..992cacf08 --- /dev/null +++ b/apps/verification/templates/out-of-band-verification.template.ts @@ -0,0 +1,61 @@ +export class OutOfBandVerification { + + public outOfBandVerification(email: string, uniqueCID: string, orgName: string): string { + try { + return ` + + + + + + + + + +
+
+ Credebl Logo +
+
+ verification Image +
+
+

+ Hello ${email} , +

+

+ The organization ${orgName} has requested your assistance in verifying your credentials. + To proceed, kindly follow the steps outlined below: +

    +
  • Download the ADHAYA Wallet application from the Play Store.
  • +
  • Create a new account within the app.
  • +
  • Scan the QR code provided below within the app.
  • +
  • Accept the request for the Credential Document.
  • +
  • Access the issued Credential Document within your wallet.
  • +
  • Create a new account within the app.
  • +
+ Should you encounter any difficulties or have inquiries, our dedicated support team is available to assist you. Feel free to reach out. +

+
+
+
+ + f + t +
+

+ Best Regards,The CREDEBL Team +

+
+
+
+ + `; + + } catch (error) { + } + } +} \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index db4398083..20a626de5 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -94,12 +94,14 @@ export enum CommonConstants { URL_SHAGENT_GET_PROOFS_BY_PRESENTATION_ID = '/multi-tenancy/proofs/#/@', URL_SHAGENT_REQUEST_PROOF = '/multi-tenancy/proofs/request-proof/#', URL_SHAGENT_ACCEPT_PRESENTATION = '/multi-tenancy/proofs/@/accept-presentation/#', + URL_SHAGENT_OUT_OF_BAND_CREATE_REQUEST = '/multi-tenancy/proofs/create-request-oob/#', // PROOF SERVICES URL_SEND_PROOF_REQUEST = '/proofs/request-proof', URL_GET_PROOF_PRESENTATIONS = '/proofs', URL_GET_PROOF_PRESENTATION_BY_ID = '/proofs/#', URL_VERIFY_PRESENTATION = '/proofs/#/accept-presentation', + URL_SEND_OUT_OF_BAND_CREATE_REQUEST='/proofs/create-request-oob', // server or agent URL_SERVER_STATUS = '/status', diff --git a/libs/common/src/dtos/email.dto.ts b/libs/common/src/dtos/email.dto.ts index a810bed10..e395da884 100644 --- a/libs/common/src/dtos/email.dto.ts +++ b/libs/common/src/dtos/email.dto.ts @@ -4,4 +4,14 @@ export class EmailDto { emailSubject: string; emailText: string; emailHtml: string; + emailAttachments?: AttachmentJSON[]; } + +interface AttachmentJSON { + content: string; + filename: string; + contentType: string; + type?: string; + disposition?: string; + content_id?: string; + } \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 7cd59c48f..71f012b18 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -161,7 +161,12 @@ export const ResponseMessages = { error: { notFound: 'Organization agent not found', agentUrlNotFound: 'agent url not found', - schemaIdNotFound: 'Schema Id is required' + schemaIdNotFound: 'Schema Id is required', + predicatesValueNotNumber: 'The attribuite value is not a number', + proofPresentationNotFound: 'Proof presentation not found', + invitationNotFound: 'Invitation not found', + platformConfigNotFound: 'Platform config not found', + emailSend: 'Unable to send email to the user' } } }; diff --git a/libs/common/src/send-grid-helper-file.ts b/libs/common/src/send-grid-helper-file.ts index d234c6ba3..a98bd518b 100644 --- a/libs/common/src/send-grid-helper-file.ts +++ b/libs/common/src/send-grid-helper-file.ts @@ -15,10 +15,11 @@ export const sendEmail = async (EmailDto: EmailDto): Promise => { from: EmailDto.emailFrom, subject: EmailDto.emailSubject, text: EmailDto.emailText, - html: EmailDto.emailHtml + html: EmailDto.emailHtml, + attachments: EmailDto.emailAttachments }; + return await sendgrid.send(msg).then(() => true).catch(() => false) - return await sendgrid.send(msg).then(() => true).catch(() => false); } catch (error) { return false; } diff --git a/package.json b/package.json index aa863013e..05b93ed2b 100755 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "path": "^0.12.7", "pdfkit": "^0.13.0", "pg": "^8.11.2", - "qrcode": "^1.5.1", + "qrcode": "^1.5.3", "qs": "^6.11.2", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2",