Skip to content

feat: support W3C JSON-LD credential issuance and presentation#389

Open
sagarkhole4 wants to merge 6 commits into
mainfrom
feat/JSONLD_JWTVC
Open

feat: support W3C JSON-LD credential issuance and presentation#389
sagarkhole4 wants to merge 6 commits into
mainfrom
feat/JSONLD_JWTVC

Conversation

@sagarkhole4

@sagarkhole4 sagarkhole4 commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features
    • Added W3C credential format support with full storage and retrieval capabilities
    • New endpoint to retrieve stored W3C credentials
    • Support for credential versioning (v1.1 and v2.0 formats)
    • DCQL-based presentation request validation
    • Enhanced credential binding for additional JWT formats
    • UI customization options for verifier client (background and text colors)
    • Pre-authorized code flow enhancements

Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
@sagarkhole4 sagarkhole4 self-assigned this May 20, 2026
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

📝 Walkthrough

Walkthrough

Adds W3C Verifiable Credential support to the OpenID4VC holder, issuer, and verifier flows. New endpoints and service methods enable retrieval, storage, and deletion of W3C credential records. Credential offer generation gains version-aware payload transformation (v1.1/v2.0), and a new mapping branch handles JwtVcJson/JwtVcJsonLd formats. DCQL types are formally defined, and Presentation Exchange is re-enabled in the verifier.

Changes

W3C VC Support Implementation

Layer / File(s) Summary
Type Contracts, Enums & DCQL Interfaces
src/controllers/openid4vc/types/holder.types.ts, src/controllers/openid4vc/types/issuer.types.ts, src/controllers/openid4vc/types/verifier.types.ts
Adds W3C_VC enum member, IssuerObject/CredentialSubject interfaces, version fields for issuance (v1.1/v2.0) and authorization (v1/v1.draft21/v1.draft24), typed DCQL interfaces replacing loosely-typed shapes, preAuthorizedCode field, and optional verifier metadata background_color/text_color.
Holder W3C Credential Retrieval Endpoint
src/controllers/openid4vc/holder/holder.service.ts, src/controllers/openid4vc/holder/holder.Controller.ts, src/routes/routes.ts
Adds HolderService.getW3cCredentials importing W3cCredentialRecord/W3cCredentialService, new GET /openid4vc/holder/w3c-vcs controller method, and JWT-authenticated TSOA route dispatching to HolderController.getW3cCredentials.
Holder Credential Storage, Deletion & Binding
src/controllers/openid4vc/holder/holder.service.ts, src/controllers/openid4vc/holder/credentialBindingResolver.ts
Extends requestAndStoreCredentials to detect and store W3cCredentialRecord, adds W3C_VC branch in deleteCredential using W3cCredentialService.removeCredentialRecord, and expands binding resolver to include JwtVcJsonLd/JwtVcJson in the plain JWK binding path.
Holder Presentation Request DCQL/PE Routing
src/controllers/openid4vc/holder/holder.service.ts
Refactors acceptPresentationRequest to build acceptOptions and route credential selection through either DCQL (acceptOptions.dcql.credentials) or Presentation Exchange (acceptOptions.presentationExchange.credentials), throwing if neither is present.
Issuer Offer Version-Aware Generation
src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts
Adds effectiveVersion computation per credential format, extends validateCredentialConfig to require credentialSubject for W3C formats and validate issuer.id for v2.0, passes preAuthorizedCodeFlowConfig and version:'v1' directly, and introduces transformPayloadForVersion for v2.0 date/context normalization.
JWT VC JSON to W3C Credential Transformation
src/utils/oid4vc-agent.ts
Adds JwtVcJson/JwtVcJsonLd mapping branch constructing V1/V2-aware credential JSON with normalized credentialSubject.id, derived issuer, V2 date-field aliases, and JsonTransformer conversion to W3cV2Credential/W3cCredential; also updates MsoMdoc path to parse the full x509 certificate chain and set keyId.
Verifier Version & Presentation Exchange Support
src/controllers/openid4vc/verifier-sessions/verification-sessions.service.ts
Passes dto.version into createAuthorizationRequest, switches presentationExchange from error-throwing to supported assignment, and sets publicJwk.keyId before assigning x5c in the X.509 signer path.
Generated Routes & OpenAPI Schema
src/routes/routes.ts, src/routes/swagger.json
TSOA-generated files reflecting all type, endpoint, and schema changes: new DCQL schemas, expanded SD-JWT/W3C offer payloads, W3cCredentialRecord, W3cIssuer/W3cCredentialSchema/W3cCredentialStatus/W3cCredentialSubject components, renamed @contextcontext in W3cJsonLdVerifiableCredential, and the new GET /openid4vc/holder/w3c-vcs endpoint.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HolderController
  participant HolderService
  participant CredentialMapper as getMixedCredentialRequestToCredentialMapper
  participant W3cCredentialStore as agent.w3cCredentials
  participant JsonTransformer

  rect rgba(70, 130, 180, 0.5)
    note over Client, W3cCredentialStore: W3C Credential Issuance (JwtVcJson/JwtVcJsonLd)
    Client->>HolderController: POST requestAndStoreCredentials
    HolderController->>HolderService: requestAndStoreCredentials(agentReq)
    HolderService->>CredentialMapper: map JwtVcJson/JwtVcJsonLd format
    CredentialMapper->>JsonTransformer: fromJSON(credentialJson, V1/V2 context)
    JsonTransformer-->>CredentialMapper: W3cCredential or W3cV2Credential
    CredentialMapper-->>HolderService: credential with verificationMethod
    HolderService->>W3cCredentialStore: store({ record: W3cCredentialRecord })
    W3cCredentialStore-->>HolderService: stored
    HolderService-->>HolderController: result
    HolderController-->>Client: stored credentials
  end

  rect rgba(60, 179, 113, 0.5)
    note over Client, W3cCredentialStore: W3C Credential Retrieval
    Client->>HolderController: GET /openid4vc/holder/w3c-vcs
    HolderController->>HolderService: getW3cCredentials(request)
    HolderService->>W3cCredentialStore: getAll()
    W3cCredentialStore-->>HolderService: W3cCredentialRecord[]
    HolderService-->>HolderController: W3cCredentialRecord[]
    HolderController-->>Client: 200 W3cCredentialRecord[]
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • feat: Support for W3c JSONLD credential using oid4vc platform#1639: This PR directly implements W3C JSON-LD credential issuance support via OpenID4VC, adding JwtVcJsonLd/JwtVcJson credential format handlers, W3C credential storage/retrieval/deletion endpoints, version-aware payload transformations, and the corresponding type definitions.

Possibly related PRs

  • credebl/agent-controller#357: Both PRs modify createCredentialOffer logic in issuance-sessions.service.ts, with one adding W3C/version/payload transformations and the other adding revocation status-list block injection.
  • credebl/agent-controller#344: Both PRs modify acceptPresentationRequest in holder.service.ts; this PR refactors credential selection via acceptOptions/DCQL routing, the other tweaks response/result handling.
  • credebl/agent-controller#341: Both PRs modify oid4vc-agent.ts for issuer certificate parsing and keyId attachment in the x509/x5c construction code path.

Suggested labels

enhancement

Suggested reviewers

  • RinkalBhojani
  • tipusinghaw
  • GHkrishna

Poem

🐰 Hop hop, new credentials in store,
W3C VCs come bouncing through the door!
JwtVcJson mapped with care,
V1 and V2 contexts fair.
DCQL typed, the verifier's bright —
This bunny's pleased to see it right! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main changes across the codebase: adding support for W3C JSON-LD credential issuance and presentation flows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/JSONLD_JWTVC

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/controllers/openid4vc/holder/credentialBindingResolver.ts (1)

100-107: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update fallback messaging to match newly supported JWT VC formats.

The branch now supports JwtVcJsonLd and JwtVcJson, but the comment/error text still implies only sd-jwt/mdoc support.

✏️ Suggested text alignment
-    // Otherwise we also support plain jwk for sd-jwt only
+    // Otherwise we also support plain jwk for sd-jwt, jwt-vc-json(-ld), and mdoc
@@
-      `No supported binding method could be found. Supported methods are did:key and did:jwk, or plain jwk for sd-jwt/mdoc. Issuer supports ${
+      `No supported binding method could be found. Supported methods are did:key and did:jwk, or plain jwk for sd-jwt/jwt-vc-json/jwt-vc-json-ld/mdoc. Issuer supports ${
         supportsJwk ? 'jwk, ' : ''
       }${supportedDidMethods?.join(', ') ?? 'Unknown'}`,

Also applies to: 115-118

🤖 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 `@src/controllers/openid4vc/holder/credentialBindingResolver.ts` around lines
100 - 107, Update the comment/error text in credentialBindingResolver.ts to
reflect the expanded fallback support: where the code checks supportsJwk and
credentialFormat against OpenId4VciCredentialFormatProfile (including SdJwtVc,
SdJwtDc, JwtVcJsonLd, JwtVcJson, MsoMdoc), change any messaging that currently
mentions only "sd-jwt/mdoc" to list or generically state "sd-jwt, JWT VC
(JSON/JSON-LD) and mdoc" so logs and comments align with the actual supported
formats checked by the supportsJwk/credentialFormat branch.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/controllers/openid4vc/holder/holder.service.ts`:
- Line 157: The if-condition formatting around credentialRecord and
W3cCredentialRecord is violating Prettier; update the three occurrences (the
conditional at the current if and the ones around lines referenced) to a
Prettier-compliant style by reformatting the conditional expression that checks
instance and type (use a consistent cast/parentheses and spacing for
"(credentialRecord as any).type" or prefer a safe type guard) so the file
holder.service.ts compiles with linting; locate references to credentialRecord
and W3cCredentialRecord in the methods around the mentioned checks and apply the
consistent formatting change.

In `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`:
- Around line 36-43: transformPayloadForVersion currently returns the original
payload so effectiveVersion is ignored and downstream mapping still treats
offers as v1; update transformPayloadForVersion (and its calls in
issuance-sessions.service.ts where effectiveVersion is computed) to actually
rewrite the credential JSON-LD for 'v2.0' (e.g., set `@context`, validFrom,
structure and vct fields to v2 names) or, alternatively, persist the requested
version on the session/offer object (e.g., add a requestedVersion property) so
oid4vc-agent's mapper (which decides between W3cCredential and W3cV2Credential)
can read it; locate transformPayloadForVersion and any code referencing
effectiveVersion/statusBlock/cred.payload and implement the real transformation
or persist the explicit version flag accordingly.
- Around line 71-72: The code always forces version to 'v1' when building the
offer (see the assignment to version in the issuance session creation), so
options.version is ignored; change the logic in the offer creation (the
variable/assignment that currently reads version: options.version ? 'v1' : 'v1')
to actually use options.version when provided (e.g., set version =
options.version ?? 'v1' or pass options.version directly) so the emitted offer
reflects the requested version; update any callers or validation in the issuance
session creation method (e.g., methods in IssuanceSessionsService that construct
the offer) to accept and propagate the provided options.version.
- Around line 88-90: The current validation always requires
cred.payload.credentialSubject and thereby rejects non-W3C credential formats;
update the validator in createCredentialOffer to only enforce credentialSubject
for W3C VC formats — i.e., first detect W3C VCs by checking the credential's
format metadata (e.g., cred.format or cred.format.type / formatId used in your
code) and only then throw BadRequestError using the existing message referencing
cred.credentialSupportedId when cred.payload?.credentialSubject is missing;
leave SdJwtDc and MsoMdoc flows (flat claims or namespaces) untouched so their
mapper logic can run.

In `@src/controllers/openid4vc/types/issuer.types.ts`:
- Line 7: Replace the value import of SignerMethod with a type-only import since
SignerMethod is used only in type positions; update the import statement that
currently reads "import { SignerMethod } ..." to use a type import (e.g.,
"import type { SignerMethod } ...") so it satisfies
`@typescript-eslint/consistent-type-imports` and unblocks linting for the types
referenced in issuer.types.ts.

In
`@src/controllers/openid4vc/verifier-sessions/verification-sessions.service.ts`:
- Line 66: Remove the trailing comma after "version: dto.version" in the object
literal and reformat the indented block around lines 180-184 to match project
Prettier settings; specifically, in the VerificationSessionsService (where the
verification session object is constructed) remove the extra comma from the
"version: dto.version" property and align the nested block (the subsequent
object/array entries) to the same indentation level as other object properties
so Prettier linting passes.
- Around line 74-82: The code must enforce mutual exclusivity between
dto.presentationExchange and dto.dcql before building options for
verifier.createAuthorizationRequest: validate that not both are provided and
throw/return a clear error when they are; if only presentationExchange is
present set options.presentationExchange = dto.presentationExchange and do not
set options.dcql, otherwise set options.dcql = dto.dcql; keep the existing
parsedCertificate/requestSigner handling (parsedCertificate.publicJwk.keyId and
options.requestSigner.x5c) unchanged but ensure options.dcql is not forwarded
when presentationExchange is used and vice versa.

