Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
f268d48
feat(oid4vc): add support for jwt_vc_json-ld credential format
sagarkhole4 May 20, 2026
4011757
feat(oid4vc): add support for jwt_vc_json-ld credential format
sagarkhole4 May 20, 2026
03152e4
refactor(oid4vc): fix DTO formatting and nbf/exp verification
sagarkhole4 May 21, 2026
e931936
feat(oid4vc): fix jwt_vc_json-ld support and add holder service
sagarkhole4 Jun 5, 2026
05b4fbb
feat: replace context array with schemaUrl in SdJwtTemplate
sagarkhole4 Jun 15, 2026
70f9446
refactor(oid4vc): remove oid4vc-holder microservice and gateway integ…
sagarkhole4 Jun 23, 2026
4afb9e1
refactor(oid4vc): resolve security warnings, harden schema validation…
sagarkhole4 Jun 23, 2026
40c0412
feat(oid4vc): add support for jwt_vc_json-ld credential format
sagarkhole4 May 20, 2026
26d7b5c
feat(oid4vc): add support for jwt_vc_json-ld credential format
sagarkhole4 May 20, 2026
0d1652e
refactor(oid4vc): fix DTO formatting and nbf/exp verification
sagarkhole4 May 21, 2026
6211dd0
feat(oid4vc): fix jwt_vc_json-ld support and add holder service
sagarkhole4 Jun 5, 2026
2e05268
feat: replace context array with schemaUrl in SdJwtTemplate
sagarkhole4 Jun 15, 2026
ae7e8ea
refactor(oid4vc): remove oid4vc-holder microservice and gateway integ…
sagarkhole4 Jun 23, 2026
3a70c87
refactor(oid4vc): resolve security warnings, harden schema validation…
sagarkhole4 Jun 23, 2026
f73b179
feat(oid4vc):ldp_vc support for jsonLd
sagarkhole4 Jun 25, 2026
f584dc1
Merge branch 'feat/jwt_vc_jsonld' of https://github.com/credebl/platf…
sagarkhole4 Jun 25, 2026
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: 3 additions & 0 deletions apps/api-gateway/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import { IssuanceModule } from './issuance/issuance.module';
import { LoggerModule } from '@credebl/logger/logger.module';
import { NotificationModule } from './notification/notification.module';
import { Oid4vcIssuanceModule } from './oid4vc-issuance/oid4vc-issuance.module';
import { Oid4vcHolderModule } from './oid4vc-holder/oid4vc-holder.module';
import { Oid4vpModule } from './oid4vc-verification/oid4vc-verification.module';

import { OrganizationModule } from './organization/organization.module';
import { ConfigModule as PlatformConfig } from '@credebl/config/config.module';
import { PlatformModule } from './platform/platform.module';
Expand Down Expand Up @@ -77,6 +79,7 @@ import { shouldLoadOidcModules } from '@credebl/common/common.utils';
GeoLocationModule,
CloudWalletModule,
ConditionalModule.registerWhen(Oid4vcIssuanceModule, shouldLoadOidcModules),
ConditionalModule.registerWhen(Oid4vcHolderModule, shouldLoadOidcModules),
ConditionalModule.registerWhen(Oid4vpModule, shouldLoadOidcModules),
ConditionalModule.registerWhen(X509Module, shouldLoadOidcModules),
KeycloakConfigModule
Expand Down
40 changes: 40 additions & 0 deletions apps/api-gateway/src/oid4vc-holder/dtos/oid4vc-holder.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="apps/api-gateway/src/oid4vc-holder/dtos/oid4vc-holder.dto.ts"

# Show the relevant area around the mentioned line(s)
nl -ba "$FILE" | sed -n '1,260p'

Repository: credebl/platform

Length of output: 103


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="apps/api-gateway/src/oid4vc-holder/dtos/oid4vc-holder.dto.ts"

# Sanity: file exists
test -f "$FILE"

# Print with line numbers (no nl available)
awk '{printf "%6d\t%s\n", NR, $0}' "$FILE" | sed -n '1,260p'

Repository: credebl/platform

Length of output: 1409


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --no-ignore-vcs "credentialsToRequest" .

Repository: credebl/platform

