JWT is a signed token containing user claims, issued after login.
Stateless: server verifies without a session database call.
Structure: header (alg, typ), payload (claims), signature (HMAC or RSA).
Verification cost: ~5–20 microseconds per token — database roundtrips are 1000x slower.
Biggest mistake: accepting "none" algorithm — grants full access with no signature.
Production insight: revocation requires a blacklist or short expiry — no server-side invalidation by default.
✦ Definition~90s read
What is JWT Authentication Flow?
JWT (JSON Web Token) authentication is a stateless authentication mechanism. When a user logs in, the server creates a signed JSON token containing the user's identity and permissions. The client stores it (typically in localStorage or an HTTP-only cookie) and sends it with every subsequent request via the Authorization header.
★
Imagine you go to a theme park and buy a wristband at the gate.
The server verifies the signature — no database lookup needed. The flow is: Login → Server issues JWT → Client stores JWT → Client sends JWT in request header → Server verifies JWT → Server grants or denies access.
JWTs are base64url-encoded and signed, not encrypted by default. That means anyone with the token can read the payload — so you never put secrets like passwords inside. What makes it trustable is the cryptographic signature appended to the payload. If the payload is tampered with, the signature verification fails.
Plain-English First
Imagine you go to a theme park and buy a wristband at the gate. Every ride operator can look at your wristband and immediately know you've paid — they don't need to call the front gate to check. JWTs work exactly like that wristband: the server hands you a token when you log in, and every future request you carry contains that token so the server can trust you instantly, without looking you up in a database each time. The token itself contains your identity, and it's tamper-proof because it's been cryptographically signed by the server that issued it.
Every modern web application needs to answer one question on every single request: 'Do I know this person, and are they allowed to do this?' The naive answer is to store a session in a database and look it up on every request. That works fine for a single server handling a few hundred users — but the moment you scale horizontally, add microservices, or need a mobile app talking to multiple APIs, that session-database approach becomes a bottleneck and an architectural headache.
JWT — JSON Web Token — was designed to solve exactly this problem. Instead of storing state on the server, you encode the user's identity and permissions directly into a signed token and hand it to the client. The client sends it back with every request, and the server can verify it cryptographically in microseconds without touching a database. The server went from being a stateful gatekeeper to a stateless verifier. That shift has enormous implications for scalability, microservices architecture, and cross-domain authentication.
By the end of this article you'll understand exactly how a JWT is structured, how the full login-to-protected-request flow works under the hood, why the signature makes it tamper-proof, how to implement it correctly in Java, and — critically — the mistakes that create real security vulnerabilities even when the basic flow looks right. Whether you're building your first authenticated API or preparing for a system design interview, you'll walk away with a complete mental model of JWT authentication.
What is JWT Authentication Flow?
JWT (JSON Web Token) authentication is a stateless authentication mechanism. When a user logs in, the server creates a signed JSON token containing the user's identity and permissions. The client stores it (typically in localStorage or an HTTP-only cookie) and sends it with every subsequent request via the Authorization header. The server verifies the signature — no database lookup needed. The flow is: Login → Server issues JWT → Client stores JWT → Client sends JWT in request header → Server verifies JWT → Server grants or denies access.
JWTs are base64url-encoded and signed, not encrypted by default. That means anyone with the token can read the payload — so you never put secrets like passwords inside. What makes it trustable is the cryptographic signature appended to the payload. If the payload is tampered with, the signature verification fails.
JwtExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.thecodeforge.auth;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
publicclassJwtExample {
// Never hardcode secrets in production; use environment variables or a vaultprivatestaticfinalSecretKey SECRET_KEY = Keys.hmacShaKeyFor(
"my-super-secret-key-at-least-256-bits-long-for-hs256".getBytes()
);
publicstaticStringcreateToken(String userId, String role, long ttlMillis) {
Date now = newDate();
Date expiration = newDate(now.getTime() + ttlMillis);
returnJwts.builder()
.setSubject(userId)
.claim("role", role)
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(SECRET_KEY, SignatureAlgorithm.HS256)
.compact();
}
publicstaticvoidmain(String[] args) {
String token = createToken("user-1234", "admin", 3600000);
System.out.println(token);
}
}
Type this code yourself rather than copy-pasting. The muscle memory of writing it will help it stick. And never commit secrets — use environment variables or a secret manager like AWS Secrets Manager.
📊 Production Insight
A common production mistake: developers set the signing key as a plain string under 256 bits. HMAC-SHA256 requires keys at least 256 bits long; shorter keys are silently padded, creating a false sense of security.
If you're using RS256, keep the private key off the authentication service's disk — mount it from a secrets volume or fetch from Vault at startup.
Rule: always verify the key length before the first deployment.
🎯 Key Takeaway
JWT provides stateless authentication via signed claims.
The signature is the only guarantee of integrity — protect the signing key as a production secret.
Never trust the algorithm from the token header; enforce algorithm in code.
Token Creation: Signing the Right Way
Creating a JWT securely involves three artifacts: the header, the payload, and the signature. The header typically contains the algorithm (HS256, RS256) and token type (JWT). The payload contains registered claims (iss, sub, exp, iat, jti) and custom claims like roles. The signature is computed by combining the base64url-encoded header and payload with a secret (HMAC) or private key (RSA/ECDSA).
Two broad signing categories: symmetric (HS256) and asymmetric (RS256, ES256). Symmetric uses the same key for signing and verification — fast but requires sharing the secret between issuer and verifier. Asymmetric uses a private key to sign and a public key to verify — you can safely distribute the public key to any verifying service. In production, asymmetric is preferred because you can rotate the private key without touching every verifier.
The most common mistake: using HS256 when the verifying party should not have the signing secret. For example, a mobile app that verifies its own JWT should never hold the HMAC secret — attackers reverse-engineer the app.
Think of the private key as a unique seal ring, the public key as a glass display of that seal's impression.
The ring (private key) stamps an impression (signature) onto the document (token content).
Anyone with the glass display (public key) can verify the impression matches the ring — they can't forge a new document using only the display.
HS256 is like sharing the seal ring itself — you must trust everyone who holds it not to impersonate you.
In production microservice environments, asymmetric keys let you revoke an issuer's ring (private key) without re-distributing secrets to verifiers.
📊 Production Insight
Key rotation is a nightmare if you hardcode HMAC secrets in every service. With RS256, simply publish the new public key to your JWKS endpoint and wait for the old tokens to expire.
If you rotate an HMAC secret, all existing tokens become invalid immediately — users forced to re-login.
Rule: for multi-service architectures, choose asymmetric signing (RS256 or ES256) from day one.
🎯 Key Takeaway
Use asymmetric keys (RS256/ES256) when multiple services verify tokens.
Symmetric keys (HS256) are fine for a single server or trusted internal network.
Never sign tokens with a weak algorithm — enforce a minimum key length.
thecodeforge.io
Jwt Authentication Flow
How Verification Works without State
Stateless verification is the killer feature of JWT. The verifying server does the following: parse the token, decode the header, check the algorithm against a whitelist, decode the payload (but don't trust it yet), recompute the signature using the expected key, and compare with the provided signature. If they match, you trust the payload claims. If not, reject.
Stateless means no database call for each request. That's a massive win for latency and scalability. However, you lose the ability to force-logout a user — a compromised token remains valid until expiry. To mitigate, use short-lived tokens (15 minutes) combined with a refresh token mechanism. The refresh token is stored server-side (or in a database) and can be revoked.
Stateless verification also requires the verifying service to possess the correct key at all times. With RS256 and JWKS endpoint, you can fetch the public key on startup and cache it. If the key rotates, the token signed with the old key stays valid until its expiry — no interruption.
user-1234 (subject) from verified token with role=admin
⚠ Watch Out: Clock Skew
If the issuing server and verifying server run on different system clocks, the exp claim might be interpreted differently. Set a small clock skew tolerance (e.g., 30 seconds) to avoid rejecting legitimate tokens. But don't set it too high — attackers can replay a stolen token for that window.
📊 Production Insight
Stateless verification fails silently: a missing public key or wrong kid points to a non-existent key. The token is rejected without a clear error, and the developer often blames the token instead of the key source.
Use a dedicated health check endpoint that returns the current JWKS keyset id and verify this matches the token's kid.
Rule: log the kid from the token and the kid from the JWKS response on every verification failure — catch key mismatches immediately.
🎯 Key Takeaway
Stateless verification trades revocation granularity for speed and scale.
Always enforce algorithm whitelist and clock skew tolerance.
Log key mismatches explicitly — they are the most common cause of verification failures.
Common JWT Vulnerabilities and How to Avoid Them
JWTs are secure only if implemented correctly. Top vulnerabilities:
Algorithm Confusion: The attacker changes 'alg' from 'RS256' to 'HS256'. If your server uses the public key as the HMAC secret (which is a string), it will verify the token using the public key as the HMAC key — the attacker can sign tokens with the public key. Fix: never derive the verification key from the token; always hardcode the expected algorithm.
'none' Algorithm: Some libraries accept 'alg: none' and skip verification. Fix: reject tokens where the algorithm is 'none' or not in the whitelist.
Weak Secret: HS256 with a short or predictable secret. Fix: use a key of at least 256 bits generated from a cryptographically secure random source.
Token Replay: A stolen token can be used until expiry. Fix: use short-lived access tokens (15 min) and implement refresh token rotation (issue a new refresh token each time, invalidate the old one).
Payload Secrets: Developers put passwords or credit card numbers in the payload. The payload is base64 encoded, not encrypted. Fix: never store sensitive data in JWT payload. If needed, use JWE (JSON Web Encryption) to encrypt the payload.
Token accepted only if algorithm matches RS256 and signature verifies
⚠ Critical: Algorithm Confusion Attack
If your verification code uses the public key as the HMAC key (because you pass it as a string to the parser), an attacker changes the header to HS256 and signs the token using that same public key (which is public!). Your server then accepts it. Always set a whitelist of allowed algorithms in the parser configuration.
📊 Production Insight
The 'kty' (key type) in JWKS is often ignored. If your JWKS contains an RSA key (kty:'RSA') but the header says 'alg:HS256', some libraries use the RSA public key bytes as the HMAC secret — still exploitable.
Always validate the kty matches the expected algorithm.
Rule: if you use RS256, ensure your JWKS only contains RSA keys and verify the header's algorithm matches.
🎯 Key Takeaway
Whitelist algorithms in the parser — never trust the token's header.
Never store secrets in payload — it's base64, not encrypted.
Short expiry + refresh token rotation is your best defense against token theft.
JWT in Microservices: Token Propagation and Revocation
In a microservices architecture, JWT shines because each service can independently verify the token without contacting a central auth store. The authentication service issues a token scoped to a user. Downstream services extract the token from the incoming request (often via a gateway) and verify it.
Challenge: revocation. Stateless tokens cannot be invalidated server-side without a blacklist. Best practice: short access token TTL (15 minutes) plus a long-lived refresh token stored in the auth service's database. When the user logs out, you invalidate the refresh token. The access token will expire soon. For immediate revocation, you can maintain a distributed blacklist (Redis) of jti claims, but this reintroduces state.
Another challenge: token size. With multiple claims and signatures, tokens can grow beyond 2KB. In HTTP headers, that's fine. But if you pass tokens via URL query parameters (bad practice), you'll hit length limits.
Token propagation across service boundaries should use the same signed token — never create a new token per service, as that requires each service to trust the other. Use the existing JWT and let each service verify it with the same public key.
Claims attached to request; downstream services read userId and roles from request attributes
Mental Model
Mental Model: The Passport
A JWT is like an international passport — issued by one country's authority, verifiable by any border control that trusts that authority.
The passport contains your identity and photo (claims).
The passport's anti-forgery features (watermarks, holograms) are like the cryptographic signature.
Each border control (microservice) verifies the passport without calling the issuing embassy (auth service) — that's stateless verification.
If the passport is stolen, you must cancel it (revoke the refresh token) — the passport itself cannot be invalidated until expiry.
You don't put your bank PIN in the passport — similarly, don't put secrets in the JWT payload.
📊 Production Insight
Microservice teams often verify the same JWT multiple times (gateway, service A, service B). Each verification costs microseconds, but if you verify at every hop, you add latency. Instead, have the gateway verify the token once, extract the claims, and propagate them via request context (HTTP header) signed by a short-lived internal token.
However, this internal token must be scoped to only the gateway and downstream services, not the user. It's an additional trust boundary.
Rule: if all services run in a trusted network (e.g., Kubernetes cluster with mTLS), you can skip re-verification in downstream services and trust the claims set by the gateway.
🎯 Key Takeaway
JWT thrives in microservices because each service verifies independently.
Short expiry + refresh token rotation solves revocation.
Gateway-level verification with claim propagation avoids redundant crypto and reduces latency.
thecodeforge.io
Jwt Authentication Flow
Session Tokens vs. JWT: Why You Reach for Asymmetric Keys
You've heard the debate. Sessions live on the server. JWTs live on the client. The real difference is failure mode. Session tokens require a database lookup every request. That lookup adds latency and couples your auth to a centralized store. For a single web app, that's fine. For a microservice mesh, it's a nightmare. JWT flips the model: the server signs a token with a private key, and any service can verify it with a public key. No shared database, no synchronous lookups. The price? You can't revoke a JWT without a deny list. That's why you choose asymmetric signing (RS256 or ES256). Symmetric signing (HS256) forces every service to hold the same secret—one leak and your entire system is compromised. Asymmetric keys let you distribute trust. Your auth service holds the private key. Every other service only needs the public key. If one service gets pwned, the attacker still can't mint new tokens. This is not academic. I've cleaned up after teams that used HS256 across six services. Don't repeat that mistake.
tokenVerifier.jsNODE.JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// io.thecodeforge
const jwt = require('jsonwebtoken');
const fs = require('fs');
// In production, load from a secure vault, not the filesystem
const PUBLIC_KEY = fs.readFileSync('./keys/public.pem', 'utf8');
function verifyJwt(token) {
try {
const payload = jwt.verify(token, PUBLIC_KEY, {
algorithms: ['RS256']
});
return { valid: true, payload };
} catch (err) {
if (err.name === 'TokenExpiredError') {
return { valid: false, reason: 'expired' };
}
if (err.name === 'JsonWebTokenError') {
return { valid: false, reason: 'malformed_or_invalid_signature' };
}
return { valid: false, reason: 'unknown' };
}
}
// Usage: every microservice calls this on incoming requests
console.log(verifyJwt('some.jwt.here'));
Never use the same symmetric secret for multiple services. If you must use HS256, rotate the secret weekly and store it in a vault. Asymmetric keys eliminate the shared secret problem entirely.
🎯 Key Takeaway
Always use asymmetric signing (RS256/ES256) for multi-service architectures. Symmetric signing is a single point of failure.
Implementation in Node.js with Express: Where Most Teams Bleed
The code is the easy part. The mistakes are in the flow. Here's what a production JWT authentication middleware looks like. Notice what is missing: no database calls, no session stores. You check the token, verify the signature, and extract the user ID from the 'sub' claim. That's it. But here's the trap: you must set an 'exp' claim with a short expiration (15 minutes for access tokens, 7 days for refresh tokens). Do not set 'exp' to a year. I've seen tokens with a 5-year expiration in a SaaS product. The engineer thought it was 'convenient' for users. The attacker who stole that token thought it was 'convenient' too. The second trap: never trust the token's 'role' or 'isAdmin' claim without also verifying it against a backend store if the role changes at runtime. Otherwise, a user can change their role in the JWT if they forge the signature. Use 'sub' and look up roles per request if you need real-time authorization. The code below shows a secure, minimal Express middleware that follows this pattern. One job. One validation. No surprises.
authMiddleware.jsNODE.JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// io.thecodeforge
const jwt = require('jsonwebtoken');
// PUBLIC_KEY loaded from environment or vault
const PUBLIC_KEY = process.env.JWT_PUBLIC_KEY;
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing or malformed Authorization header' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] });
// Only trust the subject (user ID) from the token
req.userId = decoded.sub;
// Never trust roles from the token if they can change at runtime
// Look up roles from your database here if needed
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired. Please refresh.' });
}
return res.status(403).json({ error: 'Invalid token.' });
}
}
module.exports = authMiddleware;
Output
200 OK (on success)
401 Unauthorized (missing or expired token)
403 Forbidden (invalid signature)
⚠ Production Trap:
Set 'exp' to 15 minutes. Use a refresh token with a longer lifespan saved in an HTTP-only, Secure, SameSite=Strict cookie. Rotate refresh tokens on each use to limit damage if stolen.
🎯 Key Takeaway
Keep access tokens short-lived (15 minutes). Never embed mutable authorization data in the payload. Use the 'sub' claim as a stable user identifier.
● Production incidentPOST-MORTEMseverity: high
The Alg None Attack That Compromised 20+ Services
Symptom
Support tickets reported random users accessing admin endpoints without valid credentials.
Assumption
The team assumed the JWT library defaulted to rejecting unsigned tokens.
Root cause
The Node.js jsonwebtoken library (pre-v9) accepted 'none' algorithm when the public key was passed as a string. The attacker modified the JWT header to 'alg: none' and stripped the signature. The server parsed the token and trusted it.
Fix
Upgraded to jsonwebtoken v9 which rejects 'none' by default. Added explicit algorithm whitelist: { algorithms: ['RS256'] } in verification options. Conducted a token replay audit.
Key lesson
Never trust the JWT header to specify which algorithm to use — your code must enforce it.
The simplest attacks exploit what the spec allows but your security posture prohibits.
Third-party library defaults are not security promises; read the changelog for every major version.
Production debug guideSymptom → Action guide for the most common JWT issues4 entries
Symptom · 01
API returns 401 on every request right after login
→
Fix
Check token expiry (exp claim). If it's in the past, the token is already expired — regenerate with a longer iat-to-exp window.
Symptom · 02
Signature mismatch errors (InvalidSignatureError)
→
Fix
Verify the signing key: HS256 uses the same secret on issuer and consumer; RS256 uses private key to sign and public key to verify. A trailing newline or wrong encoding breaks it.
Symptom · 03
Token works on local dev but fails in staging
→
Fix
Check environment variables for the signing secret — often missing or different in staging. Use a secret manager, not hardcoded values.
Symptom · 04
Intermittent 401 after token refresh
→
Fix
Clock skew between servers. JWT libraries have a clockTolerance option (default 30s). If multiple services verify the same token with different system clocks, allow 60s tolerance.
★ JWT Debugging Cheat SheetCommands and checks to diagnose JWT issues in Java/Node.js environments
Token is rejected with 'exp' claim validation−
Immediate action
Decode token (base64url) and check the exp timestamp against current server time.
If kid is not in JWKS, the key was rotated. Request a new token or update the JWKS cache.
JWT Authentication vs Session-Based Authentication
Aspect
JWT (Stateless)
Session (Stateful)
Storage
Client stores token (localStorage / cookie)
Server stores session in database / Redis
Verification
Cryptographic signature (microseconds)
Database lookup on every request (1–10ms)
Revocation
Must wait for token expiry or maintain a blacklist
Immediate: delete session from store
Scalability
No shared session store; each server verifies independently
Requires shared session store (Redis) or sticky sessions
Security
Compromised token valid until expiry; payload visible (not encrypted)
Session ID is opaque; no payload exposure; immediate revocation
Best for
Microservices, mobile APIs, cross-domain auth
Monolith, server-rendered web apps, low-latency tolerance
⚙ Quick Reference
7 commands from this guide
File
Command / Code
Purpose
JwtExample.java
public class JwtExample {
What is JWT Authentication Flow?
JwtSigner.java
public class JwtSigner {
Token Creation
JwtVerifier.java
public class JwtVerifier {
How Verification Works without State
SecureJwtConfig.java
public class SecureJwtConfig {
Common JWT Vulnerabilities and How to Avoid Them
GatewayFilter.java
public class JwtGatewayFilter implements Filter {
JWT in Microservices
tokenVerifier.js
const jwt = require('jsonwebtoken');
Session Tokens vs. JWT
authMiddleware.js
const jwt = require('jsonwebtoken');
Implementation in Node.js with Express
Key takeaways
1
JWT enables stateless authentication
the server verifies a signed token without a database lookup.
2
The signature is the only guarantee of integrity
protect the signing key at all costs.
3
Short-lived access tokens (15 min) plus refresh token rotation solve the revocation gap.
4
Always whitelist algorithms in the parser
never trust the token's header.
5
Never store secrets in the JWT payload
it's base64, not encrypted.
6
Use asymmetric signing (RS256) in multi-service architectures for safe key distribution and rotation.
Common mistakes to avoid
5 patterns
×
Using a weak HMAC secret (less than 256 bits)
Symptom
Random users appear to have valid tokens; a brute-force attack recovers the secret.
Fix
Generate a cryptographically random key of at least 256 bits (e.g., using openssl rand -base64 32). Store in environment variable or secret manager.
×
Accepting the 'none' algorithm
Symptom
An attacker sends a JWT with alg: 'none' and empty signature — server grants access.
Fix
Explicitly whitelist allowed algorithms in the parser. In jjwt: Jwts.parserBuilder().requireAlg("RS256").... In jsonwebtoken (Node.js): { algorithms: ['RS256'] }.
×
Storing sensitive data in the JWT payload
Symptom
A data breach results in exposure of user PII or secrets because the payload is base64, not encrypted.
Fix
Never put passwords, credit card numbers, or other secrets in the JWT. If needed, use JWE (encrypted JWT) or store a reference ID in the payload and look up the data server-side.
×
Not verifying the token's issuer (iss) or audience (aud)
Symptom
A token from a different auth service (e.g., a web login token) is accepted by an API that expects mobile-only tokens.
Fix
Validate the iss and aud claims match your expected values. In jjwt: parserBuilder().requireIssuer("auth.myapp.com").requireAudience("api").
×
Using JWT as a session replacement without thinking about revocation
Symptom
After user logout, the token remains valid for hours — attacker with stolen token can impersonate.
Fix
Use short-lived access tokens (15 min) with a refresh token that can be revoked server-side. Implement refresh token rotation: issue a new refresh token on each refresh, invalidating the old one.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01JUNIOR
Explain the structure of a JWT. What are the three parts and how are the...
Q02SENIOR
What is the difference between HS256 and RS256? When would you choose on...
Q03SENIOR
How do you handle token revocation in a stateless JWT architecture?
Q04SENIOR
Explain the algorithm confusion vulnerability and how to prevent it.
Q01 of 04JUNIOR
Explain the structure of a JWT. What are the three parts and how are they composed?
ANSWER
A JWT consists of three base64url-encoded parts separated by dots: header, payload, signature. Header contains the signing algorithm (e.g. HS256, RS256) and token type (JWT). Payload contains registered claims (iss, sub, exp, iat, jti) and custom claims (e.g., roles, permissions). Signature is computed by combining the header and payload with a secret (HMAC) or private key (RSA/ECDSA) using the algorithm specified in the header. The signature ensures the token has not been tampered with.
Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjkr5pem9Bp8NVNw9gC.
Q02 of 04SENIOR
What is the difference between HS256 and RS256? When would you choose one over the other?
ANSWER
HS256 (HMAC with SHA-256) is a symmetric algorithm — the same secret key is used for both signing and verification. Fast, simple, but requires sharing the secret between issuer and verifier. RS256 (RSA with SHA-256) is asymmetric — uses a private key to sign and a public key to verify. The public key can be safely distributed to any verifier.
Choose HS256 when only one server or a tightly controlled network issues and verifies tokens (e.g., a single monolithic application). Choose RS256 when multiple services need to verify tokens without sharing a secret (e.g., microservices, third-party APIs). RS256 also simplifies key rotation: you can replace the private key without updating every verifier's config.
Q03 of 04SENIOR
How do you handle token revocation in a stateless JWT architecture?
ANSWER
You cannot revoke a stateless JWT without introducing state. The standard approach is to use short-lived access tokens (15 minutes) combined with a long-lived refresh token stored on the server (or in a database). When the user logs out, you revoke the refresh token. The access token expires naturally. For immediate revocation needs, you can maintain a blacklist of token IDs (jti) in Redis — but this adds latency and defeats statelessness.
Better: accept that you lose immediate revocation and rely on short expiry. Attackers exploit stolen tokens within the window — minimize that window. Also implement refresh token rotation: each time a refresh token is used, issue a new refresh token and invalidate the old one. This limits the impact of a leaked refresh token.
Q04 of 04SENIOR
Explain the algorithm confusion vulnerability and how to prevent it.
ANSWER
The algorithm confusion vulnerability occurs when a server accepts a token with the algorithm set by the attacker, then uses a public key (suitable for RS256) as the HMAC secret for HS256. Since the public key is public, the attacker can create a valid HS256 token using that public key as the secret. The server verifies it and accepts.
Prevention: never base your verification key on the token's header. Always whitelist accepted algorithms in the parser configuration. For example, in jjwt: parserBuilder().requireAlg("RS256"). In Node.js jsonwebtoken: pass { algorithms: ['RS256'] } to the verify call. Also ensure your JWKS endpoint only returns keys of the expected type (e.g., only RSA keys if you use RS256).
01
Explain the structure of a JWT. What are the three parts and how are they composed?
JUNIOR
02
What is the difference between HS256 and RS256? When would you choose one over the other?
SENIOR
03
How do you handle token revocation in a stateless JWT architecture?
SENIOR
04
Explain the algorithm confusion vulnerability and how to prevent it.
SENIOR
FAQ · 5 QUESTIONS
Frequently Asked Questions
01
What is a JWT and how does it work?
A JSON Web Token (JWT) is a compact, URL-safe token that encodes claims (user identity, permissions, metadata) in a JSON object. It is digitally signed so the receiving party can verify its authenticity. The flow: user logs in → server creates a JWT signed with a secret (or private key) → client stores the JWT → client sends it in the Authorization header with every request → server verifies the signature and extracts claims to authorize the request.
Was this helpful?
02
Is JWT secure?
JWT is secure when implemented correctly. The signature prevents tampering. However, the payload is only base64-encoded, not encrypted — anyone with the token can read it. Never put sensitive data (passwords, credit card numbers) in the payload. Also, a compromised signing key or accepting the 'none' algorithm breaks security. Use strong keys, whitelist algorithms, and short-lived tokens.
Was this helpful?
03
What is the difference between JWT and OAuth2?
JWT is a token format; OAuth2 is an authorization framework. OAuth2 specifies how to obtain and use tokens for delegated access, often using JWTs as the token format. JWT can be used standalone for authentication (login), while OAuth2 is about scoped access to resources. In practice, many OAuth2 implementations issue JWTs as access tokens.
Was this helpful?
04
How do I invalidate a JWT before it expires?
JWTs are stateless — you cannot invalidate them server-side without maintaining a blacklist. Best practice: set short expiry (15 minutes) and use a refresh token stored on the server. Revoke the refresh token on logout. For immediate revocation, maintain a Redis blacklist of token IDs (jti) but this adds state. Accept short expiry as your primary revocation mechanism.
Was this helpful?
05
Should I store JWT in localStorage or cookies?
Neither is perfect. localStorage is accessible via JavaScript (XSS vulnerability). Cookies with HttpOnly and Secure flags are safer against XSS but vulnerable to CSRF. If using cookies, ensure CSRF protection. A common production pattern: store the access token in memory (not persistent) and the refresh token in an HttpOnly cookie. That way, XSS cannot steal the access token (since it's in memory not localStorage), and CSRF protects the refresh endpoint.