How JWTs Work and Where to Store Them Safely

JSON Web Tokens (JWTs) are a compact, URL-safe standard used to securely transmit data between clients and servers for authentication and authorization. This guide breaks down the internal structure and mechanics of JWTs, outlines the standard authentication workflow, and details the most secure storage patterns within JavaScript web applications to protect your users against common vulnerabilities like Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF).

Understanding the Structure of a JWT

A JSON Web Token is a single string composed of three distinct parts separated by periods (.):

  1. Header: Contains metadata about the token, typically the type of token (JWT) and the signing algorithm used (such as HS256 or RS256).
  2. Payload: Contains the claims, which are statements about the user and additional context (e.g., userId, role, and token expiration timestamp exp). This data is Base64URL-encoded, meaning it is readable by anyone who inspects the token and must never contain sensitive secrets like plain passwords.
  3. Signature: Created by taking the encoded header, the encoded payload, and hashing them with a secret key (or private key) using the algorithm specified in the header.

Because the signature is verified against the payload, any tampering with the claims automatically invalidates the token.

The JWT Authentication Workflow

  1. Authentication: The user submits their credentials (e.g., email and password) to the authentication server.
  2. Generation: Upon successful validation, the server generates a JWT containing the user’s identity claims, signs it with a private key or secret, and sends it back to the client.
  3. Transmission: For subsequent protected API requests, the client sends this token to the server, typically inside the Authorization request header using the Bearer <token> schema.
  4. Verification: The server verifies the token’s cryptographic signature using its secret or public key. If the signature is valid and the token is not expired, the request is processed without requiring a database lookup for session data.

Where to Safely Store JWTs in JavaScript Web Apps

Choosing where to store tokens in the browser represents a critical security trade-off between XSS vulnerability and CSRF vulnerability.

1. Browser Storage (localStorage / sessionStorage)

2. In-Memory (JavaScript Variable)

The Best-Practice Architecture: Dual-Token Pattern

The most secure approach for modern JavaScript single-page applications (SPAs) is the dual-token system:

  1. Short-Lived Access Token (Stored in Memory): Used to authenticate individual API requests. It has a short lifespan (e.g., 5 to 15 minutes) and lives entirely in JavaScript memory.
  2. Long-Lived Refresh Token (Stored in an HttpOnly, Secure, SameSite Cookie): Has a longer lifespan (e.g., 7 days) and cannot be read by JavaScript.
  3. Silent Refresh Cycle: When the in-memory access token expires, the client calls a dedicated /refresh-token endpoint. The browser automatically attaches the secure refresh token cookie. The server validates the cookie, issues a new short-lived access token, and returns it to the client memory, maintaining a seamless and secure session.