·10 min read

JWT Tokens Explained: How JSON Web Tokens Work

JSON Web Tokens (JWT) have become the de facto standard for stateless authentication in modern web applications. This guide breaks down how JWTs work, their internal structure, and when you should (and should not) use them.

What Is a JSON Web Token?

A JSON Web Token (JWT, pronounced “jot”) is a compact, URL-safe token format defined in RFC 7519. JWTs are used to securely transmit information between parties as a JSON object. The information can be verified and trusted because it is digitally signed using a secret (HMAC) or a public/private key pair (RSA or ECDSA).

Unlike traditional session-based authentication where the server stores session data, JWTs are self-contained. The token itself carries all the information needed to authenticate a user, making JWTs ideal for distributed systems, microservices, and single-page applications where maintaining server-side session state is impractical.

A typical JWT looks like this — three Base64URL-encoded strings separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header . Payload . Signature

The Three Parts of a JWT

1. Header

The header typically contains two fields: the type of token (typ, always “JWT”) and the signing algorithm (alg) being used, such as HMAC SHA-256 or RSA.

{
  "alg": "HS256",
  "typ": "JWT"
}

This JSON is then Base64URL-encoded to form the first part of the JWT. The algorithm field is critical because it tells the verifier which algorithm to use when checking the signature. Common algorithms include HS256 (HMAC with SHA-256), RS256 (RSA with SHA-256), and ES256 (ECDSA with P-256 curve).

2. Payload

The payload contains claims — statements about the user and additional metadata. There are three types of claims:

Registered claims are predefined by the JWT specification. They are not mandatory but recommended. Key registered claims include:

  • iss (Issuer) — Who created the token
  • sub (Subject) — Who the token is about (usually user ID)
  • aud (Audience) — Who the token is intended for
  • exp (Expiration) — When the token expires (Unix timestamp)
  • iat (Issued At) — When the token was created
  • jti (JWT ID) — Unique identifier for the token

Public claims are custom claims registered in the IANA JSON Web Token Registry to avoid collisions. Private claims are custom claims agreed upon between parties, such as user roles or permissions.

{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516242622
}

Important: The payload is Base64URL-encoded, not encrypted. Anyone can decode and read it. Never store sensitive information like passwords, credit card numbers, or secrets in a JWT payload.

3. Signature

The signature ensures the token has not been tampered with. It is created by taking the encoded header, the encoded payload, a secret, and the algorithm specified in the header:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

When the server receives a JWT, it recalculates the signature using the same secret and algorithm. If the recalculated signature matches the one in the token, the server knows the data has not been modified. If even a single character in the header or payload was changed, the signature will not match.

How JWT Authentication Works in Practice

Here is a typical JWT authentication flow in a web application:

  1. Login: The user sends credentials (username/password) to the authentication endpoint. The server verifies the credentials and generates a JWT containing the user's ID, roles, and an expiration time.
  2. Token delivery: The server returns the JWT to the client. The client stores it — typically in memory for SPAs, or in an HttpOnly cookie for traditional web apps.
  3. Authenticated requests: For subsequent API calls, the client includes the JWT in the Authorization header as Bearer <token>.
  4. Verification: The server verifies the signature, checks the expiration time, and extracts user information from the payload — all without querying a database.

This stateless approach scales well because any server in a cluster can verify the token independently. There is no need for a shared session store or sticky sessions.

JWT Security Best Practices

  • Always verify the signature. Never trust data from a JWT without verifying its signature first. Libraries like jsonwebtoken (Node.js) and PyJWT (Python) handle this automatically.
  • Set short expiration times. Access tokens should expire in 15 minutes to 1 hour. Use refresh tokens to obtain new access tokens without requiring the user to log in again.
  • Use strong secrets. For HMAC algorithms, use at least 256 bits of entropy. For RSA, use a minimum 2048-bit key. Never hardcode secrets in your source code.
  • Validate the algorithm. Always specify the expected algorithm when verifying tokens. The “alg: none” attack exploits servers that accept tokens without signatures.
  • Store tokens securely. Avoid localStorage for sensitive tokens as it is vulnerable to XSS attacks. HttpOnly cookies with SameSite and Secure flags are generally safer.
  • Implement token revocation. Since JWTs are stateless, revoking them requires maintaining a blocklist or using short-lived tokens with a refresh token rotation strategy.

When to Use (and Not Use) JWTs

Good Use Cases

  • API authentication for SPAs and mobile apps
  • Microservices communication where services need to verify identity without shared state
  • OAuth 2.0 and OpenID Connect implementations
  • Short-lived authorization tokens (e.g., email verification links, password reset tokens)

Consider Alternatives When

  • You need instant token revocation (sessions may be simpler)
  • Your tokens contain large amounts of data (increases request size)
  • You have a simple monolithic application with server-rendered pages
  • Security requirements demand encrypted token contents (use JWE instead)

Related Tools

Inspect and work with JWTs using these free online tools: