Skip to content

fix(auth): clear a stale/expired JWT on 401 and skip /users/me without a token (#42) - #200

Merged
presidojay1 merged 2 commits into
StellarTickets:mainfrom
miraclesonly:fix/42-clear-stale-jwt
Sep 26, 2026
Merged

presidojay1 merged 2 commits into
StellarTickets:mainfrom
miraclesonly:fix/42-clear-stale-jwt

Conversation

@miraclesonly

@miraclesonly miraclesonly commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

refresh() in src/lib/auth-context.tsx caught every /users/me failure and only called setUser(null). An expired or invalid JWT stayed in localStorage, so:

  • every later request sent a dead Authorization header;
  • every page load repeated the failing /users/me call.

Changes

  1. Clear the token on 401.
    • New isUnauthorized(err) helper in src/lib/api.ts: err instanceof ApiError && err.status === 401.
    • When it's true, refresh() calls clearToken() before setUser(null).
    • Only a 401 clears the token. 403, 5xx and network or timeout failures (NetworkError, status 0) keep it, so a backend blip doesn't sign the user out.
  2. Skip /users/me when there's no token. With nothing stored, refresh() sets the user to null and returns without a network call. After a 401 has cleared the token, later page loads no longer repeat the failing request.
  3. Tests: src/lib/api-unauthorized.test.ts checks that isUnauthorized is true only for a 401 ApiError, and false for 403, 500, NetworkError and plain errors.

Commits

  • fix(auth): clear an expired/invalid JWT when /users/me returns 401 (#42)
  • fix(auth): skip /users/me when no token is stored; test isUnauthorized (#42)

Testing

Not run locally. To check: npm test.

Manual check:

  1. Set an expired token in localStorage['stellartickets.token'].
  2. Reload: the token is removed and you're signed out.
  3. Reload again: no /users/me request is made.
    Closes A stale or expired JWT is never removed from localStorage #42
    Closes Expired sessions mid-use produce generic errors instead of logging the user out #43
    Closes localStorage access is unguarded and throws in blocked-storage/private modes #44
    Closes login/register succeed then bounce the user back to /login when /users/me fails #45

…tellarTickets#42)

refresh() swallowed every /users/me failure with setUser(null), leaving the
dead token in localStorage so every request kept sending it. On a 401
(new isUnauthorized helper in api.ts) clear the token first; network/5xx
errors still keep it since the session may be valid.
StellarTickets#42)

With no token there is nothing to validate, so refresh() now sets the user
to null without the network call that would fail on every page load.
Adds unit tests: only a 401 ApiError clears the token; 403/5xx/network
errors do not.
@netlify

netlify Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

❌ Deploy Preview for stellartickets failed.

Name Link
🔨 Latest commit a1afaae
🔍 Latest deploy log https://app.netlify.com/projects/stellartickets/deploys/6ab80f69a52e280008db57b1

@drips-wave

drips-wave Bot commented Sep 26, 2026

Copy link
Copy Markdown

@miraclesonly Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@presidojay1
presidojay1 merged commit 17bc20e into StellarTickets:main Sep 26, 2026
0 of 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

2 participants