Five Gears That Hold a Login Session Together Without a Stateful Server
Authentication is the first gate in nearly every web app, and getting it wrong silently breaks user sessions. This walkthrough makes the full chain explicit — token, storage, state, transport, and routing — so a developer can debug any link in that chain rather than treating login as a black box.
HTTP remembers nothing, so a logged-in session must be rebuilt on every request. This walkthrough assembles a complete authentication loop inside a React frontend — no real backend required — using JWT signing and verification, Zustand for global state, axios interceptors, route guards, and localStorage persistence. A mock server handles the sign/verify cycle, while the frontend code demonstrates the exact sequence that keeps a user recognized across page refreshes and route changes.
The five pieces form a chain: a JWT token acts as a portable, verifiable identity card; localStorage survives refreshes so the token isn't lost; a Zustand store pulls that token into React's reactive memory; an axios request interceptor silently attaches it to every API call; and a route guard component blocks unauthenticated page visits before they render.
Each mechanism solves one specific failure mode — token loss on refresh, forgotten headers on API calls, unprotected routes rendering for logged-out users — and the mock-driven setup means the entire flow can be developed and tested before a real backend exists.
Treating authentication as a chain of five independent mechanisms — token, storage, state, transport, routing — makes failures easier to isolate than treating it as one opaque feature.
The mock-driven approach decouples frontend auth development from backend availability; the same code switches to a real server by changing a base URL and disabling the mock plugin.
Zustand's lack of Provider boilerplate matters most in auth, where the token must be readable from deeply nested components, navigation bars, and route guards without wiring props through every layer.
The `cancelled` flag pattern in useEffect is under-taught but essential for any component that fires an async request on mount and can unmount before the response arrives.
Reading the token directly from localStorage inside the axios interceptor, rather than from the Zustand store, keeps the interceptor independent of React's component tree and avoids circular dependencies.