JWT Token Structure Explained
JSON Web Tokens (JWTs) are the standard way to pass authentication and authorization data between services. If you work with APIs, single sign-on, or microservices, you encounter JWTs daily. Yet many developers treat them as opaque strings without understanding their structure — which leads to security vulnerabilities and debugging frustration.
This guide explains exactly what is inside a JWT, how the three parts work together, and the mistakes that cause real-world security incidents.
What is a JWT?
A JWT (pronounced "jot") is a compact, URL-safe token defined by RFC 7519. It carries a set of claims (key-value pairs) that are digitally signed so the recipient can verify their authenticity without contacting the issuer.
A JWT looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNjE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
It is three Base64url-encoded segments separated by dots: Header.Payload.Signature.
Part 1: The Header
The header is a JSON object that identifies the token type and the signing algorithm:
{
"alg": "HS256",
"typ": "JWT"
}
alg— the algorithm used to create the signature. Common values:HS256(HMAC-SHA256, symmetric),RS256(RSA-SHA256, asymmetric),ES256(ECDSA P-256, asymmetric).typ— always"JWT".kid— (optional) key ID, used when the issuer rotates signing keys.
The header is Base64url-encoded to form the first segment.
Part 2: The Payload (Claims)
The payload contains the claims — statements about the user or session. Claims come in three categories:
Registered claims (RFC 7519)
| Claim | Name | Example |
|---|---|---|
iss | Issuer | "https://auth.example.com" |
sub | Subject (user ID) | "user_42" |
aud | Audience | "https://api.example.com" |
exp | Expiration (Unix timestamp) | 1716239022 |
nbf | Not Before | 1716235422 |
iat | Issued At | 1716235422 |
jti | JWT ID (unique identifier) | "a1b2c3d4" |
Public claims
Custom claims registered with IANA or using collision-resistant names:
{
"email": "alice@example.com",
"roles": ["admin", "editor"],
"org_id": "org_123"
}
Private claims
Agreed-upon claims between parties, not registered anywhere.
The payload is Base64url-encoded — not encrypted. Anyone who has the token can decode and read the claims. Never put secrets, passwords, or sensitive PII in JWT claims.
Part 3: The Signature
The signature verifies that the token was not tampered with. For HMAC-SHA256:
HMAC-SHA256(
base64url(header) + "." + base64url(payload),
secret
)
For RSA-SHA256, the issuer signs with a private key, and the recipient verifies with the corresponding public key.
The signature prevents modification: if an attacker changes a claim in the payload, the signature will not match when the recipient recalculates it.
How JWT Authentication Works
- The user logs in with credentials.
- The auth server validates credentials and creates a JWT with the user's claims.
- The auth server signs the JWT and returns it to the client.
- The client sends the JWT in the
Authorization: Bearer <token>header on subsequent requests. - The API server validates the signature, checks
exp, and uses the claims for authorization.
This is stateless: the API server does not need to query a database or session store on every request. The JWT carries all the information needed.
Common JWT Security Mistakes
1. Not validating the signature
Decoding a JWT is not the same as verifying it. Always validate the signature before trusting the claims. Libraries like jsonwebtoken (Node.js), PyJWT (Python), and golang-jwt (Go) do this automatically — but only if you pass the correct secret or public key.
2. Using the "none" algorithm
The alg: "none" attack exploits servers that accept unsigned tokens. Always reject tokens with alg: "none" and validate that the algorithm matches what you expect.
3. Storing JWTs in localStorage
localStorage is accessible to any JavaScript on the page, including XSS-injected scripts. For browser-based apps, store JWTs in httpOnly cookies (not accessible to JavaScript) or use short-lived tokens with refresh token rotation.
4. Long-lived tokens without revocation
JWTs cannot be revoked once issued — the server has no session to invalidate. Mitigations:
- Keep
expshort (5-15 minutes for access tokens) - Use refresh tokens (stored securely, revocable in database)
- Maintain a token blocklist for critical revocations
5. Not checking exp and aud
Always validate:
exp— reject expired tokensaud— reject tokens intended for a different serviceiss— reject tokens from unexpected issuers
When to Use JWTs (and When Not To)
Good use cases:
- Stateless API authentication between microservices
- Single sign-on (SSO) across multiple applications
- Short-lived authorization tokens
Bad use cases:
- Session management for a single web app (server-side sessions are simpler and revocable)
- Storing large amounts of data (JWTs are sent on every request — keep them small)
- Situations where immediate revocation is required
Inspecting JWTs
When debugging authentication issues, you need to see what is inside a token. StackCache JWT Inspector decodes the header and payload, displays the claims in a readable format, shows expiration status, and highlights potential issues — all locally in your browser. Your token never leaves your device.
Summary
A JWT is three Base64url-encoded parts: a header (algorithm), a payload (claims), and a signature (integrity proof). Understanding this structure helps you debug authentication failures, avoid security mistakes, and make informed decisions about when JWTs are the right choice for your architecture.
Try it yourself
Open the tool mentioned in this guide — it runs locally in your browser, no account needed.
Open tool