Skip to content

Auth JWT Migration

Purpose: Replace cookie-based sessions with JWT so mobile clients can authenticate without a browser.

Done when:

  • All 14 route guards accept JWT bearer tokens
  • Refresh token rotation is implemented at POST /auth/refresh
  • Cookie-based session middleware is removed
  • Integration tests pass for both access and refresh token flows

Status: In Progress

Progress: ~60%


Plan

  • [x] Design token schema and choose signing algorithm (RS256)
  • [x] Implement JWT issuing at POST /auth/login
  • [x] Add token verification middleware
  • [x] Implement refresh token rotation at POST /auth/refresh
  • [ ] Update route guards to accept JWT (8 of 14 done)
  • [ ] Remove old session middleware
  • [ ] Write integration tests for token flows
  • [ ] Update API documentation

Session Log

Session 3 — 2026-03-17

What happened:

  • Implemented refresh token rotation at POST /auth/refresh
  • Decided to use Redis for refresh token storage instead of Postgres — the token-revocation lookup needs to be fast at scale and Redis TTL handles expiry automatically
  • Updated 8 of 14 route guards to accept JWT bearer tokens
  • Discovered that the admin routes use a different auth pattern (role-based middleware) — these need special handling

Blocked on: Nothing

Next: Update the remaining 6 route guards. The admin routes (src/routes/admin/*.js) use requireRole() middleware that reads from req.session — refactor it to read from req.user (set by the new JWT middleware) instead. After that, remove the old session middleware and write integration tests.

Session 2 — 2026-03-15

What happened:

  • Implemented JWT issuing at POST /auth/login
  • Added token verification middleware at src/middleware/auth.js
  • Tried using HS256 initially but switched to RS256 — the API gateway needs to verify tokens without access to the signing secret
  • Set access token TTL to 15 minutes, refresh token to 7 days

Blocked on: Nothing

Next: Implement refresh token rotation at POST /auth/refresh. Use Redis for token storage (see decision above about RS256). Then start updating route guards — there are 14 total, listed in src/routes/index.js.

Session 1 — 2026-03-14

What happened:

  • Created workstream from issue #42
  • Designed token schema: access token carries user ID, email, and roles; refresh token is opaque and stored server-side
  • Chose RS256 for signing so the API gateway can verify without the private key
  • Sketched out the migration plan in the checklist above

Blocked on: Nothing

Next: Implement JWT issuing at POST /auth/login — start with src/routes/auth.js and add a new issueTokenPair() utility.