Skip to content

JWT session token silently reverted from HttpOnly cookies back to localStorage #403

Description

@Kingvic300

Summary

A previously merged security fix — moving the JWT session token out of localStorage into HttpOnly cookies (PR #235, closing High-severity issue #11) — was silently reverted by an unrelated merge-conflict "repair main" commit one day later. The raw JWT has been readable from localStorage on every commit since, and issue #11 still reads as closed/fixed.

Background

PR #235 (merged, closing issue #11 "[High] JWT stored in localStorage and leaked in OAuth redirect URL") replaced ApiService.setToken with a no-op and fully removed localStorage token handling. Commit ae2453d8 ("fix: repair main after bad merge (99e3e92) broke the Rust build and CI", PR #236) — while fixing an unrelated CI/build break the next day — clobbered api.ts back to its pre-#235 state, silently reintroducing full localStorage read/write of the raw JWT. No new issue was ever filed for the regression, and #11 remains closed as if the fix still stands.

Evidence

  • src/services/api.ts:511-518ApiService.token initializer reads directly from localStorage.getItem('txio_token').
  • src/services/api.ts:520-537setToken() writes/removes localStorage['txio_token'] on every call.
  • src/services/api.ts:688, :715 — both login() and register() (the primary email/password path, not just OAuth) call this.setToken(data.token).
  • src/features/SignInPage.tsx:62,74,128,130,144,146 — reads/writes localStorage for 'txio_token' directly, and the OAuth callback handling (SignInPage.tsx:19-77) feeds the fragment token straight into localStorage.
  • git blame -L 505,536 src/services/api.ts attributes all of it to commit ae2453d8; gh pr diff 235 confirms the pre-ae2453d8 state had setToken as a no-op with all localStorage calls removed.
  • git merge-base --is-ancestor ae2453d8 HEAD succeeds — this is on current main, not an abandoned branch.

Impact: every logged-in user's session token is readable by any script running in the page context — a future XSS, a malicious browser extension, or a compromised dependency in the large multi-chain wallet dependency tree. This is exactly the "High" severity scenario the project already scored and fixed once. Anyone auditing via GitHub issue state alone would conclude this is resolved, since #11's closing comment says "Fixed in commit(s) on branch test-191."

Proposed Solution

Re-apply the intent of PR #235: remove localStorage token storage entirely, restore ApiService.setToken to a no-op (or align with whatever the backend currently issues — confirm the backend still sets the HttpOnly cookie post-#236 revert, since the same "repair main" commit may have touched backend code too). Update SignInPage.tsx's OAuth/login/logout paths accordingly. Add a lightweight regression check — a grep-based CI lint or a unit test asserting ApiService never touches localStorage for the token key — so a future merge-conflict resolution can't silently reintroduce this again.

Technical Scope

  • src/services/api.tsApiService.token, setToken, login, register
  • src/features/SignInPage.tsx — OAuth callback handling, login/logout paths
  • Backend verification (cross-repo: confirm Txio-labs/txio-backend still issues the HttpOnly cookie as PR Fix 13 unassigned issues: security, CI/CD, and UI improvements #235 expected)
  • New regression test/CI check

Acceptance Criteria

  • No code path reads or writes the JWT via localStorage under the txio_token key
  • Login, register, Google OAuth login, and logout all function correctly against cookie-based auth
  • A test or CI check fails if localStorage + token-key usage is reintroduced in api.ts or SignInPage.tsx
  • Issue [High] JWT stored in localStorage and leaked in OAuth redirect URL #11 is reopened or commented on to reflect that the original fix was reverted, independent of this new issue's resolution

Edge Cases

  • OAuth redirect flow specifically (the original vulnerability's exact vector — token appearing in a URL fragment)
  • Logout must clear the cookie server-side, not just stop reading a local value
  • Any other code path (besides api.ts/SignInPage.tsx) that might independently read txio_token from localStorage — worth a full-repo grep before closing

Risks

  • If the backend's HttpOnly cookie issuance was also affected by the same "repair main" commit, this fix needs to land alongside a backend verification/fix, not frontend-only
  • Regression risk is exactly what happened before (a future merge-conflict resolution reintroducing this) — the CI/test guard in the acceptance criteria exists specifically to prevent a third occurrence

Deliverables

Priority

Critical — a previously-fixed, maintainer-acknowledged High-severity session-theft vulnerability is currently live on main and invisible to anyone trusting GitHub issue state.


GrantFox Evaluation

Impact Score: 90/100
Difficulty Score: 25/100
Priority Score: 85.5
Confidence: 95%

Category: Security

Estimated Reward Tier: A

AI Rationale: A confirmed, git-blame-traced silent regression of a previously fixed High-severity vulnerability, currently live on main and masked by stale issue state. The fix is small and well-scoped — PR #235's diff is a direct reference for what "done" looks like — making this an unusually high-confidence, high-impact, low-difficulty finding.


Estimated Completion: 96 hours

Telegram: https://t.me/txioCommunity

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Third CampaignCampaign: Third CampaignbugSomething isn't workingpriority:criticalDrop everythingsecuritySecurity-relevant issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions