Skip to content

feat: add support for w3c vc data model v2#69

Merged
nghaninn merged 3 commits into
alphafrom
feat/add-data-model-v2-support
Aug 13, 2025
Merged

feat: add support for w3c vc data model v2#69
nghaninn merged 3 commits into
alphafrom
feat/add-data-model-v2-support

Conversation

@rongquan1
Copy link
Copy Markdown
Contributor

@rongquan1 rongquan1 commented Aug 12, 2025

Summary by CodeRabbit

  • New Features

    • Added support for W3C VC v2 (contexts, validFrom/validUntil) alongside v1.1.
    • Version-aware mandatory fields for ECDSA-SD-2023 signing.
    • Automatic inclusion of credentialSubject when no selective disclosure pointers are provided.
    • Improved validation: clearer date checks and expiration treated as warnings during verification.
    • Defensive checks: require derive before verify; prevent multiple derivations.
  • Documentation

    • Updated examples to VC v2 context; renamed issuanceDate/expirationDate to validFrom/validUntil where applicable.
  • Chores

    • Upgraded dependency to jsonld-signatures ^11.5.0.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 12, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Upgrades jsonld-signatures dependency and updates the context loader accordingly. Adds VC v2 support alongside v1.1, introducing version-aware mandatory pointers, date-field validation, and base-vs-derived proof checks. Updates ECDSA-SD-2023 signing/deriving/verifying flows and tests to handle both versions. README samples switch to v2 context and fields.

Changes

Cohort / File(s) Change summary
Dependency & context loader
packages/w3c-context/package.json, packages/w3c-context/src/lib/index.ts
Upgrade from jsonld-signatures@7 alias to jsonld-signatures@^11.5.0; adjust imports and call to extendContextLoader accordingly.
VC core logic (ECDSA-SD-2023)
packages/w3c-vc/src/lib/w3c-vc.ts
Version-aware mandatory pointers for signing (v1.1 uses /issuanceDate, v2 uses /validFrom if present); base-proof detection gates verification/derivation; auto-include /credentialSubject when no selective pointers target it; validation for revealedAttributes.
VC helper validation
packages/w3c-vc/src/lib/helper/index.ts
Support VC v2 alongside v1.1: context assertion accepts v1/v2; date-field rules split (issuanceDate for v1.1, validFrom/validUntil for v2); verification warnings for expirations; before-date errors; credentialStatus and proof checks unchanged.
Tests (multi-version)
packages/w3c-vc/src/lib/w3c-vc.test.ts
Parameterized tests for v1.1 and v2 credentials; updated fixtures; coverage for sign-derive-verify, pointer behaviors, mandatory pointers, negative paths, and single-derivation constraint.
Docs (samples)
packages/w3c-vc/README.md
Update ECDSA-SD-2023 examples to credentials v2: context to /ns/credentials/v2, rename issuanceDatevalidFrom, expirationDatevalidUntil.

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant VC as w3c-vc
  participant Ctx as @trustvc/w3c-context
  App->>VC: signCredential(credential, options)
  VC->>Ctx: detect version via @context[0]
  Note right of VC: Build mandatory pointers<br/>v1.1: /issuer, /issuanceDate<br/>v2: /issuer, (/validFrom if present)
  VC-->>App: signed credential
Loading
sequenceDiagram
  participant App
  participant VC as w3c-vc
  App->>VC: deriveCredential(credential, pointers)
  VC->>VC: Check proofValue → base or derived?
  alt Not base
    VC-->>App: Error (must derive from base)
  else Base
    alt No /credentialSubject pointers
      VC->>VC: Auto-add /credentialSubject
    end
    VC-->>App: derived credential (ECDSA-SD-2023)
  end
Loading
sequenceDiagram
  participant App
  participant VC as w3c-vc
  App->>VC: verifyCredential(credential)
  VC->>VC: Check proofValue → base or derived?
  alt Base
    VC-->>App: verified=false, error "deriveCredential() first"
  else Derived
    VC->>VC: Validate dates (v1.1 vs v2), status, proof
    VC-->>App: verification result
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