In `@src/routes/routes.ts`:
- Around line 1444-1473: The schemas W3cIssuer, W3cCredentialStatus, and
W3cCredentialSubject are too strict (additionalProperties: false) and reject
valid VCs; fix by updating the original TypeScript model definitions that
produce these refs so tsoa will allow extra top-level fields — add an index
signature (e.g. [key: string]: any) or otherwise enable additionalProperties on
the interfaces/types for W3cIssuer, W3cCredentialStatus, and
W3cCredentialSubject (and ensure W3cCredentialSubject still includes the
existing id and claims properties but permits other top-level terms); then
re-run tsoa to regenerate routes.ts so the generated refs no longer set
additionalProperties: false.

In `@src/routes/swagger.json`:
- Around line 1783-1804: The schema for validFrom and validUntil currently uses
anyOf with {type: "string"} plus {type: "string", format: "date-time"}, which
effectively allows any string and bypasses date-time validation; replace the
anyOf with a single schema that enforces date-time (e.g., { "type": "string",
"format": "date-time" }) for the properties named validFrom and validUntil in
the swagger.json so the format constraint is enforced (also make the identical
change for the other occurrence around the 2057-2078 region).

In `@src/utils/oid4vc-agent.ts`:
- Around line 206-247: The code flattens array credentialSubject values by doing
"{ ...(payload.credentialSubject || {}) }" and later assumes a single object
when assigning ids; change the logic to detect whether payload.credentialSubject
is an Array or an Object (preserve arrays as-is), build credentialSubjectRaw
accordingly, and when applying subject ids set the id on each object element
(e.g., loop through credentialSubjectRaw array and assign subjectId per element)
before creating credInstance via JsonTransformer.fromJSON (used with
W3cV2Credential / W3cCredential), and likewise, when mutating
credInstance.credentialSubject assign id per-element if it's an array or assign
directly if it's an object.
- Around line 260-262: The catch block that currently does
fs.appendFileSync('mapper-error.log', e.stack + '\n') (the catch (e: any) {...}
that ends with throw e;) must not perform a synchronous file write that can
throw and block; either log the stack via the existing logger (e.g.,
processLogger or logger) instead of appendFileSync, or if you still want a file
fallback use fs.promises.appendFile(...) and wrap that call in its own try/catch
so any error from the write is swallowed or logged but does not override the
original exception; in all cases ensure you always rethrow the original error
variable e with throw e.
- Around line 193-253: The mapping for JwtVcJson/JwtVcJsonLd currently always
uses issuerDidVerificationMethod and thus drops SignerMethod.X5c signer data;
fix by detecting when credential.signerOptions.method === SignerMethod.X5c (or
binding.method indicates x5c) and either (A) reject/unable-to-map with a clear
error/throw when X5c is configured for JwtVcJson/JwtVcJsonLd, or (B) implement
the x5c path: populate verificationMethod with an object containing the x5c
certificate chain (e.g., include x5c array and appropriate type/id) instead of
issuerDidVerificationMethod, and ensure the returned credential entry (inside
the credentials.map) carries that verificationMethod. Reference symbols:
JwtVcJson/JwtVcJsonLd branch, credential.signerOptions.method, SignerMethod.X5c,
issuerDidVerificationMethod, and the credentials.map return object.

---

Outside diff comments:
In `@src/controllers/openid4vc/holder/credentialBindingResolver.ts`:
- Around line 100-107: Update the comment/error text in
credentialBindingResolver.ts to reflect the expanded fallback support: where the
code checks supportsJwk and credentialFormat against
OpenId4VciCredentialFormatProfile (including SdJwtVc, SdJwtDc, JwtVcJsonLd,
JwtVcJson, MsoMdoc), change any messaging that currently mentions only
"sd-jwt/mdoc" to list or generically state "sd-jwt, JWT VC (JSON/JSON-LD) and
mdoc" so logs and comments align with the actual supported formats checked by
the supportsJwk/credentialFormat branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75954c14-55f0-4b8c-bd82-a66658c621a6

📥 Commits

Reviewing files that changed from the base of the PR and between 3116c1a and cdfee53.

📒 Files selected for processing (12)
  • src/controllers/openid4vc/holder/credentialBindingResolver.ts
  • src/controllers/openid4vc/holder/holder.Controller.ts
  • src/controllers/openid4vc/holder/holder.service.ts
  • src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts
  • src/controllers/openid4vc/types/holder.types.ts
  • src/controllers/openid4vc/types/issuer.types.ts
  • src/controllers/openid4vc/types/verifier.types.ts
  • src/controllers/openid4vc/verifier-sessions/verification-sessions.service.ts
  • src/enums/enum.ts
  • src/routes/routes.ts
  • src/routes/swagger.json
  • src/utils/oid4vc-agent.ts

