Storing JWTs in localStorage

The simplest place to store a JWT on the client is localStorage. It is per-origin, persistent across refreshes, and dead easy to use. It is also readable by any JavaScript running on the page, which is the tradeoff you accept.

auth.js
javascript
export function saveToken(token) {
  localStorage.setItem('token', token);
}

export function getToken() {
  return localStorage.getItem('token');
}

export function clearToken() {
  localStorage.removeItem('token');
}

Store, read, and clear the token with the browser API.

The main risk with localStorage is cross-site scripting. If an attacker can inject JavaScript into your page, they can read the token and impersonate the user. The defense is not to avoid localStorage. It is to make sure no untrusted script ever runs on your page. Sanitize anything you render with innerHTML, validate at the backend boundary, and use a Content Security Policy.

httpOnly cookies are readable by the server but not by JavaScript, which makes them safer against XSS. They also open a different risk: CSRF, where an attacker makes the browser send the cookie to your server without the user knowing. You can defend against that with SameSite cookies or a separate CSRF token. The Next.js phase covers this pattern in depth.

Quiz: Quiz

Loading practice…