released on @alpha

Suggested reviewers

  • sgtx-nghaninn
  • nghaninn

Poem

A rabbit signs in moonlit hue,
With v1.1 and v2 in view.
Pointers hop to where they should,
Base to derived—now understood.
Contexts fresh, dates sprout anew,
Thump-thump—the proofs all verify true! 🐇✨

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-data-model-v2-support

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@rongquan1
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 12, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🔭 Outside diff range comments (3)
packages/w3c-vc/src/lib/helper/index.ts (3)

90-95: Date regex accepts invalid formats; escape literal dots

The regex uses . which matches any character. This can let invalid timestamps pass. Escape them to require literal dots.

Apply this diff:

-const dateRegex = new RegExp(
-  '-?([1-9][0-9]{3,}|0[0-9]{3})' +
-    '-(0[1-9]|1[0-2])' +
-    '-(0[1-9]|[12][0-9]|3[01])' +
-    'T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](.[0-9]+)?|(24:00:00(.0+)?))' +
-    '(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?',
-);
+const dateRegex = new RegExp(
+  '-?([1-9][0-9]{3,}|0[0-9]{3})' +
+    '-(0[1-9]|1[0-2])' +
+    '-(0[1-9]|[12][0-9]|3[01])' +
+    'T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?|(24:00:00(\\.0+)?))' +
+    '(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?',
+);

106-117: assertDateString reads raw property; use jsonld.getValues and type-check

If the property is an array or object, .test(value) can behave unexpectedly. Read via jsonld.getValues, ensure single string.

Apply this diff:

-function assertDateString({
-  credential,
-  prop,
-}: {
-  credential: VerifiableCredential;
-  prop: string;
-}): void {
-  const value = credential[prop];
-  if (!dateRegex.test(value)) {
-    throw new Error(`"${prop}" must be a valid date: ${value}`);
-  }
-}
+function assertDateString({
+  credential,
+  prop,
+}: {
+  credential: VerifiableCredential;
+  prop: string;
+}): void {
+  const values = jsonld.getValues(credential, prop);
+  if (values.length < 1) {
+    throw new Error(`"${prop}" must be present when provided.`);
+  }
+  if (values.length > 1) {
+    throw new Error(`"${prop}" property can only have one value.`);
+  }
+  const value = values[0];
+  if (typeof value !== 'string' || !dateRegex.test(value)) {
+    throw new Error(`"${prop}" must be a valid date: ${value}`);
+  }
+}

260-273: Misleading error message for unsupported proof types

The thrown message mentions only BbsBlsSignature2020. If the mapping doesn’t include a proof type, the error should say “unsupported” to cover all cases.

Apply this diff:

-    if (!proofTypeMapping[proofType]) {
-      throw new Error('"proof" type is not of BbsBlsSignature2020.');
-    }
+    if (!proofTypeMapping[proofType]) {
+      throw new Error('"proof" type is unsupported.');
+    }

Note: Update any tests asserting on the old message.

🧹 Nitpick comments (5)
packages/w3c-context/package.json (1)

37-38: Manual verification needed for jsonld-signatures v11 compatibility

We’ve upgraded to "jsonld-signatures": "^11.5.0" in packages/w3c-context/package.json (lines 37–38), but the DI APIs (e.g., extendContextLoader) saw changes between 10.x and 11.x. The sandbox CI didn’t produce loader-related errors, so please verify locally on Node 18:

  • Run npm -w packages/w3c-context test and confirm extendContextLoader behaves as before.
  • Run npm -w packages/w3c-vc test and validate sign/verify/derive flows.

If you encounter regressions, pin the version to ~11.5.0 and revisit compatibility later.

packages/w3c-vc/README.md (1)

116-119: Nit: Mention that /issuer and the version-specific date field are auto-mandatory

Your implementation auto-includes /issuer and either /issuanceDate (v1.1) or /validFrom (v2) as mandatory pointers. Consider adding a short note so users don’t redundantly include them.

packages/w3c-vc/src/lib/w3c-vc.test.ts (3)

