跪拜 Guibai
← All articles
Frontend · React.js

JWT Auth in React: From HTTP Statelessness to Zustand, Axios, and Route Guards

By dzhd ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

JWT remains the default stateless auth mechanism for SPAs and mobile backends, but many frontend developers treat it as a black box. Understanding the signing and verification steps, the real XSS tradeoffs of localStorage, and how Zustand and Axios interceptors compose into a clean auth layer prevents the silent security and UX bugs that ship to production.

Summary

JWT authentication in a React app touches every layer: the stateless HTTP protocol, token structure and signing, client-side state persistence, and request plumbing. This walkthrough traces the full lifecycle of a login request, from form validation through token issuance, storage in Zustand and localStorage, automatic attachment via Axios interceptors, and route protection with React Router v6. It also unpacks the internals of Zustand's publish-subscribe model and explains why selectors prevent unnecessary re-renders.

Mock service workers simulate backend delays and JWT verification without a real server, making the frontend development loop realistic. The piece also addresses common security questions, including why JWTs are not encrypted, the XSS risks of localStorage, and why the signing secret must never appear in frontend code.

Practical details like the `Bearer` prefix, the `replace` prop on navigation to avoid redirect loops, and using `useAuthStore.getState()` inside non-React contexts round out a guide that connects protocol-level concepts to everyday React engineering.

Takeaways
JWT tokens are Base64Url-encoded, not encrypted; anyone can decode the header and payload, so never store secrets in them.
The signature is an HMAC-SHA256 hash of header and payload using a server-side secret, and verification recalculates that hash to detect tampering.
Zustand uses a publish-subscribe model with selectors, so a component reading `state.token` does not re-render when unrelated state like `todos` changes.
Syncing Zustand state to localStorage provides persistence across page refreshes, but Zustand itself does not listen for cross-tab `storage` events.
Vite's mock plugin intercepts fetch requests at the dev-server level via Connect/Express middleware, never hitting a real backend, and can simulate latency with `setTimeout`.
Axios request interceptors form a serial chain; attaching the `Bearer` prefix follows RFC 6750 and lets backends parse the token reliably.
Response interceptors that unwrap `res.data` decouple business code from backend response shape changes.
React Router's `<Navigate replace>` prevents infinite redirect loops when an unauthenticated user hits a protected route.
Inside Axios interceptors, which are not React components, Zustand's `getState()` provides a hook-free way to read or update the auth store.
Frontend code must never contain the JWT signing secret; verification happens only on the server or in mock server code.
Conclusions

Many developers mistake JWT for encryption; the article correctly stresses that its real job is tamper detection, which has direct consequences for what you put in the payload.

Coupling Zustand with localStorage is a pragmatic but leaky abstraction — cross-tab logout requires an explicit `storage` event listener that most tutorials omit.

The `Bearer` prefix is a small detail that, when missing, forces backend teams to write custom parsing logic instead of using standard OAuth2 libraries.

Using three short-circuit expressions instead of an if-else block in JSX is a deliberate DOM manipulation choice, not just a style preference, because it avoids residual event listeners on hidden elements.

React 18's automatic batching means the order of `setAuth` and `navigate` is less fragile than it appears, but the mental model of 'update state then route' still matters for older React versions.

Concepts & terms
JWT (JSON Web Token)
A compact, URL-safe token consisting of a Base64Url-encoded header and payload, plus a cryptographic signature. It is used for stateless authentication: the server signs user claims with a secret, and later verifies the signature to trust the claims without a session store.
Zustand
A lightweight React state management library based on the publish-subscribe pattern. Components subscribe to specific slices of state via selectors, avoiding re-renders when unrelated state changes. It also exposes a `getState()` method for reading state outside of React components.
Axios Interceptors
Functions that Axios runs on every request or response. Request interceptors can attach headers like `Authorization`; response interceptors can unwrap data or handle global error statuses such as 401.
Route Guard (RequireAuth)
A wrapper component in React Router that checks authentication state before rendering protected routes. If the user lacks a valid token, it redirects to the login page, optionally preserving the intended destination via route state.
Mock Service Worker / vite-plugin-mock
A dev-server middleware that intercepts HTTP requests during development and returns predefined responses, simulating a backend without needing one. It can also introduce artificial delays to test loading states.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