Skip to content

feat: add Verifiable Presentation sign/verify + fragments - #154

Merged
RishabhS7 merged 5 commits into
mainfrom
feat/w3c-verifiable-presentations
Jul 31, 2026
Merged

feat: add Verifiable Presentation sign/verify + fragments#154
RishabhS7 merged 5 commits into
mainfrom
feat/w3c-verifiable-presentations

Conversation

@rongquan1

@rongquan1 rongquan1 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added W3C Verifiable Presentation signing and verification with enforced holder binding, full-disclosure, expiry checks, and v2 envelope output.
    • Added VP verification fragments for signature integrity, credential status, and issuer identity (including multi-credential wrapping).
  • Bug Fixes
    • Improved “empty credential status” detection to avoid misclassifying Verifiable Presentations.
  • Documentation
    • Added repo developer guidance (CLAUDE.md) and expanded README with VP function documentation.
  • Tests
    • Expanded VP end-to-end and fragment tests, including challenge anti-replay, tampering, unsigned/expired cases, and fragment outcomes.
  • Chores
    • Updated W3C dependency versions and refreshed RPC endpoints/snapshots in tests.

Consume @trustvc/w3c-vc 2.3.0 and expose an opinionated VP surface:

- signW3CPresentation / verifyW3CPresentation (src/w3c/presentation.ts) that
  ENFORCE trustvc policies: fullDisclosure (auto-derive underived credentials),
  holder binding (signer DID == holder == every credentialSubject.id), a mandatory
  lifetime, and a locked v2 envelope. TransferableRecords credentials are blocked.
- Three VP verification fragments (src/verify/fragments/presentation) wired into
  verifyDocument(): W3CVpSignatureIntegrity (requires a holder proof + enforces
  holder binding), W3CVpCredentialStatus (VP expiry + embedded revocation),
  W3CVpIssuerIdentity (embedded issuers resolve).
- Bump @trustvc/w3c* 2.2.0 -> 2.3.0.
- Tests (28): create/sign/verify, invalid-key matrix, TransferableRecords block,
  underived auto-disclosure, missing subject id, different (did:web) issuer,
  v1.1-in-v2 envelope, revoked/not-revoked status, full verifyDocument() pipeline.
- CLAUDE.md: repo guidance, VP policies, gotchas, and a self-maintenance rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fd19899-106a-44e2-b927-3a4df74aa6ad

📥 Commits

Reviewing files that changed from the base of the PR and between 47baf6f and c995f05.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • README.md
  • package.json
  • src/verify/fragments/document-status/w3cEmptyCredentialStatus/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • package.json
  • src/verify/fragments/document-status/w3cEmptyCredentialStatus/index.ts
  • README.md

📝 Walkthrough

Walkthrough

Adds public W3C VP signing and verification wrappers with enforced policies, aggregate VP verification fragments, routing integration, comprehensive tests, dependency updates, repository guidance, and updated Amoy test endpoints.

Changes

W3C Verifiable Presentation support

Layer / File(s) Summary
VP wrapper contracts and execution
src/w3c/presentation.ts, src/w3c/index.ts, src/__tests__/w3c/presentation.test.ts
Adds enforced v2 VP signing, lifetime validation, full disclosure, holder binding, verification options, key validation, expiry handling, and versioning coverage.
VP verification fragment implementations
src/verify/fragments/presentation/w3cVpVerifier.ts, src/__tests__/w3c/vpFragments.test.ts
Adds VP signature integrity, credential status, issuer identity, DID resolution, holder binding, expiry, revocation, tampering, and integration verification.
Fragment exports and verification routing
src/verify/fragments/index.ts, src/verify/verify.ts, src/verify/fragments/document-status/..., src/__tests__/core/verify.test.ts
Exports and registers VP fragments, prevents VC-only status handling for VP payloads, and updates non-VP snapshots.
Dependency, documentation, and test alignment
package.json, CLAUDE.md, README.md, src/__tests__/core/*
Updates W3C packages and Amoy RPC endpoints, documents VP and development conventions, and adjusts supporting assertions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant signW3CPresentation
  participant createPresentation
  participant signPresentation
  participant verifyW3CPresentation
  participant verifyPresentation
  Caller->>signW3CPresentation: credentials, keyPair, lifetime
  signW3CPresentation->>createPresentation: enforced VP policies
  createPresentation-->>signW3CPresentation: presentation
  signW3CPresentation->>signPresentation: presentation and keyPair
  signPresentation-->>Caller: signed VP
  Caller->>verifyW3CPresentation: signed VP and options
  verifyW3CPresentation->>verifyPresentation: holder-binding options
  verifyPresentation-->>Caller: verification result
Loading

Possibly related PRs

Suggested labels: released

Suggested reviewers: kmk142789

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided, so the required Summary, Changes, and Issues sections are missing. Add the template sections: Summary, Changes, and Issues, with a brief background, list of changes, and related issue links.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding Verifiable Presentation signing, verification, and fragments.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/w3c-verifiable-presentations

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (6)
src/__tests__/w3c/presentation.test.ts (2)

11-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting assertDefined into a shared test helper.

The same helper is likely needed by the other new VP suite (src/__tests__/w3c/vpFragments.test.ts, which currently uses !), so a shared util avoids per-file duplication.

As per coding guidelines, "Do not use ! non-null assertions in tests; use the assertDefined helper instead."

🤖 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/__tests__/w3c/presentation.test.ts` around lines 11 - 15, Extract
assertDefined into a shared test utility, then import and reuse it in
presentation.test.ts and vpFragments.test.ts. Replace the non-null assertions in
vpFragments.test.ts with assertDefined calls while preserving the existing test
behavior.

Source: Coding guidelines


296-327: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

This test depends on live resolution of did:web:trustvc.github.io:did:1.

Network-dependent assertions make CI flaky when the hosted DID document is unreachable or rotated. Consider a stubbed document loader for the issuer DID while keeping one integration-tagged test for the live path.

🤖 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/__tests__/w3c/presentation.test.ts` around lines 296 - 327, Update the
test around signW3C and verifyW3CPresentation to use a stubbed document loader
or equivalent fixture for ISSUER_DID, so its credential verification does not
depend on live did:web resolution. Preserve the existing issuer/holder
distinction and add or retain a separately integration-tagged test for
validating the live resolution path.
src/__tests__/w3c/vpFragments.test.ts (1)

161-176: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

verifyDocument(signed) is called without rpcProviderUrl/provider.

Per src/core/verify.ts (lines 51-81) the builder constructs a default JsonRpcProvider, which can add slow/failing RPC calls in CI even though only VP fragments are asserted. Consider passing an explicit provider or stub.

🤖 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/__tests__/w3c/vpFragments.test.ts` around lines 161 - 176, The full
verifyDocument pipeline test invokes verifyDocument without an explicit RPC
configuration, allowing the default JsonRpcProvider to make unnecessary CI
calls. Update the test around verifyDocument(signed) to pass a deterministic
explicit provider or rpcProviderUrl stub, while preserving the existing VP
fragment assertions.
src/verify/fragments/presentation/w3cVpVerifier.ts (1)

205-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the nested ternary for readability.

♻️ Proposed extraction
-        const raw = cred.credentialStatus;
-        const statuses = (Array.isArray(raw) ? raw : raw ? [raw] : []) as CredentialStatus[];
+        const raw = cred.credentialStatus;
+        if (!raw) return [];
+        const statuses = (Array.isArray(raw) ? raw : [raw]) as CredentialStatus[];
🤖 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/verify/fragments/presentation/w3cVpVerifier.ts` at line 205, Refactor the
statuses assignment to remove the nested ternary: first normalize raw into an
array using clear conditional logic, preserving the existing handling for
arrays, single truthy values, and absent values, then cast the result to
CredentialStatus[].

Source: Linters/SAST tools

src/w3c/presentation.ts (1)

71-78: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider wrapping signPresentation in the same try/catch as createPresentation.

Create errors are normalized to { error }, but an unexpected throw from signPresentation propagates as a rejection, so the documented result contract isn't uniform.

♻️ Proposed normalization
-  return signPresentation(presentation, keyPair, enforced);
+  try {
+    return await signPresentation(presentation, keyPair, enforced);
+  } catch (err: unknown) {
+    return { error: err instanceof Error ? err.message : 'Failed to sign the presentation.' };
+  }
🤖 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/w3c/presentation.ts` around lines 71 - 78, Update the presentation
creation and signing flow around createPresentation and signPresentation so both
operations are covered by the same try/catch. Normalize any signing exception to
the existing { error } result shape, preserving Error.message when available and
the current fallback message for non-Error throws.
src/verify/verify.ts (1)

39-43: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

VP fragments are added to w3cVerifiers but not to the exported verifiers map.

verifiers.documentIntegrity/documentStatus/issuerIdentity (lines 47-63) lists the VC-level w3c fragments; consumers building custom pipelines from that map won't see the VP ones. Confirm the omission is intentional.

Also applies to: 76-79

🤖 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/verify/verify.ts` around lines 39 - 43, Update the exported verifiers map
in verify.ts to include w3cVpCredentialStatus, w3cVpIssuerIdentity, and
w3cVpSignatureIntegrity alongside the existing VC-level fragments. Ensure
consumers using verifiers can access the same VP fragments already registered in
w3cVerifiers, without removing the current entries.
🤖 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/__tests__/w3c/vpFragments.test.ts`:
- Around line 44-48: Replace the non-null assertions on signCredential and
deriveCredential results with assertDefined calls in both affected sites:
src/__tests__/w3c/vpFragments.test.ts lines 44-48 and 111-113. Apply this to
s.signed and the derived credential value while preserving the existing test
flow.

In `@src/verify/fragments/presentation/w3cVpVerifier.ts`:
- Around line 200-216: Update the embedded-credential status processing around
getCredentials and statusChecks so any credentialStatus entry with an
unsupported type is surfaced as an ERROR/INVALID verification result rather than
filtered out. Preserve verification through verifyCredentialStatus for
BitstringStatusListEntry and StatusList2021Entry, and ensure every embedded
credential’s status is accounted for so unsupported types cannot yield VALID
with checked: 0.
- Around line 42-65: Update checkVpHolderBinding to inspect every
credentialSubject rather than only the first returned by getFirstSubject. For
array-valued subjects, iterate each subject, require a credentialSubject.id, and
enforce exact equality with the holder/signer DID; preserve the existing
behavior for single-object subjects and report the corresponding credential
index when validation fails.
- Around line 267-277: Update the issuer validation around getCredentials and
the issuers mapping so every embedded credential must have a resolvable issuer.
Detect any credential whose issuer is missing or cannot be read and return the
existing INVALID ISSUER_IDENTITY result, while preserving the current successful
path only when all credentials resolve.

---

Nitpick comments:
In `@src/__tests__/w3c/presentation.test.ts`:
- Around line 11-15: Extract assertDefined into a shared test utility, then
import and reuse it in presentation.test.ts and vpFragments.test.ts. Replace the
non-null assertions in vpFragments.test.ts with assertDefined calls while
preserving the existing test behavior.
- Around line 296-327: Update the test around signW3C and verifyW3CPresentation
to use a stubbed document loader or equivalent fixture for ISSUER_DID, so its
credential verification does not depend on live did:web resolution. Preserve the
existing issuer/holder distinction and add or retain a separately
integration-tagged test for validating the live resolution path.

In `@src/__tests__/w3c/vpFragments.test.ts`:
- Around line 161-176: The full verifyDocument pipeline test invokes
verifyDocument without an explicit RPC configuration, allowing the default
JsonRpcProvider to make unnecessary CI calls. Update the test around
verifyDocument(signed) to pass a deterministic explicit provider or
rpcProviderUrl stub, while preserving the existing VP fragment assertions.

In `@src/verify/fragments/presentation/w3cVpVerifier.ts`:
- Line 205: Refactor the statuses assignment to remove the nested ternary: first
normalize raw into an array using clear conditional logic, preserving the
existing handling for arrays, single truthy values, and absent values, then cast
the result to CredentialStatus[].

In `@src/verify/verify.ts`:
- Around line 39-43: Update the exported verifiers map in verify.ts to include
w3cVpCredentialStatus, w3cVpIssuerIdentity, and w3cVpSignatureIntegrity
alongside the existing VC-level fragments. Ensure consumers using verifiers can
access the same VP fragments already registered in w3cVerifiers, without
removing the current entries.

In `@src/w3c/presentation.ts`:
- Around line 71-78: Update the presentation creation and signing flow around
createPresentation and signPresentation so both operations are covered by the
same try/catch. Normalize any signing exception to the existing { error } result
shape, preserving Error.message when available and the current fallback message
for non-Error throws.
🪄 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 Plus

Run ID: 759ff572-e886-4ca4-8a3f-28c413d01dd6

📥 Commits

Reviewing files that changed from the base of the PR and between 7598cd1 and ebdc8ff.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • CLAUDE.md
  • package.json
  • src/__tests__/core/verify.pol.test.ts
  • src/__tests__/w3c/presentation.test.ts
  • src/__tests__/w3c/vpFragments.test.ts
  • src/verify/fragments/index.ts
  • src/verify/fragments/presentation/w3cVpVerifier.ts
  • src/verify/verify.ts
  • src/w3c/index.ts
  • src/w3c/presentation.ts

Comment thread src/__tests__/w3c/vpFragments.test.ts
Comment thread src/verify/fragments/presentation/w3cVpVerifier.ts Outdated
Comment thread src/verify/fragments/presentation/w3cVpVerifier.ts
Comment thread src/verify/fragments/presentation/w3cVpVerifier.ts
rongquan1 and others added 3 commits July 29, 2026 10:45
Review fixes to the VP support:
- w3cVpSignatureIntegrity: holder binding now checks EVERY credentialSubject of
  every credential (was only the first).
- w3cVpCredentialStatus: surface unsupported credentialStatus types as ERROR
  instead of silently dropping them (revocation must not be silently unenforced).
- w3cVpIssuerIdentity: a credential with no issuer now fails (INVALID) instead of
  being dropped by filter(Boolean).
- fragments/index.ts: re-export the VP verifiers via `export … from` (Sonar S7763).
- Extract a toArray helper (removes a nested ternary, Sonar S3358).
- Tests: use assertDefined instead of `!`; +2 tests (unsupported-status ERROR,
  missing-issuer INVALID). Regenerate core/verify.test snapshots for the 3 VP
  fragments now emitted (SKIPPED) on non-VP documents.

Polygon public RPC deprecation:
- Replace https://rpc-amoy.polygon.technology with
  https://polygon-amoy-bor-rpc.publicnode.com in the affected tests.

Docs:
- README: add a "Verifiable Presentations (VP)" section and renumber the rest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rifier

A VP has no top-level credentialStatus, so this VC-only verifier's test() matched
it and then reported INVALID ("not a valid SignedVerifiableCredential") — marking
every VP (even a valid one) as INVALID for DOCUMENT_STATUS. Exclude presentations
in test() so it SKIPs them; VP status is handled by W3CVpCredentialStatus.

Strengthen the full-pipeline test to assert a valid VP yields only VALID/SKIPPED
fragments (and W3CEmptyCredentialStatus is SKIPPED).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
w3c, w3c-vc, w3c-context and w3c-credential-status to ^2.4.0. w3c-issuer stays
at ^2.3.0 (no 2.4.0 was published — latest is 2.3.0). Type-check and VP tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/verify/fragments/document-status/w3cEmptyCredentialStatus/index.ts`:
- Around line 22-30: Update the Verifiable Presentation guard in the
document-status verifier to first validate that the input is a non-null object
before using the `in` operator or accessing its properties. Ensure primitive
`document` values skip this VP-specific check without throwing, while preserving
the existing `types` and `verifiableCredential` detection for object inputs.
- Around line 29-30: Update the bypass condition in the document-status
verification logic to require both a type containing “VerifiablePresentation”
and the presence of “verifiableCredential”, matching the complete VP shape used
by isVpDocument(). Keep proof validation out of this routing check so only
documents satisfying both fields bypass the VC fragment.
🪄 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 Plus

Run ID: 3fae2b60-0e28-40d8-8ec9-60c9ca7a3faa

📥 Commits

Reviewing files that changed from the base of the PR and between 060f59f and 47baf6f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json
  • src/__tests__/w3c/vpFragments.test.ts
  • src/verify/fragments/document-status/w3cEmptyCredentialStatus/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.json
  • src/tests/w3c/vpFragments.test.ts

Comment thread src/verify/fragments/document-status/w3cEmptyCredentialStatus/index.ts Outdated
Comment thread src/verify/fragments/document-status/w3cEmptyCredentialStatus/index.ts Outdated
…ations

- Bump @trustvc/w3c and @trustvc/w3c-vc to ^2.4.1, which decouples credentialStatus
  field-format validation from _checkCredential's verify path. A malformed
  tokenNetwork.chainId now surfaces as a DOCUMENT_STATUS problem instead of a
  signature-integrity failure, so the TransferableRecords verifier reports it and
  the pol "chainId is missing" test passes without any trustvc workaround.
- Harden the empty-credential-status VP guard (CodeRabbit): guard non-object input
  before the `in` operator, and require the full VP shape (type includes
  VerifiablePresentation AND has verifiableCredential) to skip, matching isVpDocument.
- README: clarify that challenge/domain are OPTIONAL when signing a VP (a plain
  assertionMethod proof needs only a lifetime); add a minimal no-challenge example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@rongquan1
rongquan1 requested a review from RishabhS7 July 29, 2026 09:16
@RishabhS7
RishabhS7 merged commit d8d51f1 into main Jul 31, 2026
21 checks passed
@RishabhS7
RishabhS7 deleted the feat/w3c-verifiable-presentations branch July 31, 2026 10:02
nghaninn pushed a commit that referenced this pull request Jul 31, 2026
## [2.15.0](v2.14.1...v2.15.0) (2026-07-31)

### Features

* add Verifiable Presentation sign/verify + fragments ([#154](#154)) ([d8d51f1](d8d51f1))
@tradetrustimda

Copy link
Copy Markdown

🎉 This PR is included in version 2.15.0 🎉

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants