Skip to content

fix(didresolver): wrap resolved verifier as requested DID#33

Merged
frrist merged 1 commit into
mainfrom
fix/didresolver-wrap-verifier
May 27, 2026
Merged

fix(didresolver): wrap resolved verifier as requested DID#33
frrist merged 1 commit into
mainfrom
fix/didresolver-wrap-verifier

Conversation

@frrist
Copy link
Copy Markdown
Member

@frrist frrist commented May 26, 2026

Summary

ucantone/ucan/token/token.go VerifySignature performs an equality check between the token's issuer DID and the verifier's DID before examining signature bytes:

if tok.Issuer() != verifier.DID() {
    return false, nil
}

When a token issued by did:web:foo is verified using a verifier returned by HTTPResolver or MapResolver, the resolver was previously handing back an unwrapped did:key: verifier. The equality check failed, VerifySignature returned false, and the validator surfaced an InvalidSignature error — even though the signature bytes were perfectly valid.

This PR wraps the resolved did:key: verifier so its DID() returns the originally-requested DID (the did:web: the caller asked for). did:key: inputs to MapResolver remain unwrapped, since they already self-describe.

Origin

The bug has been present since the resolvers were ported in #17. That PR's description called it out as a known follow-up:

It has a soft dependency on fil-forge/ucantone#7 in that you'll need to wrap the resolvers with something to make them look like what is required by ucantone right now.

This PR is that follow-up. The wrapping happens at the resolver boundary (rather than inside ucantone), so it lands here in libforge.

How this was found

While debugging a UCAN admin invocation in smelt's local dev stack: sprue's provider register self-invocation kept failing InvalidSignature despite client and server loading the same key file and the published did.json publishing the matching pub key. Tracing through ucantone's validator showed the early-return on the DID mismatch, and the resolvers were the only realistic spot where that mismatch could be introduced.

Test plan

  • go test ./didresolver/... passes locally (updated assertions reflect new behavior — resolved verifier announces requested DID; underlying did:key reachable via Unwrap())
  • End-to-end: sprue admin invocation succeeds after this change (verified against a local smelt stack)
  • Reviewer to spot-check that no callers depend on resolver.Resolve(...).DID() returning the underlying did:key: — call sites in sprue/indexing-service/delegator use the verifier only to verify signatures, so the change is safe for them; other consumers should be audited.

🤖 Generated with Claude Code

`token.VerifySignature` in `ucantone/ucan/token/token.go` compares
`tok.Issuer()` against `verifier.DID()` before checking signature bytes.
HTTPResolver and MapResolver previously returned an unwrapped did:key
verifier when asked to resolve a did:web — so the equality check failed
and signatures were rejected with `InvalidSignature` before they were
ever examined.

Wrap the underlying did:key verifier so its DID() matches the originally
requested DID. did:key inputs to MapResolver remain unwrapped (the keys
are already self-describing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alanshaw
Copy link
Copy Markdown
Member

👏 OMG good debugging

@frrist frrist merged commit 4361ce6 into main May 27, 2026
5 checks passed
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