Comment thread src/controllers/openid4vc/holder/holder.service.ts Outdated
Comment thread src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts Outdated
Comment thread src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts Outdated
Comment thread src/controllers/openid4vc/types/issuer.types.ts Outdated
Comment thread src/routes/routes.ts
Comment thread src/routes/swagger.json
Comment thread src/utils/oid4vc-agent.ts Outdated
Comment thread src/utils/oid4vc-agent.ts Outdated
Comment thread src/utils/oid4vc-agent.ts Outdated
@sagarkhole4 sagarkhole4 requested a review from sairanjit May 21, 2026 03:37
Comment thread src/controllers/openid4vc/holder/holder.service.ts Outdated
Comment thread src/controllers/openid4vc/holder/holder.service.ts Outdated
Comment thread src/enums/enum.ts Outdated
Comment thread src/utils/oid4vc-agent.ts Outdated
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts (1)

73-73: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pass options.version to the offer instead of hardcoding 'v1'.

The code computes effectiveVersion (line 31) and transforms the payload accordingly, but line 73 always passes version: 'v1' to the offer creation. This means wallets negotiating v2.0 credentials will receive v1 offer metadata despite having v2.0-transformed payloads, making the versioning system ineffective.

🔧 Proposed fix
-      version: 'v1',
+      version: options.version ?? 'v1',
🤖 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 `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts` at
line 73, The offer is being built with a hardcoded version: 'v1' which ignores
the computed effectiveVersion and breaks v2 negotiations; update the object
passed to the offer creation in issuance-sessions.service.ts to use the computed
effectiveVersion (or options.version if that's what you prefer) instead of the
literal 'v1' so the version field matches the transformed payload (refer to
effectiveVersion and the offer creation call near the existing version: 'v1'
line).
src/controllers/openid4vc/holder/holder.service.ts (1)

47-58: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Major: complete the W3C VC v2.0 holder lifecycle (store/list/delete)

  • getW3cCredentials only returns agentReq.agent.w3cCredentials.getAll(); w3cV2Credentials.getAll() support is commented out.
  • requestAndStoreCredentials only stores W3cCredentialRecord; the W3cV2CredentialRecord store branch is commented out, so v2 records hit Unsupported credential record type.
  • deleteCredential only uses W3cCredentialService; the W3cV2CredentialService.removeCredentialRecord fallback is commented out and v2 deletion never happens.

This breaks the advertised v2.0 flow (version?: 'v1.1' | 'v2.0', plus v2 mapping in oid4vc-agent.ts -> W3cV2Credential).

Suggested direction
 import {
   Mdoc,
   SdJwtVcRecord,
   MdocRecord,
   W3cCredentialRecord,
   W3cCredentialService,
-  // W3cV2CredentialRecord,
-  // W3cV2CredentialService,
+  W3cV2CredentialRecord,
+  W3cV2CredentialService,
 } from '`@credo-ts/core`'
@@
   public async getW3cCredentials(agentReq: Req) {
-    return await agentReq.agent.w3cCredentials.getAll()
+    const [v1Records, v2Records] = await Promise.all([
+      agentReq.agent.w3cCredentials.getAll(),
+      agentReq.agent.w3cV2Credentials.getAll(),
+    ])
+
+    return [...v1Records, ...v2Records]
   }
@@
         if (
           credentialRecord instanceof W3cCredentialRecord ||
           (credentialRecord as { type?: string }).type === 'W3cCredentialRecord'
         ) {
           return await agentReq.agent.w3cCredentials.store({
             record: credentialRecord as W3cCredentialRecord,
           })
         }
+
+        if (
+          credentialRecord instanceof W3cV2CredentialRecord ||
+          (credentialRecord as { type?: string }).type === 'W3cV2CredentialRecord'
+        ) {
+          return await agentReq.agent.w3cV2Credentials.store({
+            record: credentialRecord as W3cV2CredentialRecord,
+          })
+        }
@@
     if (credentialType === CredentialType.W3C_VC) {
       const w3cCredentialService = await agentReq.agent.dependencyManager.resolve(W3cCredentialService)
+      const w3cV2CredentialService = await agentReq.agent.dependencyManager.resolve(W3cV2CredentialService)
@@
       try {
         return await w3cCredentialService.removeCredentialRecord(agentReq.agent.context, credentialId)
       } catch (error) {
-        throw error
+        return await w3cV2CredentialService.removeCredentialRecord(agentReq.agent.context, credentialId)
       }
     }
🤖 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 `@src/controllers/openid4vc/holder/holder.service.ts` around lines 47 - 58,
getW3cCredentials currently returns only agentReq.agent.w3cCredentials.getAll()
and the w3cV2 path is commented out—re-enable and combine both by fetching
agentReq.agent.w3cCredentials.getAll() and
agentReq.agent.w3cV2Credentials.getAll() (e.g., Promise.all) and returning the
concatenated results; in requestAndStoreCredentials un-comment and implement the
W3cV2 branch so that when the credential is a W3cV2CredentialRecord you store it
using the W3cV2CredentialRecord handling code (mirror the W3cCredentialRecord
branch but use the v2 record/service); in deleteCredential try removing via
W3cCredentialService first and if not found/fails, call
W3cV2CredentialService.removeCredentialRecord as a fallback so v2 records are
deletable (ensure you reference the existing functions/classes:
getW3cCredentials, requestAndStoreCredentials, deleteCredential,
W3cCredentialRecord, W3cV2CredentialRecord,
agentReq.agent.w3cV2Credentials.getAll(), and
W3cV2CredentialService.removeCredentialRecord).
🧹 Nitpick comments (1)
src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts (1)

174-197: ⚡ Quick win

Remove unreachable else block.

The early return at line 129 ensures that lines 133-199 only execute when version === 'v2.0'. This makes the conditional at line 174 always true and the else block (lines 186-197) unreachable dead code.

♻️ Proposed cleanup
-    if (version === 'v2.0') {
-      const currentCtx = Array.isArray(transformed['`@context`'])
-        ? transformed['`@context`']
-        : typeof transformed['`@context`'] === 'string'
-          ? [transformed['`@context`']]
-          : []
-
-      const ctxSet = new Set(currentCtx)
-      ctxSet.delete(v1Context)
-      ctxSet.delete(v2Context)
-      // W3C V2.0 requires the V2 context to be the very first element.
-      transformed['`@context`'] = [v2Context, v1Context, ...Array.from(ctxSet)]
-    } else {
-      // W3C V1.1 / Default behavior
-      if (!transformed['`@context`']) {
-        transformed['`@context`'] = [v1Context]
-      } else if (Array.isArray(transformed['`@context`'])) {
-        const ctxSet = new Set(transformed['`@context`'])
-        ctxSet.delete(v1Context)
-        transformed['`@context`'] = [v1Context, ...Array.from(ctxSet)]
-      } else if (typeof transformed['`@context`'] === 'string') {
-        transformed['`@context`'] = [v1Context, transformed['`@context`']]
-      }
-    }
+    const currentCtx = Array.isArray(transformed['`@context`'])
+      ? transformed['`@context`']
+      : typeof transformed['`@context`'] === 'string'
+        ? [transformed['`@context`']]
+        : []
+
+    const ctxSet = new Set(currentCtx)
+    ctxSet.delete(v1Context)
+    ctxSet.delete(v2Context)
+    // W3C V2.0 requires the V2 context to be the very first element.
+    transformed['`@context`'] = [v2Context, v1Context, ...Array.from(ctxSet)]
🤖 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 `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`
around lines 174 - 197, The else branch that handles non-v2 contexts is dead
code because the surrounding function returns early unless version === 'v2.0';
remove the unreachable else block and simplify the code to only perform the v2.0
handling for transformed['`@context`'] (using v2Context, v1Context and ctxSet),
keeping the existing logic that ensures v2Context is first and v1Context second;
also remove any related array/string checks inside the removed branch and ensure
no unused variables remain (version, v1Context, v2Context,
transformed['`@context`']).
🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`:
- Around line 5-7: Reorder and group the related `@credo-ts` imports to satisfy
import-order rules: move the CREDENTIALS_CONTEXT_V1_URL and
CREDENTIALS_CONTEXT_V2_URL import (symbols CREDENTIALS_CONTEXT_V1_URL,
CREDENTIALS_CONTEXT_V2_URL) so it appears before the
OpenId4VcIssuanceSessionRepository import (symbol
OpenId4VcIssuanceSessionRepository) and remove the blank line between them so
both `@credo-ts` imports are adjacent and correctly ordered.

---

Duplicate comments:
In `@src/controllers/openid4vc/holder/holder.service.ts`:
- Around line 47-58: getW3cCredentials currently returns only
agentReq.agent.w3cCredentials.getAll() and the w3cV2 path is commented
out—re-enable and combine both by fetching
agentReq.agent.w3cCredentials.getAll() and
agentReq.agent.w3cV2Credentials.getAll() (e.g., Promise.all) and returning the
concatenated results; in requestAndStoreCredentials un-comment and implement the
W3cV2 branch so that when the credential is a W3cV2CredentialRecord you store it
using the W3cV2CredentialRecord handling code (mirror the W3cCredentialRecord
branch but use the v2 record/service); in deleteCredential try removing via
W3cCredentialService first and if not found/fails, call
W3cV2CredentialService.removeCredentialRecord as a fallback so v2 records are
deletable (ensure you reference the existing functions/classes:
getW3cCredentials, requestAndStoreCredentials, deleteCredential,
W3cCredentialRecord, W3cV2CredentialRecord,
agentReq.agent.w3cV2Credentials.getAll(), and
W3cV2CredentialService.removeCredentialRecord).

In `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`:
- Line 73: The offer is being built with a hardcoded version: 'v1' which ignores
the computed effectiveVersion and breaks v2 negotiations; update the object
passed to the offer creation in issuance-sessions.service.ts to use the computed
effectiveVersion (or options.version if that's what you prefer) instead of the
literal 'v1' so the version field matches the transformed payload (refer to
effectiveVersion and the offer creation call near the existing version: 'v1'
line).

---

Nitpick comments:
In `@src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts`:
- Around line 174-197: The else branch that handles non-v2 contexts is dead code
because the surrounding function returns early unless version === 'v2.0'; remove
the unreachable else block and simplify the code to only perform the v2.0
handling for transformed['`@context`'] (using v2Context, v1Context and ctxSet),
keeping the existing logic that ensures v2Context is first and v1Context second;
also remove any related array/string checks inside the removed branch and ensure
no unused variables remain (version, v1Context, v2Context,
transformed['`@context`']).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f2c9616-ddad-472a-a729-4f264f42ce21

📥 Commits

Reviewing files that changed from the base of the PR and between cdfee53 and 2a653b1.

📒 Files selected for processing (4)
  • src/controllers/openid4vc/holder/holder.service.ts
  • src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts
  • src/controllers/openid4vc/types/issuer.types.ts
  • src/controllers/openid4vc/verifier-sessions/verification-sessions.service.ts

Comment thread src/controllers/openid4vc/issuance-sessions/issuance-sessions.service.ts Outdated
    - Updated mDoc issuance to parse and include full X.509 certificate chains.
    - Fixed CBOR date encoding in mDoc validityInfo by casting to Date objects.
    - Added explicit error for unsupported x5c signing in W3C VC formats.
    - Refactored W3C VC mapping logic for better context detection (V1 vs V2).

Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/cliAgent.ts`:
- Around line 214-215: The closing lines of the object passed to the call that
includes documentLoader: CustomDocumentLoader are mis-indented; reformat so the
closing brace/parenthesis align with the opening call (adjust the indents for
the line containing "})") to satisfy Prettier—locate the object containing
documentLoader and fix the indentation of the "})" line to match the call start.

In `@src/utils/oid4vc-agent.ts`:
- Around line 206-208: The isV2 detection currently checks for
CREDENTIALS_CONTEXT_V2_URL or payload?.validFrom and thus treats payloads that
only include validUntil as v1; update the check to also consider
payload?.validUntil so that credentials with only an expiry are treated as VC
2.0. Locate the isV2 computation in src/utils/oid4vc-agent.ts (the lines using
payload, CREDENTIALS_CONTEXT_V2_URL and validFrom) and add a check for
validUntil; make the same change at the other occurrence referenced (around the
second isV2 check at the later block).
- Around line 126-132: The code assumes issuerx509certificate contains at least
one cert and dereferences parsedCertificates[0]; first validate that
issuerx509certificate.length > 0 before mapping or before accessing
parsedCertificates[0] (in the block using X509Service.parseCertificate and when
assigning parsedCertificates[0].publicJwk.keyId). If the array is empty, either
throw a clear error (e.g., indicating missing x5c chain for
credential.signerOptions) or skip the assignment path that relies on a parsed
certificate so the runtime cannot crash; update references around
issuerx509certificate, parsedCertificates, X509Service.parseCertificate, and
credential.signerOptions.keyId accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e21bfd4-8b6b-4421-b812-3ffe4f03af42

📥 Commits

Reviewing files that changed from the base of the PR and between 2a653b1 and 5cd2a6d.

📒 Files selected for processing (2)
  • src/cliAgent.ts
  • src/utils/oid4vc-agent.ts

Comment thread src/cliAgent.ts Outdated
Comment thread src/utils/oid4vc-agent.ts
Comment thread src/utils/oid4vc-agent.ts Outdated
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/routes/swagger.json (2)

3940-3992: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the JSON-LD property name as @context.

The schema now requires context, but the rest of this OpenAPI document models JSON-LD credentials with @context (W3cJsonCredential, JsonCredential, and the signing options). This makes /agent/credential/verify reject or misdocument the same credential shape /agent/credential/sign accepts.

🔧 Suggested schema fix
-					"context": {
+					"`@context`": {
 						"items": {
 							"anyOf": [
 								{
 									"type": "string"
 								},
 								{
 									"$ref": "`#/components/schemas/JsonObject`"
 								}
 							]
 						},
 						"type": "array"
 					},
...
-					"context",
+					"`@context`",
🤖 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 `@src/routes/swagger.json` around lines 3940 - 3992, In the swagger.json schema
definition (around the context property), change the property name from
"context" to "`@context`" to maintain consistency with other JSON-LD credential
schemas in the document (such as W3cJsonCredential and JsonCredential). This
ensures that the `/agent/credential/verify` endpoint accepts the same credential
shape as the `/agent/credential/sign` endpoint, preventing rejection or
documentation mismatch of credentials.

2134-2146: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Allow object-valued JSON-LD contexts in W3C offers.

OpenId4VciOfferW3cCredential.payload["@context"] only allows string or string array, while the JSON-LD credential schemas below allow context entries to be strings or JSON objects. This rejects custom-context offers that the signing/credential schemas otherwise support.

🔧 Suggested schema fix
 							"`@context`": {
 								"anyOf": [
 									{
 										"type": "string"
 									},
+									{
+										"$ref": "`#/components/schemas/JsonObject`"
+									},
 									{
 										"items": {
-											"type": "string"
+											"anyOf": [
+												{
+													"type": "string"
+												},
+												{
+													"$ref": "`#/components/schemas/JsonObject`"
+												}
+											]
 										},
 										"type": "array"
 									}
 								]
 							},
🤖 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 `@src/routes/swagger.json` around lines 2134 - 2146, The `@context` field
definition in the W3C credential schema currently only allows string or string
array types in its anyOf constraint, but the credential schemas support
object-valued contexts as well. Expand the anyOf array for the `@context`
property to also include object type and array of objects type, allowing it to
accept strings, string arrays, objects, or arrays containing objects, which
aligns with the JSON-LD specification and the actual credential schema
validation.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/utils/oid4vc-agent.ts`:
- Around line 208-209: The line containing the isV2 constant declaration in the
oid4vc-agent.ts file has formatting that does not comply with Prettier
standards. Run Prettier on the file (either via `prettier --write
src/utils/oid4vc-agent.ts` or your configured pre-commit/CI formatter) to
automatically reformat the isV2 assignment and its condition to meet the code
style requirements.

---

Outside diff comments:
In `@src/routes/swagger.json`:
- Around line 3940-3992: In the swagger.json schema definition (around the
context property), change the property name from "context" to "`@context`" to
maintain consistency with other JSON-LD credential schemas in the document (such
as W3cJsonCredential and JsonCredential). This ensures that the
`/agent/credential/verify` endpoint accepts the same credential shape as the
`/agent/credential/sign` endpoint, preventing rejection or documentation
mismatch of credentials.
- Around line 2134-2146: The `@context` field definition in the W3C credential
schema currently only allows string or string array types in its anyOf
constraint, but the credential schemas support object-valued contexts as well.
Expand the anyOf array for the `@context` property to also include object type
and array of objects type, allowing it to accept strings, string arrays,
objects, or arrays containing objects, which aligns with the JSON-LD
specification and the actual credential schema validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 707bee73-e26e-4584-a335-5e4494303e47

📥 Commits

Reviewing files that changed from the base of the PR and between 5cd2a6d and 68e1f2f.

📒 Files selected for processing (4)
  • src/controllers/openid4vc/types/verifier.types.ts
  • src/routes/routes.ts
  • src/routes/swagger.json
  • src/utils/oid4vc-agent.ts
✅ Files skipped from review due to trivial changes (1)
  • src/routes/routes.ts

Comment thread src/utils/oid4vc-agent.ts Outdated
Signed-off-by: Sagar Khole <sagar.khole@ayanworks.com>
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants