What Is a JWT?
A JSON Web Token (JWT) is a compact, self-contained token format for securely transmitting information between parties. It's widely used for:
- Authentication — After login, the server issues a JWT; the client sends it with each request to access protected resources
- API authorization — Microservices use JWTs to pass identity and permissions without hitting a central auth service
- Single Sign-On (SSO) — Share authentication state across domains
The Three-Part Structure
A JWT is three Base64Url-encoded sections separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U
Header Payload Signature
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
{
"alg": "HS256", // signing algorithm: HS256, RS256, ES256
"typ": "JWT"
}
2. Payload (Claims)
{
"sub": "1234567890", // subject: unique user identifier
"name": "John Doe",
"iat": 1516239022, // issued at (Unix timestamp)
"exp": 1516242622, // expiration time
"iss": "auth.example.com", // issuer
"aud": "api.example.com" // audience
}
3. Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
your-secret-key
)
How to Debug a JWT Online
Use the tool.tl JWT Debugger:
- Go to tool.tl/jwt-debugger
- Paste your JWT token into the input field
- Instantly see the decoded Header and Payload in readable JSON
- Enter your secret to verify the signature
Security note: The JWT payload is Base64Url encoded, not encrypted. Anyone who gets a JWT can decode it and read its contents. Never put sensitive data (passwords, payment info) in the payload.
Standard JWT Claims Reference
| Claim | Full Name | Purpose |
sub | Subject | Unique identifier for the user/entity |
iss | Issuer | Who issued the token (e.g., auth.example.com) |
aud | Audience | Intended recipient (e.g., api.example.com) |
exp | Expiration | Token expiry (Unix timestamp) |
iat | Issued At | When the token was created |
nbf | Not Before | Token not valid before this time |
jti | JWT ID | Unique token ID (for replay prevention) |
Common JWT Security Vulnerabilities
1. Algorithm Confusion (alg=none)
An attacker modifies the header to "alg": "none", removing signature verification. Fix: always validate and whitelist the algorithm server-side; reject none.
2. Weak Secret
HS256 uses a shared secret. Simple secrets like "secret" or "password" can be brute-forced offline. Fix: use a cryptographically random secret of at least 256 bits.
3. Missing Expiration Check
Not validating exp means expired tokens remain valid indefinitely. Fix: always verify exp in every token validation, without exception.
JWT in Code
# Python (PyJWT)
import jwt
token = jwt.encode({'sub': '123', 'exp': 1700000000}, 'my-secret', algorithm='HS256')
data = jwt.decode(token, 'my-secret', algorithms=['HS256'])
// Node.js (jsonwebtoken)
const jwt = require('jsonwebtoken');
const token = jwt.sign({ sub: '123' }, 'my-secret', { expiresIn: '1h' });
const data = jwt.verify(token, 'my-secret');
# Go (golang-jwt)
token, _ := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
return []byte("my-secret"), nil
})
Frequently Asked Questions
What's the difference between JWT and session-based auth?
Sessions store state server-side (in memory or a database); the client only holds a session ID. JWTs are stateless — all information lives in the token itself. JWTs scale horizontally without shared session storage, but can't be invalidated without a blacklist or short expiry.
Where should I store a JWT on the client?
localStorage is vulnerable to XSS but immune to CSRF. HttpOnly cookies can't be accessed by JavaScript (XSS-safe) but require CSRF protection. The recommended combination: HttpOnly cookie for the access token + a separate CSRF token in a header.
How do I invalidate a JWT before it expires?
JWTs have no built-in revocation. Common patterns: maintain a token blacklist in Redis (check on every request); use short expiry (15 minutes) with a Refresh Token; rotate a per-user secret version so old tokens fail validation.