Repository navigation
fix(auth): require a live session for bearer tokens - #2041
Open
liutingqiu wants to merge 1 commit into
Open
liutingqiu wants to merge 1 commit into
liutingqiu wants to merge 1 commit into
Conversation
requireWalletAuth fell back to decoding an address out of a session_<address>_<digits> token when no session existed. Tokens created by createSessionToken contain a single underscore, so that pattern can only ever match forged input, and it let any caller authenticate as any wallet on the preferences and notifications routes. Drop the fallback so the address is only taken from a live server-side session, and mint real tokens in the two test suites that were authenticating through the removed branch.
|
@liutingqiu is attempting to deploy a commit to the 1nonly's projects Team on Vercel. A member of the Team first needs to authorize it. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Follows #1926.
requireWalletAuthinsrc/lib/backend/preferences.tsis the bearer guard used by the user-preferences routes and the notifications routes. After the real session lookup it had a format-based fallback:That branch returns an address without any session existing. Real tokens are
session_<16 random bytes as hex>—createSessionTokenbuilds them as`session_${randomBytes(16).toString('hex')}`, which contains one underscore. The regex requires two, so it cannot match a genuine token: the only input that ever reachesreturn match[1]is a forged string of the formsession_<anything>_<digits>. Anyone could therefore sendBearer session_<victim address>_1and be authenticated as that wallet, on both the preferences and notifications endpoints.What changed
src/lib/backend/preferences.ts— removed the fallback; the address now comes only from a record returned byverifySessionToken, so a token that does not match a live session is a 401. The stale doc comment (which described the placeholder format as the supported one) and theTODO: Replace with proper JWT verificationare updated to describe what the function now actually does. The malformed-header checks are unchanged.src/app/api/user/preferences/route.test.ts— the suite authenticated with hard-codedsession_<address>_<timestamp>strings, i.e. it was passing because of the vulnerability. Tokens are now minted per test withcreateSessionTokenafter_clearStores()inbeforeEach. All existing assertions kept their meaning (per-wallet isolation, 401 cases, ETag, idempotency, concurrency).src/app/api/notifications/__tests__/route.test.ts— same fake-token pattern, same fix. That route shares this guard, so the change is not scoped to preferences alone.session_<address>_<digits>token is rejected; and a bare address is rejected.I proved the new test catches the bug rather than assuming it: with the old fallback temporarily restored,
requireWalletAuth rejects a forged token that encodes a wallet addressfails (1 failed / 29 passed); with the fix it passes (30/30).How to verify
Local results on this branch (Windows,
node v24.21.0— not the pinned Node 20 from.nvmrc, so CI on Node 20 is the authoritative run):npx prettier --checkandnpx eslinton all changed files: clean.npx tsc --noEmit: 58 errors, identical to unmodifiedmasterin this environment, none in the changed files. The job iscontinue-on-error: truewhile the pre-existing backlog is burned down (seedocs/CI.md).npm run test:coverage: the failing-test set is byte-for-byte identical tomaster's (192 failing items, no additions, no removals), and neither changed test file fails. The suite also gains exactly the 1 net new test (3 added, 2 replaced).Behaviour note for reviewers
This is a breaking change for any client that was relying on the placeholder format. It is not a real client path: that format was only ever produced by tests, and
verifySessionTokenis the sole legitimate way a token becomes valid. TheBearercontract and all success paths viacreateSessionTokenare unchanged.Closes #1926