
JWT: Stateless Authentication Secret
Maintain login without session storage. Server just verifies token. The identity of Base64-encoded JSON. Why stateless scales better.

Maintain login without session storage. Server just verifies token. The identity of Base64-encoded JSON. Why stateless scales better.
Why does my server crash? OS's desperate struggle to manage limited memory. War against Fragmentation.

Two ways to escape a maze. Spread out wide (BFS) or dig deep (DFS)? Who finds the shortest path?

A comprehensive deep dive into client-side storage. From Cookies to IndexedDB and the Cache API. We explore security best practices for JWT storage (XSS vs CSRF), performance implications of synchronous APIs, and how to build offline-first applications using Service Workers.

Fast by name. Partitioning around a Pivot. Why is it the standard library choice despite O(N²) worst case?

When building my first API server, I was lost on how to implement login functionality. "I'll use sessions," I thought, storing sessions in server memory. But when I restarted the server, all users got logged out. I heard Redis could help, but session sharing gets complex when scaling to multiple servers.
"Use JWT," someone advised. "Token-based authentication," they called it. But I couldn't grasp how the server could maintain login state without storing anything. Tokens live on the client? Won't someone forge it? But there's a signature for security? I didn't even know what a signature was.
At first, I thought "JWT is an encrypted token," but then discovered Base64 decoding reveals everything. Shocked. If it's not even encrypted, how is it secure? Why use it? Questions piled up.
If clients hold the token, can't users manipulate it? Change the payload to say "I'm admin" and fool the server? Sessions are server-side, so they're safe. But client-side tokens seem risky.
JWT looked like random characters with 3 parts separated by .. Initially thought "this is encrypted," but Base64 decoding shows everything plainly. Wait, this isn't encryption? Why use it this way?
"Signatures prevent forgery," they said. But what is a signature? Just a hash? What are HMAC and RSA? Why does having a secret key make it secure? Didn't understand.
Sessions let servers invalidate anytime since server stores state. JWT can't be invalidated until expiration once issued. Isn't that worse for security? Why even use JWT?
A colleague explained it like this:
"Sessions are like getting a number ticket at an amusement park. The entrance gives you '12' and notes 'Number 12 is John.' Every ride, you show '12,' and staff check their notes: 'Number 12 is John, okay to ride.' Problem: the park must manage everyone's list. Memory burden, and if there are multiple parks, sharing the list gets complicated.
JWT is like a ticket with 'Name: John, Age: 25, Entry: 10am' written on it, plus the park's stamp. Next visit, you show the ticket, staff check the stamp, 'Oh, John.' The park has no list. All info is on the ticket. That's Stateless."
This metaphor clicked. Sessions are server "memory," JWT carries info in the token itself so the server doesn't need to remember. What's the stamp? The signature. Without a valid stamp (signature), you can't get in. That's JWT's core security.
First time seeing JWT, it looked like:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMzQ1LCJuYW1lIjoiQWxpY2UifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Split into 3 parts (by .):
{
"alg": "HS256", // HMAC SHA-256
"typ": "JWT"
}
Base64 encoded: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. Here, HS256 means HMAC SHA-256 algorithm. Later learned this is symmetric key - only server needs the secret. There's also RSA (asymmetric), but that's another topic.
{
"userId": 12345,
"name": "Alice",
"exp": 1735689600, // Expiration (timestamp)
"iat": 1735603200 // Issued at
}
Base64 encoded: eyJ1c2VySWQiOjEyMzQ1LCJuYW1lIjoiQWxpY2UifQ. Critical: Base64 is encoding, not encryption. Anyone can decode and see contents. Never put passwords or SSNs here.
JWT has standard claims I learned later:
iss (issuer): Token issuersub (subject): Token subject (usually user ID)aud (audience): Token audienceexp (expiration): Expiration timeiat (issued at): Issue timenbf (not before): Token activation timeDidn't know these existed at first, but studying OAuth2 and OpenID Connect taught me these standard claims matter.
Most important part. Signature is made like:
HMACSHA256(
base64(header) + "." + base64(payload),
secret_key
)
Result: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Signature uses a secret key only the server knows. If someone modifies the payload to say "I'm admin," the signature won't match and server rejects it during verification. Can't create valid signature without the secret.
const jwt = require('jsonwebtoken');
app.post('/login', (req, res) => {
const { email, password } = req.body;
const user = db.findUser(email, password);
if (!user) {
return res.status(401).send('Login failed');
}
const token = jwt.sign(
{
userId: user.id,
name: user.name,
role: user.role
},
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
});
Important: manage secret in environment variables. Hardcoding in code risks exposure when pushed to GitHub. Store in .env and add to .gitignore.
// Client
const token = localStorage.getItem('token');
fetch('/api/profile', {
headers: { 'Authorization': `Bearer ${token}` }
});
// Server
app.get('/api/profile', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).send('No token');
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = db.getUserById(decoded.userId);
res.json(user);
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).send('Token expired');
}
res.status(401).send('Invalid token');
}
});
jwt.verify() verifies the signature. If token is tampered or expired, throws error. Server queries no storage here. Token has info, just verify signature. That's Stateless core.
1. Login success
2. Server: Generate session ID → store in memory/Redis/DB
sessions = {
"abc123": { userId: 12345, name: "Alice", ... }
}
3. Client: Store session ID in cookie
4. Next request: Send session ID via cookie
5. Server: Query storage with session ID for user info
Problems:
1. Login success
2. Server: Generate JWT (includes user info)
3. Client: Store JWT (localStorage or cookie)
4. Next request: Send JWT
5. Server: Only verify JWT (no storage query!)
Pros:
Cons:
Initially thought "JWT is encrypted token." Actually, it's just encoding.
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMzQ1LCJyb2xlIjoiYWRtaW4ifQ.xxx";
// Base64 decode reveals contents!
const parts = token.split('.');
const payload = JSON.parse(atob(parts[1]));
console.log(payload); // { userId: 12345, role: "admin" }
JWT is encoding, not encryption! Anyone can see contents. Never put passwords, SSNs, credit card numbers.
"Then how is it secure?" → Signature.
Forgery attempt:
1. Eve modifies payload: { userId: 99999, role: "admin" }
2. Base64 encodes to manipulate token
3. Sends to server
Server verification:
HMAC(header + payload, secret_key) != token signature
→ ❌ Verification fails! Forgery detected
Can't create valid signature without secret key. If Eve changes payload, signature won't match and server rejects. That's JWT's core security.
Initially only used HMAC (HS256), later learned RSA (RS256) exists.
RSA is better for microservices. Auth server has private key to issue tokens, other services only need public key to verify. No need to share secret, better security.
Problem: User logs out, but token still valid
// Logout
localStorage.removeItem('token');
// But if Eve copied the token?
// Still usable until expiration!
Sessions can be immediately invalidated by deleting server-side. JWT can't be stopped once issued until expiration. JWT's biggest weakness.
Solutions:
Session ID: 6 bytes
JWT: 200+ bytes
Every request → bandwidth waste
Especially problematic on mobile. Sending 200 bytes in headers constantly. Minimize payload or use JWT only for sensitive requests.
Payload visible to anyone. Accidentally putting passwords or personal info is disastrous.
To solve JWT invalidation, use Refresh Token pattern. Most realistic approach.
// Access Token: 15min (short)
// Refresh Token: 7days (long, stored in DB)
// Login
app.post('/login', (req, res) => {
const user = authenticate(req.body);
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user.id, tokenType: 'refresh' },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
// Store Refresh Token in DB (for invalidation)
db.saveRefreshToken(user.id, refreshToken);
res.json({ accessToken, refreshToken });
});
// When Access Token expires
app.post('/refresh', (req, res) => {
const { refreshToken } = req.body;
// Check DB (validate if not invalidated)
if (!db.isValidRefreshToken(refreshToken)) {
return res.status(401).send('Please login again');
}
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
// Issue new Access Token
const newAccessToken = jwt.sign(
{ userId: decoded.userId },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken: newAccessToken });
} catch (err) {
res.status(401).send('Invalid refresh token');
}
});
// Logout
app.post('/logout', (req, res) => {
const { refreshToken } = req.body;
// Delete Refresh Token from DB (invalidate)
db.deleteRefreshToken(refreshToken);
res.send('Logout successful');
});
Pros:
Cons:
No perfect solution. Accepted the trade-off.
Where to store JWT was another dilemma.
const { token } = await fetch('/login').then(r => r.json());
localStorage.setItem('token', token);
fetch('/api/profile', {
headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` }
});
Pros: Simple. JavaScript accessible. Cons: Vulnerable to XSS. Malicious scripts can steal token.
// Server
res.cookie('token', token, {
httpOnly: true, // JavaScript can't access
secure: true, // HTTPS only
sameSite: 'strict'
});
// Client automatically sends cookie (no header setup needed)
Pros: XSS protection. JavaScript can't access. Cons: CSRF vulnerable (need CSRF token defense).
Conclusion: httpOnly Cookie + CSRF defense is safer.
Studying OAuth2, JWT appeared. OAuth2 is authentication/authorization framework, often uses JWT as Access Token format.
1. User logs in with Google
2. Google issues Authorization Code
3. Server requests Access Token with Code
4. Google issues JWT format Access Token
5. Server verifies JWT → confirms user info
Access Tokens from Google, GitHub, Facebook are mostly JWT format. OpenID Connect uses JWT as standard.
// ❌ Never do this
const token = jwt.sign(payload, 'my-secret-key');
// ✅ Use environment variables
const token = jwt.sign(payload, process.env.JWT_SECRET);
// ❌ 7 days too long
jwt.sign(payload, secret, { expiresIn: '7d' });
// ✅ Short, use Refresh Token
jwt.sign(payload, secret, { expiresIn: '15m' });
Sending JWT over HTTP allows interception. Must use HTTPS.
// ❌ Don't put password
jwt.sign({ userId: 123, password: 'secret' }, secret);
// ✅ Minimal info only
jwt.sign({ userId: 123, role: 'user' }, secret);
// ❌ Gives hints to attackers
res.status(401).send('Signature mismatch');
// ✅ Generic message
res.status(401).send('Invalid token');
How I understand JWT's core:
JWT is "traded session's memory burden for scalability, accepted token management complexity." No perfect solution. Learned choosing between session and JWT depends on situation.
JWT is better for microservices or many servers. Sessions are better when security is critical or immediate invalidation needed. Both have pros/cons. No need to always use JWT - that's what I learned.