Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions Dockerfiles/Dockerfile.oid4vc-issuance
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ RUN npm run build oid4vc-issuance
FROM node:18-alpine
# Install OpenSSL
RUN apk add --no-cache openssl

# Set the working directory
WORKDIR /app

Expand All @@ -40,4 +39,4 @@ COPY --from=build /app/libs/ ./libs/
COPY --from=build /app/node_modules ./node_modules

# Set the command to run the microservice
CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/oid4vc-issuance/main.js"]
CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/oid4vc-issuance/main.js"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsDefined, IsEnum, IsOptional, IsString, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { SignerOption } from '@prisma/client';
import { ResponseMode } from '@credebl/enum/enum';

class RequestSignerDto {
@ApiProperty({ enum: SignerOption, example: SignerOption.DID })
@IsDefined()
@IsEnum(SignerOption)
method: SignerOption;
}

export class CreateIntentBasedVerificationDto {
@ApiProperty({ description: 'Intent name to lookup template for', example: 'kyc-intent' })
@IsDefined()
@IsString()
intent: string;

@ApiProperty({ enum: ResponseMode, example: ResponseMode.DIRECT_POST_JWT })
@IsDefined()
@IsEnum(ResponseMode)
responseMode: ResponseMode;

@ApiPropertyOptional({ type: RequestSignerDto, description: 'Optional request signer option' })
@IsOptional()
@ValidateNested()
@Type(() => RequestSignerDto)
requestSigner?: RequestSignerDto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import {
Matches
} from 'class-validator';
import { Type } from 'class-transformer';
import { SignerOption } from '@prisma/client';
import { ResponseMode } from '@credebl/enum/enum';
import { SignerOption } from '@prisma/client';

/**
* DTO for verification-presentation query parameters.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { IsEnum, IsNotEmpty, IsObject, IsString } from 'class-validator';
import { ApiProperty, PartialType } from '@nestjs/swagger';
import { SignerOption } from '@prisma/client';

export class CreateVerificationTemplateDto {
@ApiProperty({
description: 'Name of the verification template',
example: 'KYC Verification Template'
})
@IsString()
@IsNotEmpty()
name: string;

@ApiProperty({
description: 'JSON configuration for the verification template',
example: {
dcql: {
query: {
combine: 'all',
credentials: [
{
id: 'example-dc_sd_jwt',
format: 'dc+sd-jwt',
meta: {
vct: 'urn:example:vc+sd-jwt'
},
require_cryptographic_holder_binding: true,
claims: [
{
path: ['full_name'],
intent_to_retain: true
}
]
}
]
}
}
}
})
@IsObject()
@IsNotEmpty()
templateJson: object;

@ApiProperty({ enum: SignerOption, description: 'Signer option type' })
@IsEnum(SignerOption)
signerOption!: SignerOption;
}

export class UpdateVerificationTemplateDto extends PartialType(CreateVerificationTemplateDto) {}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import { Oid4vcVerificationService } from './oid4vc-verification.service';
import { CreateVerifierDto, UpdateVerifierDto } from './dtos/oid4vc-verifier.dto';
import { PresentationRequestDto, VerificationPresentationQueryDto } from './dtos/oid4vc-verifier-presentation.dto';
import { Oid4vpPresentationWhDto } from '../oid4vc-issuance/dtos/oid4vp-presentation-wh.dto';
import { CreateVerificationTemplateDto, UpdateVerificationTemplateDto } from './dtos/verification-template.dto';
import { CreateIntentBasedVerificationDto } from './dtos/create-intent-based-verification.dto';

@Controller()
@UseFilters(CustomExceptionFilter)
Expand Down Expand Up @@ -305,6 +307,63 @@ export class Oid4vcVerificationController {
return res.status(HttpStatus.CREATED).json(finalResponse);
}

@Post('/orgs/:orgId/oid4vp/intent-based-verification-presentation')
@ApiOperation({
summary: 'Create intent-based verification presentation',
description: 'Creates a new verification presentation using an intent template for the specified organization.'
})
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Verification presentation created successfully.',
type: ApiResponseDto
})
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async createIntentBasedVerificationPresentation(
@Param(
'orgId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
}
})
)
orgId: string,
@Query(
'verifierId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException('Invalid verifier ID');
}
})
)
verifierId: string,
@User() user: user,
@Body() createIntentDto: CreateIntentBasedVerificationDto,
@Res() res: Response
): Promise<Response> {
this.logger.debug(
`[createIntentBasedVerificationPresentation] Called with orgId=${orgId}, verifierId=${verifierId}, intent=${createIntentDto?.intent}, user=${user.id}`
);

const presentation = await this.oid4vcVerificationService.createIntentBasedVerificationPresentation(
orgId,
verifierId,
createIntentDto,
user
);

this.logger.debug(`[createIntentBasedVerificationPresentation] Presentation created successfully`);

const finalResponse: IResponse = {
statusCode: HttpStatus.CREATED,
message: ResponseMessages.oid4vpSession.success.create,
data: presentation
};
return res.status(HttpStatus.CREATED).json(finalResponse);
}

@Get('/orgs/:orgId/oid4vp/verifier-presentation')
@ApiOperation({
summary: 'Get OID4VP verifier presentation details',
Expand Down Expand Up @@ -448,6 +507,7 @@ export class Oid4vcVerificationController {
});

if (webhookUrl) {
this.logger.log(`posting webhook response to webhook url`);
await this.oid4vcVerificationService
._postWebhookResponse(webhookUrl, { data: oid4vpPresentationWhDto })
.catch((error) => {
Expand All @@ -457,4 +517,205 @@ export class Oid4vcVerificationController {

return res.status(HttpStatus.CREATED).json(finalResponse);
}

@Post('/orgs/:orgId/oid4vp/verification-template')
@ApiOperation({
summary: 'Create verification template',
description: 'Creates a new verification template for the specified organization.'
})
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Verification template created successfully.',
type: ApiResponseDto
})
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async createVerificationTemplate(
@Param(
'orgId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
}
})
)
orgId: string,
@User() user: user,
@Body() createTemplateDto: CreateVerificationTemplateDto,
@Res() res: Response
): Promise<Response> {
this.logger.debug(`[createVerificationTemplate] Called with orgId=${orgId}, user=${user.id}`);

const template = await this.oid4vcVerificationService.createVerificationTemplate(createTemplateDto, orgId, user);

this.logger.debug(`[createVerificationTemplate] Template created: ${template['id']}`);

const finalResponse: IResponse = {
statusCode: HttpStatus.CREATED,
message: 'Verification template created successfully',
data: template
};
return res.status(HttpStatus.CREATED).json(finalResponse);
}

@Get('/orgs/:orgId/oid4vp/verification-template')
@ApiOperation({
summary: 'Get verification template(s)',
description: 'Retrieves verification template(s) for the specified organization.'
})
@ApiQuery({
name: 'templateId',
required: false,
type: String,
description: 'UUID of the template (optional)'
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Verification template(s) retrieved successfully.',
type: ApiResponseDto
})
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async getVerificationTemplates(
@Param(
'orgId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
}
})
)
orgId: string,
@Res() res: Response,
@Query(
'templateId',
new ParseUUIDPipe({
version: '4',
optional: true,
exceptionFactory: (): Error => {
throw new BadRequestException('Invalid template ID');
}
})
)
templateId?: string
): Promise<Response> {
this.logger.debug(`[getVerificationTemplates] Called with orgId=${orgId}, templateId=${templateId ?? 'all'}`);

const templates = await this.oid4vcVerificationService.getVerificationTemplates(orgId, templateId);

this.logger.debug(`[getVerificationTemplates] Templates fetched successfully`);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: 'Verification template(s) retrieved successfully',
data: templates
};
return res.status(HttpStatus.OK).json(finalResponse);
}

@Put('/orgs/:orgId/oid4vp/verification-template/:templateId')
@ApiOperation({
summary: 'Update verification template',
description: 'Updates an existing verification template for the specified organization.'
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Verification template updated successfully.',
type: ApiResponseDto
})
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async updateVerificationTemplate(
@Param(
'orgId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
}
})
)
orgId: string,
@Param(
'templateId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException('Invalid template ID');
}
})
)
templateId: string,
@User() user: user,
@Body() updateTemplateDto: UpdateVerificationTemplateDto,
@Res() res: Response
): Promise<Response> {
this.logger.debug(
`[updateVerificationTemplate] Called with orgId=${orgId}, templateId=${templateId}, user=${user.id}`
);

const template = await this.oid4vcVerificationService.updateVerificationTemplate(
templateId,
updateTemplateDto,
orgId,
user
);

this.logger.debug(`[updateVerificationTemplate] Template updated: ${template['id']}`);

const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: 'Verification template updated successfully',
data: template
};
return res.status(HttpStatus.OK).json(finalResponse);
}

@Delete('/orgs/:orgId/oid4vp/verification-template/:templateId')
@ApiOperation({
summary: 'Delete verification template',
description: 'Deletes a verification template for the specified organization.'
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Verification template deleted successfully.',
type: ApiResponseDto
})
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async deleteVerificationTemplate(
@Param(
'orgId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
}
})
)
orgId: string,
@Param(
'templateId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException('Invalid template ID');
}
})
)
templateId: string,
@Res() res: Response
): Promise<Response> {
this.logger.debug(`[deleteVerificationTemplate] Called with orgId=${orgId}, templateId=${templateId}`);

const template = await this.oid4vcVerificationService.deleteVerificationTemplate(orgId, templateId);

this.logger.debug(`[deleteVerificationTemplate] Template deleted: ${templateId}`);

const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: 'Verification template deleted successfully',
data: template
};
return res.status(HttpStatus.OK).json(finalResponse);
}
}
Loading