JSON Web Tokens (JWT) are the de-facto standard for stateless authentication in modern web APIs. Understanding their structure, how they work, and where they can fail is essential knowledge for any web developer building or consuming APIs. This guide covers everything from the basics of JWT structure to advanced security best practices.
What Is a JWT?
A JSON Web Token is a compact, URL-safe means of representing claims (pieces of information) between two parties. Typically, JWTs are used to prove that a user has authenticated and to pass information about that user between services without requiring a database lookup on every request.
A JWT looks like this: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
It's a string with three parts separated by dots (.). Each part is Base64URL-encoded.
JWT Structure: Three Parts
A JWT has three parts: header.payload.signature
1. Header
The header contains the token type (JWT) and the signing algorithm used. Decode it and you'll see JSON like:
{ "alg": "HS256", "typ": "JWT" }
Common algorithms include HS256 (HMAC with SHA-256, symmetric), RS256 (RSA with SHA-256, asymmetric), and ES256 (ECDSA with SHA-256).
2. Payload
The payload contains the "claims" — the actual data being communicated. Standard registered claims include:
sub(subject) — who the token refers to, usually a user IDexp(expiration time) — Unix timestamp after which the token is invalidiat(issued at) — Unix timestamp when the token was creatediss(issuer) — the entity that issued the tokenaud(audience) — who the token is intended for
You can also add custom claims: "role": "admin", "email": "user@example.com", etc. Keep the payload small — it's part of every request.
3. Signature
The signature is a cryptographic hash of the header + payload, created using the signing key. For HS256: HMAC-SHA256(base64url(header) + "." + base64url(payload), secret). If the payload or header are tampered with, the signature check will fail and the token will be rejected. This is what makes JWTs trustworthy — not the encoding, but the signature.
How JWT Authentication Works
- User logs in with username/password
- Server verifies credentials, creates a JWT with user's claims, signs it with its secret key, returns the JWT to the client
- Client stores the JWT (usually in memory or an httpOnly cookie)
- Client sends the JWT in the
Authorization: Bearer <token>header on subsequent requests - Server verifies the signature, checks
exp, and trusts the claims in the payload
No database lookup is needed to verify a JWT — the server only needs its signing key. This makes JWTs fast and stateless, ideal for microservice architectures.
Security Best Practices
- Always verify JWTs server-side. Never trust client-decoded JWT values for authorization decisions. A user can decode the payload (it's just Base64) and modify it — only the server can verify the signature.
- Use short expiry times for access tokens. 15 minutes is a common standard for access tokens. Use refresh tokens for long sessions.
- Store JWTs in httpOnly cookies, not localStorage. JavaScript cannot read httpOnly cookies, protecting against XSS attacks. LocalStorage is accessible to any JavaScript on the page and is a common attack vector.
- Use asymmetric algorithms (RS256, ES256) for public services. Symmetric algorithms (HS256) require sharing the secret with every service that needs to verify tokens. Asymmetric algorithms let you distribute the public key while keeping the private signing key secret.
- Validate all claims, not just the signature. Check
exp(not expired),iss(correct issuer), andaud(intended for your service). - Never put sensitive data in the payload. JWT payloads are encoded, not encrypted. Any party with the token can read the claims. Don't put passwords, credit card numbers, or private data in JWT payloads.
JWT vs Sessions: When to Use Each
Sessions store state server-side (in a database or cache) and use an opaque session ID in a cookie. JWTs store state in the token itself. Sessions are easier to revoke (delete from database), but require database lookups. JWTs are stateless but harder to revoke — once issued, a JWT is valid until it expires, unless you maintain a revocation list (which defeats the stateless benefit).
Use JWTs for: microservices, APIs consumed by third parties, mobile apps. Use sessions for: traditional web apps, situations where immediate revocation (like logout everywhere) is important.
Decode Any JWT Instantly
Inspect and decode any JWT with the Toolskuy JWT Decoder. Paste a token and see the header, payload, and expiry status displayed clearly — no server upload, completely private, runs in your browser.