396-612: Good coverage for dual-version ECDSA-SD-2023; add a future-date negative test and adjust messages if helper changes

  • Consider adding a test for v2 where validFrom is in the future to assert the before-date error is thrown.
  • If you accept the helper change to a generic unsupported proof error, update the BBS+ unsupported-suite test expectation accordingly.

Here’s a test you can append (outside this selected range):

it('should fail verification when validFrom is in the future (v2.0)', async () => {
  const future = new Date(Date.now() + 60_000).toISOString();
  const testCredential = { ...ecdsaCredentialV2_0, validFrom: future };
  const signed = await signCredential(testCredential, ecdsaKeyPair, 'ecdsa-sd-2023');
  expect(signed.error).toBeUndefined();
  const res = await verifyCredential(signed.signed as VerifiableCredential);
  expect(res.verified).toBe(false);
  expect(res.error).toMatch(/before the "validFrom"/);
});

And, if you adopt the generic error string in helper:

- expect(verificationResult.error).equals('"proof" type is not of BbsBlsSignature2020.');
+ expect(verificationResult.error).equals('"proof" type is unsupported.');

396-612: Potential secret leakage flagged by Gitleaks – confirm keys are test-only and consider allowlisting

Static analysis flagged a generic API key. The ECDSA test key at Line 106–107 appears to be a test-only secret. Please confirm it is non-prod. To silence false positives safely, add a gitleaks allowlist for this test path.

Add a .gitleaks.toml (or update it) like:

title = "gitleaks config"

[extend]
# keep defaults

[allowlist]
paths = [
  '''packages/w3c-vc/src/lib/w3c-vc.test.ts''',
]
# or allow specific test-only patterns:
regexes = [
  '''z42tmUXTVn3n9BihE6NhdMpvVBTnFTgmb6fw18o5Ud6puhRW''',
]

If this key is not test-only, rotate immediately and purge history.

I can open a PR adding the allowlist or swap in ephemeral test keys loaded from env if preferred.


109-170: Type safety: replace pervasive any in test fixtures with concrete types

Using any for credential fixtures and reveal documents loses compile-time checks, especially after the v2 schema split. Prefer RawVerifiableCredential/ContextDocument/key-pair types from your lib to catch shape regressions earlier.

Example:

-const modifiedCredential: any = {
+import type { RawVerifiableCredential, ContextDocument } from './types';
+const modifiedCredential: RawVerifiableCredential = {
...
-const revealDocument: any = {
+const revealDocument: ContextDocument = {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b63352f and ed7007d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • packages/w3c-context/package.json (1 hunks)
  • packages/w3c-context/src/lib/index.ts (2 hunks)
  • packages/w3c-vc/README.md (3 hunks)
  • packages/w3c-vc/src/lib/helper/index.ts (2 hunks)
  • packages/w3c-vc/src/lib/w3c-vc.test.ts (3 hunks)
  • packages/w3c-vc/src/lib/w3c-vc.ts (5 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/w3c-vc/src/lib/w3c-vc.ts (1)
packages/w3c-context/src/lib/index.ts (1)
  • CredentialContextVersion (83-86)
packages/w3c-vc/src/lib/helper/index.ts (1)
packages/w3c-context/src/lib/index.ts (1)
  • CredentialContextVersion (83-86)
packages/w3c-vc/src/lib/w3c-vc.test.ts (2)
packages/w3c-vc/src/lib/types.ts (1)
  • VerifiableCredential (40-40)
packages/w3c-vc/src/lib/w3c-vc.ts (3)
  • signCredential (79-176)
  • deriveCredential (290-395)
  • verifyCredential (184-281)
🪛 Gitleaks (8.27.2)
packages/w3c-vc/src/lib/w3c-vc.test.ts

193-193: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (8)
packages/w3c-vc/README.md (3)

101-112: LGTM: Switch to VC v2 context and v2 temporal fields in ECDSA-SD-2023 example

Using https://www.w3.org/ns/credentials/v2 and validFrom/validUntil aligns with VC DM v2. The example looks correct.


196-207: LGTM: Verification example reflects VC v2 fields

The context and validFrom/validUntil fields align with the VC v2 example. No issues spotted.


321-332: LGTM: Derivation example is consistent with VC v2

The v2 context and date fields are consistent. The selective disclosure pointer pathing is correct.

packages/w3c-context/src/lib/index.ts (1)

3-3: Import change to jsonld-signatures default is fine, but double-check ESM/CJS interop

jsonld-signatures typically provides a default export; with tsup + Node 18 this should be fine. Ensure tsconfig has esModuleInterop or the bundler config aligns; otherwise switch to import * as jsonldSignatures.

packages/w3c-vc/src/lib/w3c-vc.ts (4)

11-16: LGTM! Import changes align with W3C VC v2 support.

The addition of CredentialContextVersion import enables proper version detection for handling both v1.1 and v2 credentials.


114-137: Well-implemented version-aware mandatory pointers logic.

The implementation correctly:

  • Detects the credential version from the context
  • Applies appropriate date field requirements (v2: validFrom when present, v1.1: issuanceDate)
  • Ensures issuer is always mandatory
  • Properly merges user-provided mandatory pointers while avoiding duplicates

352-363: Smart handling of credentialSubject in selective disclosure.

The logic correctly ensures that when no specific credentialSubject properties are selected, the entire credentialSubject is included to maintain credential validity per W3C VC specification. This prevents accidentally creating invalid credentials during selective disclosure.


124-126: validFrom Date Format Validation Already Present

The existing _checkCredential logic in packages/w3c-vc/src/lib/helper/index.ts already calls assertDateString({ credential, prop: 'validFrom' }) for v2 credentials and enforces a single value via jsonld.getValues. This covers both format correctness (per the XML Schema dateTime regex) and cardinality, so no additional validation is required.

Likely an incorrect or invalid review comment.

Comment on lines +184 to 185
return jsonldSignatures.extendContextLoader(customDocLoader);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Harden the loader: consider an allowlist and scheme checks before remote fetch

While extendContextLoader wraps the custom loader, you’re still allowing arbitrary remote fetches when a context/URL isn’t in cache. To mitigate SSRF/LDoS risks:

  • Restrict to https URLs and a known allowlist (w3id.org, w3.org, your own domains).
  • Optionally cap payload size and add a timeout.

Here’s a sketch (apply outside the selected range) to harden the loader in customDocLoader:

// Add near the top
const ALLOWED_HOSTS = new Set([
  'www.w3.org',
  'w3id.org',
  'trustvc.io',
  'trustvc.github.io',
]);

// Inside customDocLoader before fetch():
const parsed = new URL(url);
if (parsed.protocol !== 'https:') {
  throw new Error(`Unsupported scheme for document loading: ${parsed.protocol}`);
}
if (!ALLOWED_HOSTS.has(parsed.hostname)) {
  throw new Error(`Refusing to load remote document from unapproved host: ${parsed.hostname}`);
}
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), 5000);
const results = await fetch(url, { redirect: 'follow', signal: controller.signal });
clearTimeout(id);

Comment thread packages/w3c-vc/src/lib/helper/index.ts Outdated
Comment thread packages/w3c-vc/src/lib/helper/index.ts
Comment thread packages/w3c-vc/src/lib/w3c-vc.ts
Comment thread packages/w3c-vc/src/lib/w3c-vc.ts
@rongquan1 rongquan1 requested a review from nghaninn August 12, 2025 03:04
@nghaninn nghaninn merged commit e4219ee into alpha Aug 13, 2025
11 checks passed
@nghaninn nghaninn deleted the feat/add-data-model-v2-support branch August 13, 2025 02:37
nghaninn pushed a commit that referenced this pull request Aug 13, 2025
@nghaninn
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.3.0-alpha.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

@nghaninn
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.3.0-alpha.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

@nghaninn
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.3.0-alpha.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Aug 13, 2025
@nghaninn
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.3.0-alpha.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Aug 13, 2025
@nghaninn
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.3.0-alpha.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

nghaninn pushed a commit that referenced this pull request Aug 13, 2025
@nghaninn
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.3.0-alpha.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants