JWT Tokens Explained - Complete Guide to JSON Web Tokens
Learn everything about JWT tokens, how they work, when to use them, and best practices for secure implementation in your applications.
What is JWT?
JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. JWTs are compact, URL-safe tokens that can be verified and trusted because they are digitally signed.
Why Use JWT?
JWTs are commonly used for:
- Authentication: Verify user identity
- Authorization: Control access to resources
- Information Exchange: Securely transmit data between parties
JWT Structure
A JWT consists of three parts separated by dots (.):
header.payload.signature
1. Header
The header typically consists of two parts:
- Token type (JWT)
- Signing algorithm (HMAC, RSA, or ECDSA)
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
The payload contains the claims - statements about an entity (typically the user) and additional data.
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}
💡 Note: The
iatandexpfields are Unix timestamps (seconds since epoch). Convert them to readable dates using our Timestamp Converter tool.
3. Signature
The signature ensures the token hasn't been tampered with:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Base64URL Note
JWT header and payload are Base64URL-encoded (URL-safe, no padding). Do not assume standard Base64; convert if necessary before decoding.
Standard Claims
JWT defines several standard claims:
Registered Claims
- iss (issuer): Who issued the token
- sub (subject): Who the token is about
- aud (audience): Who should accept the token
- exp (expiration): When the token expires (Unix timestamp)
- nbf (not before): When the token becomes valid (Unix timestamp)
- iat (issued at): When the token was created (Unix timestamp)
- jti (JWT ID): Unique identifier for the token
💡 Tip: Convert Unix timestamps (
exp,nbf,iat) to readable dates using our Timestamp Converter tool.
Custom Claims
You can add custom claims for your application:
{
"sub": "user123",
"email": "user@example.com",
"role": "admin",
"permissions": ["read", "write", "delete"]
}
How JWT Authentication Works
1. User Login
Client → Server: POST /login (username, password)
Server → Client: JWT token
2. Accessing Protected Resources
Client → Server: GET /api/data (Authorization: Bearer <token>)
Server: Validates token
Server → Client: Protected data
3. Token Validation
The server:
- Verifies the signature
- Checks expiration time
- Validates claims
- Grants or denies access
JWT Best Practices
Security
-
Use Strong Secrets
- Use cryptographically secure random strings
- Keep secrets in environment variables
- Never commit secrets to version control
-
Set Expiration Times
{ "exp": 1516242622 // 1 hour from issuance } -
Use HTTPS
- Always transmit JWTs over HTTPS
- Prevents token interception
-
Validate Everything
- Verify signature
- Check expiration
- Validate issuer and audience
- Verify token hasn't been revoked
Storage
Where to Store JWTs:
✓ Memory (React state, Vue store)
- Most secure
- Lost on refresh (use refresh tokens)
✓ HttpOnly Cookies
- Protected from XSS
- Automatically sent with requests
- Requires CSRF protection (use SameSite=Lax/Strict when possible and CSRF tokens for state-changing requests)
- Always set Secure; consider rotating refresh tokens
✗ Local Storage
- Vulnerable to XSS attacks
- Not recommended for sensitive data
✗ Session Storage
- Vulnerable to XSS attacks
- Lost on tab close
Token Design
-
Keep Payload Small
- Only include necessary data
- Smaller tokens = faster transmission
-
Don't Store Sensitive Data
- Payload is Base64 encoded, not encrypted
- Anyone can decode and read it
-
Use Refresh Tokens
Access Token: Short-lived (15 min) Refresh Token: Long-lived (7 days)
JWKS and Key Rotation
Many providers expose a JWKS (JSON Web Key Set) endpoint to publish public keys. Use the kid in the token header to select the correct key.
// Pseudo-code
const jwks = await fetch('https://issuer.example.com/.well-known/jwks.json').then(r => r.json());
const { kid } = JSON.parse(atob(token.split('.')[0].replace(/-/g, '+').replace(/_/g, '/')));
const key = jwks.keys.find(k => k.kid === kid);
// Use key.n/key.e (RSA) or x/y (EC) with your JWT library to verify
Rotate keys periodically and support overlapping validity during rotation.
Common JWT Algorithms
Symmetric (HMAC)
HS256, HS384, HS512
- Single secret key
- Same key for signing and verification
- Faster
- Simpler to implement
// Signing
const token = jwt.sign(payload, 'secret', { algorithm: 'HS256' });
// Verifying
const decoded = jwt.verify(token, 'secret');
Asymmetric (RSA, ECDSA)
RS256, RS384, RS512, ES256, ES384, ES512
- Public/private key pair
- Private key for signing
- Public key for verification
- More secure for distributed systems
// Signing with private key
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
// Verifying with public key
const decoded = jwt.verify(token, publicKey);
JWT vs Session Authentication
JWT Advantages
✓ Stateless - no server-side storage
✓ Scalable - works across multiple servers
✓ Mobile-friendly - works with native apps
✓ Cross-domain - easy CORS handling
✓ Decoupled - separates auth from backend
JWT Disadvantages
✗ Larger size - increases bandwidth
✗ Cannot invalidate - until expiration
✗ Security concerns - if secret compromised
✗ Payload visible - anyone can decode
Sessions Advantages
✓ Can be invalidated immediately
✓ Smaller - just session ID
✓ Server controls data
✓ Mature and well-tested
Sessions Disadvantages
✗ Requires server-side storage
✗ Harder to scale horizontally
✗ CORS complications
✗ Mobile app challenges
Implementing JWT
Node.js Example
const jwt = require('jsonwebtoken');
// Generate token
function generateToken(user) {
const payload = {
sub: user.id,
email: user.email,
role: user.role
};
return jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h',
issuer: 'myapp.com'
});
}
// Verify token middleware
function verifyToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
}
// Protected route
app.get('/api/profile', verifyToken, (req, res) => {
res.json({ user: req.user });
});
Python Example
import jwt
from datetime import datetime, timedelta
# Generate token
def generate_token(user):
payload = {
'sub': user['id'],
'email': user['email'],
'exp': datetime.utcnow() + timedelta(hours=1),
'iat': datetime.utcnow()
}
return jwt.encode(payload, 'secret', algorithm='HS256')
# Verify token
def verify_token(token):
try:
decoded = jwt.decode(token, 'secret', algorithms=['HS256'])
return decoded
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidTokenError:
return None
Common JWT Pitfalls
1. Using "none" Algorithm
Never accept tokens with alg: "none":
// Bad - vulnerable to attack
jwt.verify(token, null, { algorithms: ['none', 'HS256'] });
// Good - specify exact algorithm
jwt.verify(token, secret, { algorithms: ['HS256'] });
2. Not Checking Expiration
Always validate the exp claim:
try {
const decoded = jwt.verify(token, secret);
// Token is valid and not expired
} catch (err) {
if (err.name === 'TokenExpiredError') {
// Handle expired token
}
}
3. Storing Secrets in Code
// Bad
const token = jwt.sign(payload, 'mysecret123');
// Good
const token = jwt.sign(payload, process.env.JWT_SECRET);
4. Not Rotating Secrets
- Rotate secrets periodically
- Have a strategy for key rotation
- Support multiple valid secrets during transition
Refresh Token Strategy
Implementation Pattern
- Login: Return both access and refresh tokens
- Access Token: Short-lived (15 min)
- Refresh Token: Long-lived (7 days)
- When Access Token Expires: Use refresh token to get new access token
- When Refresh Token Expires: Re-authenticate
// Refresh token endpoint
app.post('/api/refresh', async (req, res) => {
const { refreshToken } = req.body;
try {
const decoded = jwt.verify(refreshToken, REFRESH_SECRET);
const user = await getUserById(decoded.sub);
const newAccessToken = generateAccessToken(user);
res.json({ accessToken: newAccessToken });
} catch (error) {
res.status(401).json({ error: 'Invalid refresh token' });
}
});
JWT Security Checklist
✓ Use strong, random secrets
✓ Set appropriate expiration times
✓ Validate signature and claims
✓ Use HTTPS for transmission
✓ Implement refresh token rotation
✓ Never store secrets in code
✓ Specify exact algorithms
✓ Handle token expiration gracefully
✓ Consider token revocation strategy
✓ Monitor for suspicious activity
Conclusion
JWT is a powerful tool for modern authentication and authorization. When implemented correctly with proper security measures, it provides a stateless, scalable solution for protecting your applications.
Key takeaways:
- Understand the three parts: header, payload, signature
- Use appropriate expiration times
- Never store sensitive data in payload
- Implement refresh token strategy
- Follow security best practices
- Choose the right algorithm for your use case
Whether you're building a REST API, microservices, or a mobile app, JWT provides a standardized way to handle authentication that scales with your application.
Try JWT Encoder/Decoder Now
Ready to put this into practice? Use our free JWT Encoder/Decoder tool. It works entirely in your browser with no signup required.
Launch JWT Encoder/DecoderFrequently Asked Questions
Q Does decoding a JWT verify its signature?
No. Decoding only parses the header and payload. Verification checks the signature against a trusted key.
Related Articles
Understanding JSON Formatting and Validation
A comprehensive guide to JSON format, why it matters, and how to format and validate JSON effectively for better code readability and debugging.
Understanding Base64 Encoding - Complete Guide
Learn everything about Base64 encoding, how it works, when to use it, and best practices for encoding and decoding binary data in text formats.
Mastering Regular Expressions - Complete Guide to Regex Patterns
Learn everything about regular expressions, how they work, common patterns, and best practices for pattern matching, validation, and text processing.
JWKS Key Rotation & 'kid' Errors: Complete Guide + Online Decoder Tool
Stop JWKS validation headaches. Learn how key rotation and the 'kid' claim work, and use our FREE, expert online tool to decode, verify, and troubleshoot your JWKS tokens fast.