JWT Authentication
JWT Authentication is one of the most important concepts in modern backend systems.
Especially in:
- Microservices
- REST APIs
- Cloud-native systems
- Mobile applications
- Distributed architectures
Most modern APIs today use JWT-based authentication.
Core Idea
Instead of storing user sessions on the server, the server issues a signed token to the client.
The client sends this token on every request.
Server verifies the token before allowing access.
High-Level JWT Flow
User Login
↓
Server Validates Credentials
↓
Server Generates JWT
↓
Client Stores JWT
↓
Client Sends JWT on Every Request
↓
Server Validates JWT
↓
Access Granted
Why JWT Became Popular
Traditional session-based systems require:
Server-side session storage
JWT enables:
Stateless Authentication
Very useful for:
- scalable systems
- distributed systems
- microservices
- cloud deployments
Traditional Session Authentication
Old-style systems work like this:
Login
↓
Server Creates Session
↓
Session ID Stored in Cookie
↓
Server Looks Up Session on Every Request
Requires:
- server memory
- session replication
- sticky sessions
Harder to scale.
JWT Authentication
JWT systems work differently.
Login
↓
Server Generates JWT
↓
Client Stores JWT
↓
Client Sends JWT
↓
Server Verifies Signature
No session lookup required.
JWT Structure
A JWT contains 3 parts:
HEADER.PAYLOAD.SIGNATURE
Example:
eyJhbGciOiJIUzI1Ni...
Visual Structure
HEADER
↓
PAYLOAD
↓
SIGNATURE
Separated using dots:
aaa.bbb.ccc
1. Header
Contains metadata about token.
Example:
{
"alg": "HS256",
"typ": "JWT"
}
Meaning:
- algorithm used
- token type
2. Payload
Contains claims/data.
Example:
{
"sub": "mohan",
"role": "ADMIN"
}
This contains identity information.
Important JWT Principle
Payload is:
ENCODED
NOT ENCRYPTED
Anyone can decode JWT payload.
NEVER store:
- passwords
- secrets
- sensitive personal data
inside JWT.
3. Signature
Most important security part.
Server creates signature using:
Header + Payload + Secret Key
Example:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Signature prevents token tampering.
MOST IMPORTANT JWT SECURITY PRINCIPLE
If payload changes:
Signature becomes invalid
This is how tampering is detected.
Complete JWT Authentication Flow
Now we connect JWT with a Spring Security filter chain.
POST /login
↓
UsernamePasswordAuthenticationFilter
↓
AuthenticationManager
↓
UserDetailsService
↓
Database Validation
↓
JWT Generated
↓
Client Stores Token
↓
Authorization: Bearer xxx
↓
JwtAuthenticationFilter
↓
SecurityContextHolder
↓
AuthorizationFilter
↓
Controller
This is the real Spring Security JWT architecture.
Step 1 — User Login Request
Suppose a client sends:
POST /login
Content-Type: application/json
Body:
{
"username": "mohan",
"password": "123"
}
What Happens Internally?
Client Request
↓
Spring Security Filter Chain
↓
UsernamePasswordAuthenticationFilter
↓
AuthenticationManager
↓
UserDetailsService
↓
Database Validation
Step 2 — AuthenticationManager
Spring Security delegates authentication to:
AuthenticationManager
This is the core authentication engine.
It verifies credentials.
Internal Authentication Flow
UsernamePasswordAuthenticationToken
↓
AuthenticationManager
↓
AuthenticationProvider
↓
UserDetailsService
↓
Load User From DB
↓
Password Validation
Step 3 — UserDetailsService
Usually:
@Service
public class CustomUserDetailsService
implements UserDetailsService{}
Purpose:
Load user from database
Example:
@Override
public UserDetails loadUserByUsername(String username) {
return userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException());
}
Step 4 — Password Validation
Spring Security compares:
Raw Password
vs
Encoded Password
using:
PasswordEncoder
Example:
BCryptPasswordEncoder
NEVER compare plain-text passwords manually.
Step 5 — Authentication Success
If credentials valid:
Spring creates:
Authentication authentication
Example:
UsernamePasswordAuthenticationToken
containing:
- user
- roles
- authorities
Step 6 — JWT Generation
NOW server generates JWT.
This is VERY important:
JWT generation happens AFTER successful authentication.
JWT is NOT authentication itself.
JWT Generation Flow
Authenticated User
↓
Extract Username
↓
Extract Roles
↓
Create Claims
↓
Generate JWT Signature
↓
Return Token
Example JWT Payload
{
"sub": "mohan",
"role": "ADMIN",
"iat": 1710000000,
"exp": 1710000900
}
Example JWT Service
Typical class:
@Service
public class JwtService{}
Responsibilities:
- generate token
- validate token
- extract claims
Simple JWT Generation Example
public String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.claim("role", "ADMIN")
.setIssuedAt(new Date())
.setExpiration(new Date(
System.currentTimeMillis() + 1000 * 60 * 15
))
.signWith(secretKey)
.compact();
}
Important JWT Claims
sub → username/user id
iat → issued at
exp → expiration
These are standard claims.
Step 7 — Server Returns JWT
Response:
{
"accessToken": "eyJhbGc..."
}
Step 8 — Client Stores JWT
Usually:
- memory
- secure cookie
- HttpOnly cookie
NOT recommended:
localStorage for sensitive apps
due to XSS risks.
Step 9 — Client Sends JWT
Every request includes:
Authorization: Bearer eyJhbGc...
This is called:
Bearer Authentication
Real Request Example
GET /api/orders
Authorization: Bearer eyJhbGc...
NOW the important part begins.
JWT Validation Flow
Incoming request:
Request
↓
Security Filter Chain
↓
JwtAuthenticationFilter
↓
Controller
JWT validation happens INSIDE filters.
MOST IMPORTANT JWT PRINCIPLE
JWT validation happens BEFORE controller execution.
Controllers should NEVER manually validate JWTs.
BAD:
if(tokenValid) {
...
}
inside controllers.
JWT Filter
In real systems:
public class JwtAuthenticationFilter
extends OncePerRequestFilter{}
Very common interview topic.
Why OncePerRequestFilter?
Ensures filter executes:
Exactly once per request
Perfect for JWT validation.
JWT Filter Responsibilities
The filter should:
Extract Token
↓
Validate Signature
↓
Check Expiration
↓
Extract Claims
↓
Load User
↓
Create Authentication
↓
Store in SecurityContextHolder
This is REAL enterprise flow.
Step 10 — Extract Authorization Header
Inside filter:
String authHeader =
request.getHeader("Authorization");
Step 11 — Check Bearer Token
if(authHeader == null ||
!authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
If no token exists:
continue request.
Step 12 — Extract JWT
String jwt = authHeader.substring(7);
Removes:
Bearer
prefix.
Step 13 — Extract Username
String username =
jwtService.extractUsername(jwt);
Step 14 — Validate JWT
Validation includes:
- signature
- expiration
- issuer
- audience
Example:
if(jwtService.isTokenValid(jwt, userDetails))
Important Security Principle
Never trust JWT payload directly.
ALWAYS validate:
Signature first
Step 15 — Load User
UserDetails userDetails =
userDetailsService.loadUserByUsername(username);
Why Load User Again?
Because:
JWT only proves identity.
But the latest user data may have changed:
- roles
- permissions
- account status
Very important enterprise concept.
Step 16 — Create Authentication Object
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
Step 17 — Store Authentication
Critical step:
SecurityContextHolder
.getContext()
.setAuthentication(auth);
NOW Spring Security considers user authenticated.
MOST IMPORTANT CONCEPT
SecurityContextHolder stores current authenticated user.
This is how Spring Security knows:
- who user is?
- roles
- permissions
Step 18 — Continue Filter Chain
filterChain.doFilter(request, response);
Request continues.
Next Step — Authorization
Now authorization filters check:
Does user have permission?
Example:
.hasRole("ADMIN")
Example Request Flow
Suppose:
GET /admin/users
Authorization: Bearer xyz
Internally
JWT Filter
↓
Token Validated
↓
Authentication Created
↓
SecurityContextHolder Updated
↓
Authorization Filter
↓
ROLE_ADMIN Check
↓
Controller Executes
What If JWT Invalid?
JWT filter rejects request.
Example:
401 Unauthorized
Controller NEVER executes.
What If Role Missing?
Authentication succeeds.
But authorization fails.
Example:
403 Forbidden
Very important distinction.
401 vs 403
| Status | Meaning |
|---|---|
| 401 | Not authenticated |
| 403 | Authenticated but not authorized |
Critical interview question.
JWT Filter Placement
Filter ordering matters.
JWT filter MUST execute before:
AuthorizationFilter
Otherwise, authorization has no authenticated user.
Typical Spring Security Configuration
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(
SessionCreationPolicy.STATELESS
)
)
Why STATELESS?
Because JWT systems do NOT use sessions.
Every request is independently authenticated.
Adding JWT Filter
Typical configuration:
http.addFilterBefore(
jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class
);
This is VERY important.
Why addFilterBefore?
Ensures JWT validation happens BEFORE:
UsernamePasswordAuthenticationFilter
and authorization processing.
Access Token vs Refresh Token
Modern production systems use BOTH.
Access Token
Short-lived.
Example:
15 minutes
Used for API access.
Refresh Token
Longer-lived.
Example:
7 days
Used to obtain new access tokens.
Refresh Flow
Access Token Expired
↓
Client Sends Refresh Token
↓
Server Validates Refresh Token
↓
New Access Token Generated
Very common enterprise architecture.
Important Industry Practices
1. Always Use HTTPS
JWT must NEVER travel over plain HTTP.
Otherwise, token theft becomes easy.
2. Keep Access Tokens Short-Lived
Good practice:
5–15 minutes
NOT:
24 hours
3. Do NOT Store Sensitive Data in JWT
JWT payload is readable.
Never store:
- passwords
- secrets
- personal data
4. Use Strong Algorithms
Enterprise standards:
RS256
ES256
Better than shared-secret systems.
5. Rotate Keys
Production systems rotate signing keys.
Usually using:
kid (Key ID)
6. Avoid localStorage for Sensitive Apps
Safer:
- HttpOnly cookies
- secure cookies
Very important security topic.
Common Beginner Mistakes
Mistake 1
Trying to authenticate inside controllers.
BAD:
if(tokenValid)
inside every endpoint.
Mistake 2
Not understanding filter order.
Causes:
- authentication failures
- SecurityContext issues
Mistake 3
Very long token expiration.
Huge security risk.
Mistake 4
Storing passwords in JWT payload.
Extremely dangerous.
Mistake 5
Not validating token signature.
Critical vulnerability.
Senior-Level Understanding
Senior engineers understand:
- JWT lifecycle
- filter-chain integration
- SecurityContext lifecycle
- stateless architecture
- access vs refresh tokens
- signing algorithms
- key rotation
- revocation strategies
- authorization flow
Not just token generation code.
Most Important Interview Question
“How does JWT authentication work internally in Spring Security?”
Strong answer:
User credentials are authenticated using AuthenticationManager.
After successful authentication, the server generates a signed JWT token.
For future requests, a JWT filter extracts and validates the token before controller execution.
The filter creates an Authentication object and stores it inside SecurityContextHolder.
Authorization filters then verify permissions before allowing access to controllers.
This is the REAL architecture.
Final Mental Model
Login Request
↓
AuthenticationManager
↓
JWT Generated
↓
Client Stores Token
↓
Authorization Header
↓
JwtAuthenticationFilter
↓
Token Validation
↓
SecurityContextHolder
↓
AuthorizationFilter
↓
Controller
If you deeply understand THIS flow, modern Spring Security becomes much easier.