What is a JWT?
A short reference on JSON Web Tokens — the three-part structure, the algorithms that sign them, what they are good for, what they are bad for, and why you should not roll your own auth on top.
The one-line definition
A JWT (JSON Web Token, RFC 7519) is a compact, URL-safe representation of a JSON payload that can be cryptographically signed and optionally encrypted. It has exactly three parts separated by dots: header, payload, signature.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJpYXQiOjE3MTYyNDEyMDB9.aJ3RzM9YHbS6h9wPN8mUYTpZRRG2RJ5fXSiqgI4mP1Q
└──────────── header ────────────┘ └─────────────── payload ───────────┘ └──── signature ────┘
Each part is base64url-encoded. The header and payload are plain JSON before encoding; the signature is the result of running an algorithm over the encoded header + . + encoded payload using a secret or private key.
The header tells you the algorithm
{ "alg": "HS256", "typ": "JWT" }
alg— which signing algorithm produced the signature. Common choices:HS256(HMAC + SHA-256, shared secret),RS256(RSA + SHA-256, public/private key),ES256(ECDSA over P-256). Asymmetric algorithms (RS*/ES*) let the verifier check the signature without holding the signing key.typ— almost alwaysJWT. Sometimesat+jwtfor OAuth access tokens.kid— optional key identifier when the verifier needs to pick among several public keys (key rotation).
The payload is the claims
The payload is a JSON object of claims. Some claim names are registered by the spec and have well-known meaning:
| Claim | Meaning |
|---|---|
iss |
Issuer — who minted the token |
sub |
Subject — usually the user ID |
aud |
Audience — which service this token is for |
exp |
Expiry (Unix seconds) — the token is invalid after this time |
nbf |
Not before (Unix seconds) — the token is invalid before this time |
iat |
Issued at (Unix seconds) |
jti |
JWT ID — unique identifier, useful for revocation |
Beyond those, you can put anything you want into the payload. Common additions: user roles, tenant ID, scope strings. Do not put secrets into the payload — the payload is base64-encoded, not encrypted. Anyone with the token can read it.
The signature is what makes it trustworthy
The signature proves that whoever held the signing key produced this exact header + payload combination. If anyone modifies a single byte of the header or payload, the signature stops verifying.
This is the whole point of JWT: a server can hand a client a signed token, the client carries it around, and any server (or service) that has the verification key can read it and trust the contents without doing a database lookup. Stateless authentication.
It also means: the signature does not encrypt anything. A JWT is signed-and-verifiable, not secret. Anyone who intercepts the token can decode the payload. If you need confidentiality, use JWE (JSON Web Encryption), or just transmit the token over TLS and treat it as a bearer credential.
What JWTs are good for
- Short-lived API access tokens (OAuth 2.0 access tokens are typically JWTs). The server signs it once at login, the client carries it for the next 5-15 minutes, every API server verifies it locally without a database call.
- Stateless services where the auth service is upstream. API gateway verifies, downstream services trust. Works well in a microservices mesh.
- Federated identity — a token issued by an identity provider, consumed by a relying party. The IDP's public key is published; the relying party can verify offline.
What JWTs are bad at
- Long-lived sessions. A JWT is valid until its
exp. Revoking a JWT before then requires a server-side revocation list, at which point you've reintroduced the database lookup the JWT was supposed to avoid. For session tokens, opaque session IDs are usually cleaner. - Storing data that needs to stay private. See above — the payload is readable to anyone who has the token.
- Anything where you need to update the contents without re-signing. Once minted, a JWT is immutable. Adding a permission to a user mid-session requires issuing a new token.
The infamous footguns
alg: none. Older JWT libraries accepted a header that said the token was unsigned. Always rejectnonein the verifier.- Algorithm confusion. A library that uses the public RSA key as an HMAC secret on a token claiming
alg: HS256will verify a token that an attacker signed using the public key. Modern libraries reject this; older ones do not. Always pin the expected algorithm. - Weak HS256 secrets. HMAC keys should be at least 256 bits of entropy. A short password as the secret is brute-forceable offline.
- No
audcheck. A token issued by your identity provider for service A should not be accepted by service B. Verify theaudclaim.
Try it
The JWT decoder at /tools/jwt-decoder splits a token into its three parts, decodes the header and payload, and shows the claim interpretations — all in your browser. The token is never sent over the network.