How JWT Works: JSON Web Tokens Explained
JSON Web Tokens (JWT) are a compact, URL-safe format for transmitting claims between two parties. If you have ever logged into a web application and stayed authenticated as you navigated between pages, there is a good chance a JWT was involved. Despite their apparent simplicity — they are just three Base64-encoded strings separated by dots — JWTs encode a surprisingly rich set of ideas from cryptography, identity management, and distributed systems design.
The Structure of a JWT
A JWT consists of three parts separated by periods: Header, Payload, and Signature. Each part is encoded using Base64url, a URL-safe variant of Base64 that replaces + with -, / with _, and strips trailing = padding.
A typical JWT looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUiLCJpYXQiOjE2MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
The Header declares the token type and signing algorithm. The Payload contains the actual claims — statements about an entity (typically the authenticated user) along with metadata like expiration time. The Signature is computed over the encoded header and payload using a secret key (for symmetric algorithms) or a private key (for asymmetric ones), preventing anyone from tampering with the token's contents.
Base64url Encoding
Standard Base64 uses +, /, and = characters that have special meanings in URLs and HTTP headers. Base64url substitutes - for + and _ for /, and omits padding.
An important point: Base64url is an encoding, not encryption. Anyone who receives a JWT can decode the header and payload and read their contents. The signature does not make the payload secret — it only guarantees integrity and authenticity. If you need confidentiality, you need JWE (discussed below) or an encrypted transport layer like TLS.
Signing Algorithms
The alg field in the header specifies how the signature is computed. Three families of algorithms are commonly used:
HMAC-SHA256 (HS256) is a symmetric algorithm. The same secret key is used to both create and verify the signature. This is suitable when the token issuer and consumer are the same server, or when they share a secret. The risk is that anyone who can verify tokens can also forge them.
RSA (RS256, RS384, RS512) is an asymmetric algorithm. The issuer signs with a private key, and consumers verify with the corresponding public key. This is essential in distributed systems: an authorization server can issue tokens, and many resource servers can independently verify them without ever having access to the signing key.
ECDSA (ES256, ES384, ES512) uses elliptic curve cryptography to achieve the same asymmetric property as RSA but with smaller key sizes. ES256 uses the P-256 curve, producing 64-byte signatures compared to RSA's 256-byte signatures. EdDSA (using Ed25519) is a newer alternative gaining adoption for its speed.
Registered Claims
The JWT specification (RFC 7519) defines seven registered claim names:
iss (Issuer) — who issued the token (typically a URL like https://auth.example.com).
sub (Subject) — the principal that is the subject of the token, usually a user ID.
aud (Audience) — the intended recipients. A token for api.example.com should be rejected by api.other.com.
exp (Expiration Time) — Unix timestamp after which the token must be rejected. Short times (5-15 minutes) limit damage from stolen tokens. This is the most important claim for security.
nbf (Not Before) — Unix timestamp before which the token must not be accepted.
iat (Issued At) — when the token was created.
jti (JWT ID) — a unique identifier for replay attack prevention.
JWS vs JWE
What most people call "a JWT" is technically a JWS — a JSON Web Signature. JWS provides integrity and authenticity but anyone can read the payload. JWE (JSON Web Encryption) provides confidentiality — the payload is encrypted so only the intended recipient can read it. JWE tokens have five parts: a protected header, an encrypted key, an initialization vector, the ciphertext, and an authentication tag.
In practice, JWS is far more common. Most systems transmit JWTs only over TLS/HTTPS, which provides transport-layer encryption.
Token Validation
A proper validation procedure includes these steps in order:
1. Parse the token by splitting on . characters. A JWS must have exactly three parts.
2. Decode the header and verify the alg matches an algorithm you expect. Never let the token's header dictate which algorithm to use without restriction.
3. Verify the signature using the appropriate key — shared secret for symmetric, public key for asymmetric (typically fetched from a JWKS endpoint).
4. Validate claims: exp is in the future (with small clock skew tolerance), nbf is in the past, iss matches a trusted issuer, aud includes your service.
5. Only then trust the payload and proceed.
JWK and JWKS
A JWK (JSON Web Key) is a JSON representation of a cryptographic key. A JWKS (JSON Web Key Set) is an array of JWKs. Authorization servers publish their public keys at a well-known endpoint, typically /.well-known/jwks.json. Resource servers fetch this to obtain verification keys. The JWT header's kid (Key ID) claim selects the right key, allowing key rotation without downtime.
This is fundamental to how OAuth 2.0 and OpenID Connect work. The authorization server signs tokens with its private key, publishes the public key via JWKS, and resource servers verify independently.
Stateless Authentication
The core appeal of JWTs is stateless authentication. Traditional session-based systems store session data on the server and give the client a session ID cookie. Every request requires a server-side lookup. With JWTs, the token contains everything the server needs — no lookup required.
This scales well: any server with the verification key can validate any token. No sticky sessions or shared session stores needed.
The tradeoff is revocation. You cannot invalidate a JWT the way you delete a session. Once issued, a JWT is valid until it expires. If you need immediate revocation, you need a blocklist, short expiration times with refresh tokens, or both.
JWT vs Opaque Tokens
An opaque token is a random string that means nothing on its own — the resource server must send it to the authorization server for validation (token introspection, RFC 7662). Opaque tokens are simpler, easier to revoke, and do not leak information. JWTs are self-contained and avoid network round-trips. Many systems use both: JWTs as short-lived access tokens and opaque refresh tokens stored server-side.
Token Storage
localStorage is accessible to any JavaScript on the page. An XSS vulnerability lets an attacker read and exfiltrate the token.
httpOnly cookies cannot be read by JavaScript. The browser includes them automatically. This prevents XSS-based token theft. CSRF risks are mitigated with SameSite cookie attributes.
The consensus: store JWTs in httpOnly, Secure, SameSite cookies whenever possible.
Expiration and Refresh
Short-lived access tokens (5-15 minutes) combined with longer-lived refresh tokens is the standard pattern. Refresh token rotation — where each refresh token can only be used once — detects stolen tokens: if both attacker and legitimate client try to use the same refresh token, the system knows it was compromised.
Common Attacks
The alg: "none" attack — An attacker changes the algorithm to "none", modifies the payload, and removes the signature. If the server honors "none", it skips verification entirely. Fix: always explicitly specify accepted algorithms.
Key confusion attack — A server uses RS256 (asymmetric) with a public key. The attacker signs a token with HS256 (symmetric) using the public key as the HMAC secret. If the server trusts the token's stated algorithm, verification succeeds. Fix: enforce the algorithm on the server side.
Missing audience validation — A token for billing.example.com gets accepted by admin.example.com. Always check the aud claim.
Excessive payload size — JWTs travel in every request header. Keep payloads minimal (IDs and roles, not full profiles) to avoid hitting header size limits.
JWTs in the Ecosystem
JWTs are most commonly encountered as access tokens in OAuth 2.0 flows. OpenID Connect extends OAuth by defining an ID Token — always a JWT — that carries identity claims. The entire JWT ecosystem relies on TLS/HTTPS for transport security.
See It in Action
While JWTs operate at the application layer, the infrastructure they travel over is visible at the network layer. Use the god.ad looking glass to trace the network path between you and an authentication server. Look up the IP address of your identity provider and see which autonomous systems carry your tokens across the internet. Every JWT, every OAuth flow, and every API call depends on the BGP routing fabric beneath it.