JSON Web Tokens (JWTs) are the most common way to handle authentication in modern APIs. This guide explains how they work, how to use them correctly, and what mistakes to avoid.
What is a JWT?
A JWT is a compact, self-contained token that encodes claims (pieces of information) about a user. It has three parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c3JfMTIzIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjE3MDAwMDM2MDB9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- Header (red) — algorithm and token type
- Payload (blue) — claims (user data)
- Signature (green) — cryptographic verification
Decode the header and payload
// Each part is Base64URL encoded
// Header decodes to:
{ "alg": "HS256", "typ": "JWT" }
// Payload decodes to:
{
"sub": "usr_123", // subject (user ID)
"name": "Alice",
"iat": 1700000000, // issued at (Unix timestamp)
"exp": 1700003600 // expires at (1 hour later)
}
Create a JWT in JavaScript (Node.js)
const jwt = require('jsonwebtoken'); // npm install jsonwebtoken
const secret = process.env.JWT_SECRET; // keep this secret!
// Sign a token
const token = jwt.sign(
{ sub: 'usr_123', name: 'Alice', role: 'admin' },
secret,
{ expiresIn: '1h' }
);
// Verify a token
try {
const payload = jwt.verify(token, secret);
console.log(payload.sub); // 'usr_123'
} catch (error) {
if (error.name === 'TokenExpiredError') console.log('Token expired');
if (error.name === 'JsonWebTokenError') console.log('Invalid token');
}
Create a JWT in Python
import jwt # pip install PyJWT
from datetime import datetime, timedelta, timezone
secret = 'your-secret-key'
# Create token
payload = {
'sub': 'usr_123',
'name': 'Alice',
'iat': datetime.now(timezone.utc),
'exp': datetime.now(timezone.utc) + timedelta(hours=1)
}
token = jwt.encode(payload, secret, algorithm='HS256')
# Decode and verify
try:
data = jwt.decode(token, secret, algorithms=['HS256'])
print(data['sub']) # usr_123
except jwt.ExpiredSignatureError:
print('Token expired')
except jwt.InvalidTokenError:
print('Invalid token')
Standard JWT claims
sub— Subject (user ID)iss— Issuer (your app name/URL)aud— Audience (intended recipient)exp— Expiration time (Unix timestamp)iat— Issued at timenbf— Not before timejti— JWT ID (unique identifier)
Critical security mistakes
- Using "none" algorithm — never accept tokens with
"alg":"none" - Storing in localStorage — vulnerable to XSS. Use httpOnly cookies instead.
- Putting sensitive data in payload — JWTs are encoded, not encrypted. Anyone can decode them.
- Not checking expiry — always verify the
expclaim - Weak secrets — use at least 256 bits of randomness:
openssl rand -hex 32
Try it free — JWT Decoder
Decode and inspect any JWT token instantly — see header, payload, and expiry.