Skip to content

Fix SD-JWT decoder to handle delegation chains - #350

Closed
maxcabd wants to merge 1 commit into
google-agentic-commerce:mainfrom
maxcabd:fix/web-client-sdjwt-chain-decode
Closed

Fix SD-JWT decoder to handle delegation chains#350
maxcabd wants to merge 1 commit into
google-agentic-commerce:mainfrom
maxcabd:fix/web-client-sdjwt-chain-decode

Conversation

@maxcabd

@maxcabd maxcabd commented Sep 6, 2026

Copy link
Copy Markdown

A display bug in the sample UI.

sdJwtDecoder.ts splits on a single ~ before checking for the ~~ chain separator used by delegation chains. When a token is a real chain, which is common for Checkout and Payment mandates, this misreads the second hop as a trailing KB JWT and drops that hop's disclosures.

The fix splits on ~~ first and decodes each hop on its own. DecodedSdJwt now has an optional hops field with one entry per hop. The existing issuerJwt, disclosures, and kbJwt fields still describe hop 0, so MandateCard.tsx and any other caller keep working without changes. Confirmed with tsc --noEmit.

Added vitest, since this package didn't have a test runner, and a test using a real two hop chain from the Python SDK. The test fails on the old code and passes after the fix.

sdJwtDecoder.ts split on a single `~` before checking for the `~~`
chain separator, so a genuine delegation-chain token (the common shape
for real Checkout/Payment mandate flows) was silently mis-decoded: the
second hop's issuer-signed JWT was misread as a trailing KB-JWT, and
that hop's own disclosures were dropped or misattributed to hop 0.

decodeSdJwt/decodeSdJwtSync now split on `~~` first and decode each
hop independently. DecodedSdJwt gains an optional `hops` array (one
entry per hop, fully decoded); the existing top-level
issuerJwt/disclosures/kbJwt fields still describe hop 0, so every
existing single-hop caller (including MandateCard.tsx) is unaffected.

Added vitest (this package had no test runner) and a test using a
real 2-hop chain from the Python reference SDK. Verified the new test
fails against the pre-fix code and passes after the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfCaW4RbZQgTpcyXRau8os
@maxcabd
maxcabd requested a review from a team as a code owner September 6, 2026 17:54
@google-cla

google-cla Bot commented Sep 6, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@maxcabd maxcabd closed this Sep 6, 2026
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.

1 participant