Length of output: 259


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the interface for credentialsToRequest
FILE_IF="apps/oid4vc-holder/src/interfaces/oid4vc-holder.interface.ts"
test -f "$FILE_IF"
awk '{printf "%6d\t%s\n", NR, $0}' "$FILE_IF" | sed -n '1,200p'

# Find where OidcRequestCredentialDto is used
rg -n --hidden --no-ignore-vcs "OidcRequestCredentialDto" .

Repository: credebl/platform

Length of output: 1145


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# controller
FILE_C="apps/api-gateway/src/oid4vc-holder/oid4vc-holder.controller.ts"
test -f "$FILE_C"
awk '{printf "%6d\t%s\n", NR, $0}' "$FILE_C" | sed -n '1,220p'

# service
FILE_S="apps/api-gateway/src/oid4vc-holder/oid4vc-holder.service.ts"
test -f "$FILE_S"
awk '{printf "%6d\t%s\n", NR, $0}' "$FILE_S" | sed -n '1,220p'

Repository: credebl/platform

Length of output: 8010


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --no-ignore-vcs "oid4vc-holder-request-credential" .

Repository: credebl/platform

Length of output: 351


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="apps/oid4vc-holder/src/oid4vc-holder.controller.ts"
test -f "$FILE"
awk '{printf "%6d\t%s\n", NR, $0}' "$FILE" | sed -n '1,260p'

Repository: credebl/platform

Length of output: 2143


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --no-ignore-vcs "holderPayload\.credentialsToRequest|credentialsToRequest" apps/oid4vc-holder

Repository: credebl/platform

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --no-ignore-vcs "ValidationPipe" apps/api-gateway .

Repository: credebl/platform

Length of output: 12291


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# main.ts around global pipes
FILE_MAIN="apps/api-gateway/src/main.ts"
test -f "$FILE_MAIN"
awk '{printf "%6d\t%s\n", NR, $0}' "$FILE_MAIN" | sed -n '100,170p'

# custom validation pipe implementation
FILE_PIPE="libs/common/src/custom-overrideable-validation-pipe.ts"
test -f "$FILE_PIPE"
awk '{printf "%6d\t%s\n", NR, $0}' "$FILE_PIPE" | sed -n '1,220p'

Repository: credebl/platform

Length of output: 5729


credentialsToRequest validation is too weak for the request contract.

@IsNotEmpty() only guarantees the array itself isn’t empty; it doesn’t validate that each entry is a non-empty string, so invalid/empty members can reach the holder flow.

Suggested fix
-import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator';
+import { ArrayNotEmpty, IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator';
@@
   `@ApiProperty`({ example: ['UniversityDegree'] })
   `@IsArray`()
-  `@IsNotEmpty`()
+  `@ArrayNotEmpty`()
+  `@IsString`({ each: true })
+  `@IsNotEmpty`({ each: true })
   credentialsToRequest: string[];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api-gateway/src/oid4vc-holder/dtos/oid4vc-holder.dto.ts` at line 2, The
credentialsToRequest array in the DTO is only using `@IsNotEmpty` on the array
itself, allowing empty/invalid entries; update the validation for the
credentialsToRequest property (in the DTO class containing credentialsToRequest,
e.g., Oid4vcHolderDto) to require a non-empty array and validate each member as
a non-empty string by adding decorators such as ArrayNotEmpty and per-item
validators (IsString({ each: true }) and IsNotEmpty({ each: true })) so every
element is a non-empty string.


export class OidcResolveCredentialOfferDto {
@ApiProperty({ example: 'openid-credential-offer://?credential_offer_uri=...' })
@IsString()
@IsNotEmpty()
credentialOfferUri: string;
}

export class OidcRequestCredentialDto {
@ApiProperty({ example: 'openid-credential-offer://?credential_offer_uri=...' })
@IsString()
@IsNotEmpty()
credentialOfferUri: string;

@ApiProperty({ example: ['UniversityDegree'] })
@IsArray()
@IsNotEmpty()
credentialsToRequest: string[];

@ApiPropertyOptional({ example: '1234' })
@IsString()
@IsOptional()
txCode?: string;
}

export class OidcResolveProofRequestDto {
@ApiProperty({ example: 'openid-vc-request://?request_uri=...' })
@IsString()
@IsNotEmpty()
proofRequestUri: string;
}

export class OidcAcceptProofRequestDto {
@ApiProperty({ example: 'openid-vc-request://?request_uri=...' })
@IsString()
@IsNotEmpty()
proofRequestUri: string;
}
128 changes: 128 additions & 0 deletions apps/api-gateway/src/oid4vc-holder/oid4vc-holder.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Controller, Post, Body, UseGuards, HttpStatus, Res, Param, UseFilters, Logger } from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiForbiddenResponse,
ApiUnauthorizedResponse
} from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';
import { ApiResponseDto } from '../dtos/apiResponse.dto';
import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto';
import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto';
import { Response } from 'express';
import { IResponse } from '@credebl/common/interfaces/response.interface';
import { Roles } from '../authz/decorators/roles.decorator';
import { OrgRoles } from 'libs/org-roles/enums';
import { OrgRolesGuard } from '../authz/guards/org-roles.guard';
import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler';
import { Oid4vcHolderService } from './oid4vc-holder.service';
import {
OidcAcceptProofRequestDto,
OidcRequestCredentialDto,
OidcResolveCredentialOfferDto,
OidcResolveProofRequestDto
} from './dtos/oid4vc-holder.dto';

@Controller('orgs/:orgId/oid4vc/holder')
@UseFilters(CustomExceptionFilter)
@ApiTags('OID4VC-Holder')
@ApiUnauthorizedResponse({ description: 'Unauthorized', type: UnauthorizedErrorDto })
@ApiForbiddenResponse({ description: 'Forbidden', type: ForbiddenErrorDto })
export class Oid4vcHolderController {
private readonly logger = new Logger('Oid4vcHolderController');
constructor(private readonly oid4vcHolderService: Oid4vcHolderService) {}

@Post('resolve-credential-offer')
@ApiOperation({
summary: 'Resolve OID4VC Credential Offer',
description: 'Resolves an OID4VC credential offer for the specified organization.'
})
@ApiResponse({ status: HttpStatus.OK, description: 'Credential offer resolved successfully.', type: ApiResponseDto })
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async oidcHolderResolveCredentialOffer(
@Param('orgId') orgId: string,
@Body() resolveDto: OidcResolveCredentialOfferDto,
@Res() res: Response
): Promise<Response> {
const data = await this.oid4vcHolderService.oidcHolderResolveCredentialOffer(orgId, resolveDto);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: 'Credential offer resolved successfully.',
data
};
return res.status(HttpStatus.OK).json(finalResponse);
}

@Post('request-credential')
@ApiOperation({
summary: 'Request OID4VC Credential',
description: 'Requests and stores an OID4VC credential for the specified organization.'
})
@ApiResponse({ status: HttpStatus.OK, description: 'Credential requested successfully.', type: ApiResponseDto })
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async oidcHolderRequestCredential(
@Param('orgId') orgId: string,
@Body() requestDto: OidcRequestCredentialDto,
@Res() res: Response
): Promise<Response> {
const data = await this.oid4vcHolderService.oidcHolderRequestCredential(orgId, requestDto);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: 'Credential requested successfully.',
data
};
return res.status(HttpStatus.OK).json(finalResponse);
}

@Post('resolve-proof-request')
@ApiOperation({
summary: 'Resolve OID4VC Proof Request',
description: 'Resolves an OID4VC proof request for the specified organization.'
})
@ApiResponse({ status: HttpStatus.OK, description: 'Proof request resolved successfully.', type: ApiResponseDto })
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async oidcHolderResolveProofRequest(
@Param('orgId') orgId: string,
@Body() resolveDto: OidcResolveProofRequestDto,
@Res() res: Response
): Promise<Response> {
const data = await this.oid4vcHolderService.oidcHolderResolveProofRequest(orgId, resolveDto);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: 'Proof request resolved successfully.',
data
};
return res.status(HttpStatus.OK).json(finalResponse);
}

@Post('accept-proof-request')
@ApiOperation({
summary: 'Accept OID4VC Proof Request',
description: 'Accepts an OID4VC proof request for the specified organization.'
})
@ApiResponse({ status: HttpStatus.OK, description: 'Proof request accepted successfully.', type: ApiResponseDto })
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async oidcHolderAcceptProofRequest(
@Param('orgId') orgId: string,
@Body() acceptDto: OidcAcceptProofRequestDto,
@Res() res: Response
): Promise<Response> {
const data = await this.oid4vcHolderService.oidcHolderAcceptProofRequest(orgId, acceptDto);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: 'Proof request accepted successfully.',
data
};
return res.status(HttpStatus.OK).json(finalResponse);
}
}
28 changes: 28 additions & 0 deletions apps/api-gateway/src/oid4vc-holder/oid4vc-holder.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Module } from '@nestjs/common';
import { NATSClient } from '@credebl/common/NATSClient';
import { getNatsOptions } from '@credebl/common/nats.config';
import { CommonConstants } from '@credebl/common/common.constant';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { HttpModule } from '@nestjs/axios';
import { Oid4vcHolderController } from './oid4vc-holder.controller';
import { Oid4vcHolderService } from './oid4vc-holder.service';

@Module({
imports: [
HttpModule,
ClientsModule.register([
{
name: 'NATS_CLIENT',
transport: Transport.NATS,
options: getNatsOptions(
CommonConstants.OIDC4VC_HOLDER_SERVICE,
process.env.API_GATEWAY_NKEY_SEED,
process.env.NATS_CREDS_FILE
)
}
])
],
controllers: [Oid4vcHolderController],
providers: [Oid4vcHolderService, NATSClient]
})
export class Oid4vcHolderModule {}
40 changes: 40 additions & 0 deletions apps/api-gateway/src/oid4vc-holder/oid4vc-holder.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { NATSClient } from '@credebl/common/NATSClient';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { BaseService } from 'libs/service/base.service';
import {
OidcAcceptProofRequestDto,
OidcRequestCredentialDto,
OidcResolveCredentialOfferDto,
OidcResolveProofRequestDto
} from './dtos/oid4vc-holder.dto';

@Injectable()
export class Oid4vcHolderService extends BaseService {
constructor(
@Inject('NATS_CLIENT') private readonly holderProxy: ClientProxy,
private readonly natsClient: NATSClient
) {
super('Oid4vcHolderService');
}

async oidcHolderResolveCredentialOffer(orgId: string, holderPayload: OidcResolveCredentialOfferDto): Promise<object> {
const payload = { orgId, holderPayload };
return this.natsClient.sendNatsMessage(this.holderProxy, 'oid4vc-holder-resolve-credential-offer', payload);
}

async oidcHolderRequestCredential(orgId: string, holderPayload: OidcRequestCredentialDto): Promise<object> {
const payload = { orgId, holderPayload };
return this.natsClient.sendNatsMessage(this.holderProxy, 'oid4vc-holder-request-credential', payload);
}

async oidcHolderResolveProofRequest(orgId: string, holderPayload: OidcResolveProofRequestDto): Promise<object> {
const payload = { orgId, holderPayload };
return this.natsClient.sendNatsMessage(this.holderProxy, 'oid4vc-holder-resolve-proof-request', payload);
}

async oidcHolderAcceptProofRequest(orgId: string, holderPayload: OidcAcceptProofRequestDto): Promise<object> {
const payload = { orgId, holderPayload };
return this.natsClient.sendNatsMessage(this.holderProxy, 'oid4vc-holder-accept-proof-request', payload);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,13 @@ export class CredentialDto {

@ApiProperty({
description: 'Credential format type',
enum: ['mso_mdoc', 'vc+sd-jwt'],
enum: ['mso_mdoc', 'vc+sd-jwt', 'jwt_vc_json-ld'],
example: 'mso_mdoc'
})
@IsString()
@IsIn(['mso_mdoc', 'vc+sd-jwt'], { message: 'format must be either "mso_mdoc" or "vc+sd-jwt"' })
@IsIn(['mso_mdoc', 'vc+sd-jwt', 'jwt_vc_json-ld'], {
message: 'format must be either "mso_mdoc", "vc+sd-jwt" or "jwt_vc_json-ld"'
})
format: string;

@ApiProperty({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ export class SdJwtTemplateDto {
@IsString()
vct: string;

@ApiPropertyOptional({
example: ['https://www.w3.org/2018/credentials/v1', 'https://json-ld.org/contexts/person.jsonld'],
description: 'JSON-LD context for the credential (optional, defaults to person schema for JSON-LD format)'
})
@IsArray()
@IsOptional()
context?: string[];

Comment on lines +199 to +206

@coderabbitai coderabbitai Bot Jun 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add element-level string validation for context array.

The @IsArray decorator validates the field is an array, but doesn't validate that each element is a string. A malformed payload with non-string elements (e.g., context: [123, {}]) would pass validation.

Proposed fix
   `@ApiPropertyOptional`({
     example: ['https://www.w3.org/2018/credentials/v1', 'https://json-ld.org/contexts/person.jsonld'],
     description: 'JSON-LD context for the credential (optional, defaults to person schema for JSON-LD format)'
   })
   `@IsArray`()
+  `@IsString`({ each: true })
   `@IsOptional`()
   context?: string[];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ApiPropertyOptional({
example: ['https://www.w3.org/2018/credentials/v1', 'https://json-ld.org/contexts/person.jsonld'],
description: 'JSON-LD context for the credential (optional, defaults to person schema for JSON-LD format)'
})
@IsArray()
@IsOptional()
context?: string[];
`@ApiPropertyOptional`({
example: ['https://www.w3.org/2018/credentials/v1', 'https://json-ld.org/contexts/person.jsonld'],
description: 'JSON-LD context for the credential (optional, defaults to person schema for JSON-LD format)'
})
`@IsArray`()
`@IsString`({ each: true })
`@IsOptional`()
context?: string[];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api-gateway/src/oid4vc-issuance/dtos/oid4vc-issuer-template.dto.ts`
around lines 199 - 206, The context property is only decorated with `@IsArray` and
`@IsOptional`, so non-string elements can slip through; update the DTO by adding
element-level string validation to the context?: string[] property (use the
class-validator decorator that validates each array element is a string, e.g.,
`@IsString`({ each: true })) alongside the existing decorators on the property in
the Oid4vc issuer template DTO.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not expose or accept the context array directly from the user in the DTO. Instead, the backend automatically constructs it internally from the schemaUrl (which is strictly validated as a string via @IsString()) and a default constant. Therefore, this issue is already mitigated.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@ApiProperty({
type: 'array',
items: { $ref: getSchemaPath(CredentialAttributeDto) },
Expand Down Expand Up @@ -229,8 +237,11 @@ export class CreateCredentialTemplateDto {
@IsEnum(CredentialFormat)
format: CredentialFormat;

@ValidateIf((o: CreateCredentialTemplateDto) => CredentialFormat.SdJwtVc === o.format)
@IsEmpty({ message: 'doctype must not be provided when format is "dc+sd-jwt"' })
@ValidateIf(
(o: CreateCredentialTemplateDto) =>
CredentialFormat.SdJwtVc === o.format || CredentialFormat.JwtVcJsonLd === o.format
)
Comment on lines +240 to +245

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix the lint-blocking ValidateIf arrow formatting.

The current multiline arrow predicate triggers the ESLint implicit-arrow-linebreak error and will fail CI.

Proposed fix
-  `@ValidateIf`(
-    (o: CreateCredentialTemplateDto) =>
-      CredentialFormat.SdJwtVc === o.format || CredentialFormat.JwtVcJsonLd === o.format
-  )
+  `@ValidateIf`(
+    (o: CreateCredentialTemplateDto) =>
+      CredentialFormat.SdJwtVc === o.format || CredentialFormat.JwtVcJsonLd === o.format
+  )
🧰 Tools
🪛 ESLint

[error] 234-234: Expected no linebreak before this expression.

(implicit-arrow-linebreak)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api-gateway/src/oid4vc-issuance/dtos/oid4vc-issuer-template.dto.ts`
around lines 232 - 235, The multiline arrow predicate for the `@ValidateIf`
decorator is causing an implicit-arrow-linebreak lint error; update the
predicate to be a single-line arrow function so ESLint accepts it. Locate the
`@ValidateIf` on CreateCredentialTemplateDto and replace the multiline predicate
with a single-line form such as (o: CreateCredentialTemplateDto) =>
CredentialFormat.SdJwtVc === o.format || CredentialFormat.JwtVcJsonLd ===
o.format, ensuring the decorator remains intact and the condition logic is
unchanged.

@IsEmpty({ message: 'doctype must not be provided when format is "dc+sd-jwt" or "jwt_vc_json-ld"' })
readonly _doctypeAbsentGuard?: unknown;

@ValidateIf((o: CreateCredentialTemplateDto) => CredentialFormat.Mdoc === o.format)
Expand All @@ -246,7 +257,7 @@ export class CreateCredentialTemplateDto {
@Type(({ object }) => {
if (object.format === CredentialFormat.Mdoc) {
return MdocTemplateDto;
} else if (object.format === CredentialFormat.SdJwtVc) {
} else if (object.format === CredentialFormat.SdJwtVc || object.format === CredentialFormat.JwtVcJsonLd) {
return SdJwtTemplateDto;
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { HttpModule } from '@nestjs/axios';
name: 'NATS_CLIENT',
transport: Transport.NATS,
options: getNatsOptions(
CommonConstants.ISSUANCE_SERVICE,
CommonConstants.OIDC4VC_ISSUANCE_SERVICE,
process.env.API_GATEWAY_NKEY_SEED,
process.env.NATS_CREDS_FILE
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export class DcqlDto {
@ValidatorConstraint({ name: 'OnlyOneOf', async: false })
export class OnlyOneOfConstraint implements ValidatorConstraintInterface {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
validate(_: any, args: ValidationArguments): Promise<boolean> | boolean {
validate(_: unknown, args: ValidationArguments): Promise<boolean> | boolean {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const object = args.object as Record<string, any>;
const properties = args.constraints as string[];
Expand Down Expand Up @@ -356,10 +356,18 @@ export class PresentationRequestDto {
@IsDefined()
@IsEnum(ResponseMode)
responseMode: ResponseMode;
//TODO: check e2e flow and add ResponseMode based restrictions
@IsOptional()
expectedOrigins: string[];

@ApiPropertyOptional({
description: 'OpenID4VP version to use',
enum: ['v1', 'v1.draft21', 'v1.draft24'],
example: 'v1.draft24'
})
@IsOptional()
@IsEnum(['v1', 'v1.draft21', 'v1.draft24'])
version?: 'v1' | 'v1.draft21' | 'v1.draft24';

/**
* Dummy property used to run a class-level validation ensuring mutual exclusivity.
* This property is not serialized into requests/responses but is required so `class-validator`
Expand Down
17 changes: 17 additions & 0 deletions apps/oid4vc-holder/src/interfaces/oid4vc-holder.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface IOidcHolderResolveCredentialOffer {
credentialOfferUri: string;
}

export interface IOidcHolderRequestCredential {
credentialOfferUri: string;
credentialsToRequest: string[];
txCode?: string;
}

export interface IOidcHolderResolveProofRequest {
proofRequestUri: string;
}

export interface IOidcHolderAcceptProofRequest {
proofRequestUri: string;
}
27 changes: 27 additions & 0 deletions apps/oid4vc-holder/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NestFactory } from '@nestjs/core';
import { HttpExceptionFilter } from 'libs/http-exception.filter';
import { Logger } from '@nestjs/common';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { getNatsOptions } from '@credebl/common/nats.config';
import { CommonConstants } from '@credebl/common/common.constant';
import NestjsLoggerServiceAdapter from '@credebl/logger/nestjsLoggerServiceAdapter';
import { Oid4vcHolderModule } from './oid4vc-holder.module';

const logger = new Logger();

async function bootstrap(): Promise<void> {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(Oid4vcHolderModule, {
transport: Transport.NATS,
options: getNatsOptions(
CommonConstants.OIDC4VC_HOLDER_SERVICE,
process.env.OIDC4VC_HOLDER_NKEY_SEED,
process.env.NATS_CREDS_FILE
)
});
app.useLogger(app.get(NestjsLoggerServiceAdapter));
app.useGlobalFilters(new HttpExceptionFilter());

await app.listen();
logger.log('OID4VC-Holder-Service Microservice is listening to NATS ');
}
bootstrap();
Loading