What Are JWT Tokens?
JSON Web Tokens (JWT) are a compact, URL-safe way to represent claims between two parties. They are widely used for authentication and authorization in modern web applications. Instead of storing session data on the server, JWTs allow stateless authentication by encoding user information directly into the token.
JWT Structure: Three Parts
A JWT consists of three Base64URL-encoded parts separated by dots:
header.payload.signature
1. Header
The header specifies the token type and the signing algorithm:
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
The payload contains the claims — statements about the user and additional metadata:
{
"sub": "1234567890",
"name": "Alice Developer",
"iat": 1516239022,
"exp": 1516242622,
"role": "admin"
}
Common registered claims include:
-
iss— Issuer -
sub— Subject (user ID) -
exp— Expiration time -
iat— Issued at -
aud— Audience
3. Signature
The signature ensures the token has not been tampered with:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
How JWT Authentication Works
Here is the typical JWT authentication flow:
- The user sends credentials (username + password) to the server.
- The server verifies credentials and generates a JWT.
- The client stores the JWT (usually in memory or an httpOnly cookie).
- On each subsequent request, the client sends the JWT in the
Authorizationheader. - The server verifies the token signature and extracts user data from the payload.
Creating JWTs in Node.js
Install the jsonwebtoken package:
npm install jsonwebtoken
Signing a Token
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET || 'your-secret-key';
function generateToken(user) {
const payload = {
sub: user.id,
name: user.name,
role: user.role
};
return jwt.sign(payload, SECRET, {
expiresIn: '1h',
issuer: 'myapp.com'
});
}
const token = generateToken({ id: 42, name: 'Alice', role: 'admin' });
console.log('Token:', token);
Verifying a Token
function verifyToken(token) {
try {
const decoded = jwt.verify(token, SECRET, {
issuer: 'myapp.com'
});
console.log('Valid token. User:', decoded.sub);
return decoded;
} catch (err) {
if (err.name === 'TokenExpiredError') {
console.error('Token has expired');
} else if (err.name === 'JsonWebTokenError') {
console.error('Invalid token:', err.message);
}
return null;
}
}
Express Middleware Example
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1];
const decoded = verifyToken(token);
if (!decoded) {
return res.status(403).json({ error: 'Invalid or expired token' });
}
req.user = decoded;
next();
}
app.get('/api/profile', authMiddleware, (req, res) => {
res.json({ userId: req.user.sub, name: req.user.name });
});
Common JWT Security Mistakes
1. Storing Tokens in localStorage
localStorage is accessible to any JavaScript on the page, making it vulnerable to XSS attacks. Prefer httpOnly cookies or in-memory storage.
2. Not Validating the Algorithm
Always specify the expected algorithm during verification to prevent algorithm confusion attacks:
// Secure: specify the algorithm explicitly
jwt.verify(token, SECRET, { algorithms: ['HS256'] });
3. Using Weak Secrets
For HMAC-based signing, use a cryptographically strong secret of at least 256 bits. For production systems, consider using RS256 with an RSA key pair.
4. Missing Expiration
Always set an exp claim. Tokens without expiration remain valid indefinitely if compromised.
5. Putting Sensitive Data in the Payload
The payload is only Base64-encoded, not encrypted. Never include passwords, credit card numbers, or other secrets.
JWT vs Server-Side Sessions
| Aspect | JWT | Sessions |
|---|---|---|
| Storage | Client-side | Server-side |
| Scalability | Stateless, easy to scale | Requires shared session store |
| Revocation | Difficult (needs blocklist) | Immediate |
| Size | Larger per-request overhead | Small session ID |
| Use case | APIs, microservices, SPAs | Traditional web apps |
JWTs excel in distributed systems and API-first architectures. Sessions are simpler when you need immediate revocation and have a monolithic server.
Try It Yourself
Need to quickly inspect or decode a JWT token? Use the DevToolBox JWT Decoder to paste any token and instantly see its header, payload, and signature verification status — no sign-up required.
Top comments (0)