Skip to content

fix(auth): return specific status codes from wallet-session, not opaque 500#325

Open
QSchlegel wants to merge 1 commit into
preprodfrom
fix/wallet-session-error-handling
Open

fix(auth): return specific status codes from wallet-session, not opaque 500#325
QSchlegel wants to merge 1 commit into
preprodfrom
fix/wallet-session-error-handling

Conversation

@QSchlegel

Copy link
Copy Markdown
Collaborator

Problem

/api/auth/wallet-session wrapped its whole body in one try/catch that returned a generic 500 Internal Server Error for any thrown error. But checkSignature() from @meshsdk/core-cst throws (rather than returning false) for several inputs — most notably a non-bech32 / hex-encoded address, where it internally calls Address.fromBech32(address) and raises Unknown letter "b". Allowed: qpzry9x8gf2tvdw0s3jn54khce6mua7l, as well as malformed COSE_Sign1 / COSE_Key bytes.

Every such case surfaced as an opaque 500, so the client (the WalletAuthModal toast) only ever showed a generic failure — which made a recent login bug much harder to diagnose.

Fix

  • Normalize the address to bech32 up front (normalizeAddressToBech32) before the nonce lookup and signature check — defense in depth; the client already normalizes, but other callers may not.
  • Wrap checkSignature in its own try/catch: a throw is logged via console.warn (so genuine failures stay diagnosable) and treated as 401 { error: "Invalid signature" }, never a 500.
  • Clear status-code contract: 400 for missing/malformed input and no-nonce, 401 for an invalid (or throwing) signature, 200 {ok:true} for success — and 500 is now reserved for genuine server faults (e.g. DB errors).

Tests

Adds src/__tests__/walletSessionApi.test.ts (6 cases, all passing) covering 400 / 401 / 200 / 500 paths — including the regression: a throwing checkSignature now yields 401, not 500, and the nonce is not consumed.

tsc --noEmit: changed files clean, no new type errors.

Context: split out of the login debugging that produced #324. A valid signature still authenticates (verified end-to-end against production); this only changes how failures are reported.

🤖 Generated with Claude Code

…ue 500

The handler wrapped everything in one try/catch that returned 500 for ANY thrown
error. checkSignature() from @meshsdk/core-cst THROWS (not just returns false) on
malformed COSE or a non-bech32/hex address — e.g. Address.fromBech32() on hex
raises `Unknown letter "b"`. Every such case surfaced as an opaque 500, hiding
the real cause from the client toast and making login bugs hard to diagnose.

- Normalize the address to bech32 up front (defense in depth) before the nonce
  lookup and signature check.
- Wrap checkSignature in its own try/catch: a throw is logged (console.warn) and
  treated as 401 Invalid signature, never a 500.
- Keep 400 for missing/malformed input and no-nonce, 401 for invalid signature;
  500 is now reserved for genuine server faults (e.g. DB errors).

Adds src/__tests__/walletSessionApi.test.ts covering the 400/401/200/500 paths,
including the regression that a throwing checkSignature now yields 401, not 500.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
multisig Ready Ready Preview, Comment Jun 26, 2026 11:44am

Request Review

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