After a successful login, your app stores a long string in localStorage or a cookie — something like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJKYW5lIERvZSIsImlhdCI6MTcwMDAwMDAwMH0.xxxx

That's a JWT — a JSON Web Token. It's not random noise; it has a deliberate structure and contains real data about the authenticated user. This article explains exactly what's inside and how the whole system works.

What is a JWT?

A JWT (pronounced "jot") is a compact, self-contained token for representing claims between two parties. It's defined in RFC 7519 and widely used for authentication and authorization in web applications.

A JWT consists of three Base64URL-encoded parts separated by dots:

  • Header: the token type and signing algorithm
  • Payload: the actual data — user ID, expiry, roles, etc.
  • Signature: a cryptographic hash that verifies the token hasn't been tampered with

The structure is: header.payload.signature. Each part is Base64URL-encoded, not encrypted — which is an important distinction we'll come back to.

Decoding a JWT: What's Inside Each Part

The Header

Decode the first segment and you'll see something like this:

{
  "alg": "HS256",   // signing algorithm: HMAC with SHA-256
  "typ": "JWT"      // token type
}

The Payload

The payload holds the claims — statements about the user and the token itself:

{
  "sub": "user_123",      // subject (user identifier)
  "name": "Jane Doe",
  "iat": 1700000000,      // issued at (Unix timestamp)
  "exp": 1700003600       // expires at (1 hour later)
}

The Signature

The signature is computed by hashing base64url(header) + "." + base64url(payload) with a secret key known only to the server. When the server receives a JWT, it recomputes this hash and compares it to the signature in the token. If someone modifies the payload, the signature won't match — and the server rejects the request.

Key point: the payload is just Base64URL-encoded, not encrypted. Anyone who holds the token can decode and read the claims. Never put sensitive data (passwords, credit card numbers) in a JWT payload.

The Authentication Flow in Practice

  • 1. Login: the user submits credentials to the server
  • 2. Token issued: the server validates credentials and returns a signed JWT
  • 3. Client stores it: the client saves the JWT (localStorage, memory, or an HttpOnly cookie)
  • 4. Subsequent requests: the client attaches the JWT in the Authorization header
  • 5. Server validates: the server checks the signature and expiry on every request — no database lookup needed
fetch('/api/profile', {
  headers: {
    'Authorization': 'Bearer eyJhbGci...'
  }
});

This stateless design is one of JWT's biggest advantages — the server doesn't need to maintain a session store, making it easier to scale horizontally.

Frequently Asked Questions

Is JWT secure?
JWTs provide integrity (via the signature) but not confidentiality — the payload is readable by anyone. They're safe to use as long as you keep your signing key secret, use HTTPS, set short expiry times, and never include sensitive data in the payload. If a JWT is stolen, the attacker can use it until it expires, so short-lived access tokens combined with refresh tokens are the recommended pattern.
Where should I store JWTs on the client?
The two common options are localStorage and HttpOnly cookies. localStorage is accessible to JavaScript, which makes it vulnerable to XSS attacks. HttpOnly cookies can't be read by JavaScript (reducing XSS risk) but are susceptible to CSRF if not protected with a SameSite attribute. Many teams prefer HttpOnly cookies with SameSite=Strict for added security.
JWT vs session cookies — which is better?
Neither is universally better. Session cookies require server-side storage (a session store), which is simpler to invalidate but harder to scale. JWTs are stateless and scale easily, but revoking them before expiry is tricky (you'd need a token blocklist, which reintroduces state). For most applications, either approach works fine — the right choice depends on your scaling needs and how critical immediate revocation is.

Summary

  • A JWT is a three-part token: header.payload.signature, each Base64URL-encoded
  • The payload contains claims like user ID and expiry — readable by anyone, so don't put secrets in it
  • The signature ensures the token hasn't been tampered with, but it's not encryption
  • JWTs enable stateless authentication, which simplifies scaling
  • Use short expiry times, HTTPS, and store them carefully on the client

Want to inspect or generate JWTs right now? Try these tools: