Imagine the application has:
Angular / React frontend
ASP.NET Core Web API
SQL Server database
JWT-based authentication
The overall flow is:
User
|
| 1. Login: username + password
v
Frontend
|
| 2. POST /api/auth/login
v
Web API
|
| 3. Validate credentials
v
Database
|
| 4. User is valid
v
Web API
|
| 5. Create JWT
v
Frontend
|
| 6. Store JWT
|
| 7. Send JWT in Authorization Header
v
Web API
|
| 8. Validate JWT
|
| 9. Authentication
|
| 10. Authorization
v
Controller
|
v
ResponseNow let's understand every step.
1. What problem does JWT solve?
Suppose a user logs into your application.
Username: mahesh
Password: ********The API verifies the username and password.
But after login, the API needs to know:
"Who is making this next request?"
For example:
GET /api/ordersThe API needs to know:
Which user?
Is the user authenticated?
What roles does the user have?
Is the user allowed to access orders?JWT provides a way for the client to prove its identity on subsequent requests.
2. Where does the JWT flow start?
The JWT authentication flow starts when the user performs login.
For example:
POST /api/auth/loginRequest:
{
"username": "mahesh",
"password": "Password123"
}The request reaches the Authentication API.
3. Step 1 — User sends credentials
The frontend sends:
Username
Passwordto:
POST /api/auth/loginFor example:
Angular Application
|
| username + password
v
ASP.NET Core Web APIThe password should be transmitted over HTTPS, not plain HTTP.
4. Step 2 — API validates the user
The API receives the credentials.
It queries the database:
SELECT Id, UserName, PasswordHash, Role
FROM Users
WHERE UserName = 'mahesh'The application should compare the supplied password against the stored password hash.
It should not store plain-text passwords.
If authentication fails:
401 UnauthorizedIf authentication succeeds:
UserId = 101
Username = mahesh
Role = CustomerNow the API can create a JWT.
5. Step 3 — JWT is created
A JWT normally looks like this:
xxxxx.yyyyy.zzzzzThere are three parts:
HEADER.PAYLOAD.SIGNATUREFor example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMDEiLCJyb2xlIjoiQ3VzdG9tZXIifQ
.
abc123xyz...These three parts have different responsibilities.
6. JWT Header
The header contains information about the token.
Example:
{
"alg": "HS256",
"typ": "JWT"
}alg
This tells the receiver which signing algorithm is being used.
For example:
HS256or:
RS256typ
This tells us the token type:
JWTConceptually:
HEADER
|
+-- Algorithm
|
+-- Token Type7. JWT Payload
The payload contains claims.
For example:
{
"sub": "101",
"name": "Mahesh",
"role": "Customer",
"email": "mahesh@example.com",
"exp": 1787220000
}These are called claims.
Common claims include:
| Claim | Meaning |
|---|---|
sub | Subject/User ID |
name | User name |
email | |
role | User role |
iss | Token issuer |
aud | Intended audience |
iat | Issued-at time |
exp | Expiration time |
For example:
{
"sub": "101",
"role": "Admin"
}means:
User ID = 101
Role = AdminImportant security point
The JWT payload is encoded, not encrypted, in a normal JWT.
Therefore, don't put sensitive information such as:
Password
Credit card number
Secret keysinside the payload.
8. JWT Signature
This is the most important part for understanding JWT security.
Conceptually, the server takes:
Base64Url(Header)
+
"."
+
Base64Url(Payload)and signs that data using a secret/private key.
For example, conceptually with HMAC:
Signature =
HMACSHA256(
Base64Url(Header) + "." +
Base64Url(Payload),
SecretKey
)The result becomes:
HEADER.PAYLOAD.SIGNATURE9. Why do we need the Signature?
Imagine the original payload is:
{
"userId": 101,
"role": "Customer"
}An attacker might try to change it to:
{
"userId": 101,
"role": "Admin"
}But the attacker doesn't have the signing secret/private key.
Therefore, they cannot generate a valid signature for the modified payload.
When the API receives the token, it validates the signature.
If the token was modified:
Payload changed
↓
Signature no longer matches
↓
JWT validation fails
↓
401 UnauthorizedSo the signature provides integrity/authenticity of the token, assuming the signing key is properly protected.
10. JWT is returned to the client
After successful login:
Web API
|
| JWT
v
FrontendFor example:
{
"accessToken": "eyJhbGciOiJIUzI1NiIs..."
}Now the frontend has the access token.
11. What happens on the next API request?
Suppose the user wants to see orders.
Frontend sends:
GET /api/ordersBut how does the API know who the user is?
The frontend sends the JWT using the HTTP Authorization header.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...This is extremely important.
12. What is the Authorization Header?
The HTTP request looks like:
GET /api/orders HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...There are two important pieces:
Authorization
|
+-- Bearer
|
+-- JWT TokenBearer essentially means:
"The caller is presenting this access token as its credential."
13. Complete request flow
Now we can visualize the entire process:
LOGIN
|
v
+---------------+
| Frontend |
+---------------+
|
| username/password
v
+---------------+
| Auth API |
+---------------+
|
| Validate credentials
v
+---------------+
| Database |
+---------------+
|
| User valid
v
+---------------+
| Auth API |
+---------------+
|
| Create JWT
v
+-------------------------+
| HEADER.PAYLOAD.SIGNATURE|
+-------------------------+
|
| JWT
v
+---------------+
| Frontend |
+---------------+Then:
API REQUEST
|
v
+-------------+
| Frontend |
+-------------+
|
| Authorization:
| Bearer JWT
v
+-------------+
| Web API |
+-------------+
|
v
JWT Middleware
|
+-------+-------+
| |
Invalid Valid
| |
v v
401 User.Identity
created
|
v
Authorization
|
+---------+---------+
| |
Allowed Denied
| |
v v
Controller 40314. What happens inside ASP.NET Core?
Suppose we have:
[Authorize]
[HttpGet("orders")]
public IActionResult GetOrders()
{
return Ok();
}The request arrives:
GET /api/orders
Authorization: Bearer <JWT>ASP.NET Core's JWT authentication middleware processes the token before the controller action executes.
Conceptually:
HTTP Request
|
v
ASP.NET Core Middleware
|
v
JWT Authentication Handler
|
v
Read Authorization Header
|
v
Extract Bearer Token
|
v
Validate JWT
|
v
Create ClaimsPrincipal
|
v
Authorization
|
v
Controller15. JWT Validation
The API validates several things depending on its configuration.
For example:
Signature
Is the signature valid?Issuer
Who issued this token?Example:
https://my-auth-serverAudience
Is this token intended for my API?Example:
my-ecommerce-apiExpiration
Has the token expired?For example:
{
"exp": 1787220000
}If the current time is beyond the expiration time, the token is rejected.
16. Authentication vs Authorization
This is one of the most important interview questions.
Authentication
Authentication answers:
Who are you?
Example:
User logs in
↓
Username/password validated
↓
JWT issued
↓
JWT presented to API
↓
API validates JWT
↓
User is authenticatedAuthentication establishes the user's identity.
17. Authorization
Authorization answers:
What are you allowed to do?
Suppose we have:
Admin
Customer
ManagerJWT:
{
"sub": "101",
"role": "Customer"
}Then:
[Authorize]means:
An authenticated user can access this endpoint.
But:
[Authorize(Roles = "Admin")]means:
Only authenticated users with the Admin role can access this endpoint.
If a Customer calls it:
Authenticated? YES
Authorized? NO
Result:
403 Forbidden18. 401 vs 403
This is another important interview question.
401 Unauthorized
Usually means:
Authentication failed / no valid authenticationExamples:
No token
Invalid token
Expired token
Invalid signatureConceptually:
Who are you?
→ I can't authenticate you.403 Forbidden
Means:
You are authenticated,
but you don't have permission.Example:
User = Customer
Endpoint requires = AdminResult:
403 ForbiddenThink:
401 = I don't know who you are.
403 = I know who you are,
but you're not allowed.19. Authentication + Authorization Example
Suppose:
[Authorize]
[HttpGet("profile")]
public IActionResult Profile()
{
return Ok();
}Any authenticated user can access it.
But:
[Authorize(Roles = "Admin")]
[HttpDelete("users/{id}")]
public IActionResult DeleteUser(int id)
{
return Ok();
}Only Admin users can access it.
The flow becomes:
JWT
|
v
Signature validation
|
v
Token valid?
|
+---- NO ----> 401
|
YES
|
v
Authentication successful
|
v
Read Claims
|
v
Role = Customer?
|
v
Endpoint requires Admin
|
v
Authorization fails
|
v
403 Forbidden20. Where does the Role come from?
The role can be included as a JWT claim.
Example:
{
"sub": "101",
"name": "Mahesh",
"role": "Admin"
}ASP.NET Core converts JWT claims into a ClaimsPrincipal.
You can then access claims:
var userId = User.FindFirst("sub")?.Value;or:
var role = User.FindFirst("role")?.Value;Depending on configuration, role claims can also be accessed through:
User.IsInRole("Admin")21. How does ASP.NET Core know which JWT to validate?
You configure JWT authentication in the application.
Conceptually:
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "...",
ValidAudience = "...",
IssuerSigningKey = ...
};
});And:
app.UseAuthentication();
app.UseAuthorization();The order is important:
UseAuthentication()
↓
UseAuthorization()
↓
MapControllers()Authentication must establish the user's identity before authorization makes the access decision.
22. Why is the Header important?
There are actually two places where "header" can mean different things.
JWT Header
Inside the JWT:
{
"alg": "HS256",
"typ": "JWT"
}This describes the token.
HTTP Authorization Header
Outside the JWT:
Authorization: Bearer <token>This transports the JWT from the client to the API.
So:
JWT Header
↓
Describes JWT
HTTP Authorization Header
↓
Carries JWT to APIDon't confuse these two.
23. Complete E-Commerce Example
Let's imagine an e-commerce application.
User:
Mahesh
UserId = 101
Role = CustomerStep 1 — Login
POST /api/auth/login{
"username": "mahesh",
"password": "********"
}Step 2 — Database validation
Username exists?
↓
Password valid?
↓
Role = Customer
↓
YESStep 3 — JWT creation
Payload:
{
"sub": "101",
"name": "Mahesh",
"role": "Customer",
"exp": "..."
}JWT:
HEADER.PAYLOAD.SIGNATUREStep 4 — JWT returned
Authentication API
↓
JWT
↓
FrontendStep 5 — Get orders
GET /api/orders
Authorization: Bearer <JWT>Step 6 — API validates JWT
Token exists?
↓
Signature valid?
↓
Issuer valid?
↓
Audience valid?
↓
Token expired?
↓
All validStep 7 — Authentication
User = Mahesh
UserId = 101
Role = CustomerStep 8 — Authorization
Suppose:
[Authorize]Customer is allowed.
Therefore:
Controller executes24. What if the JWT is modified?
Original:
{
"userId": 101,
"role": "Customer"
}Attacker changes it:
{
"userId": 101,
"role": "Admin"
}The attacker doesn't have the signing key.
Therefore:
Modified Payload
↓
Signature doesn't match
↓
JWT validation fails
↓
Authentication fails
↓
401This is why the signature is critical.
25. What if the JWT is expired?
Suppose:
{
"sub": "101",
"exp": 1787220000
}The API checks:
Current time > exp?If yes:
Token expired
↓
Authentication fails
↓
401 UnauthorizedThe frontend can then obtain a new access token using an appropriate token renewal mechanism, such as a refresh-token flow, depending on the authentication architecture.
26. Does every API call query the User table?
Not necessarily.
That's one of the major benefits of JWT.
For a properly configured self-contained JWT, the API can validate:
Signature
Issuer
Audience
Expiration
Claimswithout querying the user database on every request.
For example:
Request
↓
JWT
↓
Signature validation
↓
Claims
↓
Authorization
↓
ControllerHowever, applications sometimes still consult a database/cache for things such as account status, revocation, permissions that must change immediately, or other business rules.
27. Where is the JWT stored?
This depends on the frontend architecture and security requirements.
A common browser approach is to use secure cookie-based mechanisms, especially when designed to mitigate token theft/XSS risks.
Another approach is storing an access token in browser storage, but storing long-lived authentication tokens in localStorage has important security trade-offs because JavaScript can access it.
For a production system, token storage should be designed together with:
HTTPS
XSS protection
CSRF protection
Token lifetime
Refresh-token strategy
Cookie settings28. JWT Flow — Start to End
Here's the complete flow you can remember for interviews:
┌──────────────┐
│ USER │
└──────┬───────┘
│
│ Login
▼
┌──────────────┐
│ FRONTEND │
└──────┬───────┘
│
│ username/password
▼
┌──────────────┐
│ AUTH API │
└──────┬───────┘
│
│ Validate
▼
┌──────────────┐
│ DATABASE │
└──────┬───────┘
│
│ Valid
▼
┌──────────────┐
│ JWT CREATE │
└──────┬───────┘
│
│
HEADER.PAYLOAD
.SIGNATURE
│
▼
┌──────────────┐
│ FRONTEND │
└──────┬───────┘
│
│ Authorization:
│ Bearer JWT
▼
┌──────────────┐
│ WEB API │
└──────┬───────┘
│
▼
JWT VALIDATION
│
┌──────────┴──────────┐
│ │
Invalid Valid
│ │
▼ ▼
401 Authentication
│
▼
Authorization
│
┌─────────────┴────────────┐
│ │
Allowed Denied
│ │
▼ ▼
Controller 403
│
▼
Response29. The most important distinction
Remember these three concepts:
Header
What algorithm/type is this JWT using?Payload
Who is the user?
What claims/attributes are associated with the token?Signature
Has the token been altered,
and can it be validated using the expected signing key?And remember:
HTTP Authorization Header
↓
Carries JWT
JWT Header
↓
Describes JWT
JWT Payload
↓
Contains Claims
JWT Signature
↓
Protects token integrity/authenticity30. One-line interview answer
If an interviewer asks:
"Explain JWT authentication flow."
A strong answer is:
"When a user logs in, the authentication service validates the credentials and creates a signed JWT containing claims such as user ID, issuer, audience, role and expiration. The client then sends that token with subsequent API requests in the HTTP Authorization header using the Bearer scheme. ASP.NET Core's JWT authentication middleware extracts and validates the token's signature, issuer, audience and lifetime and creates the authenticated ClaimsPrincipal. The authorization middleware then evaluates policies or roles, such as
[Authorize(Roles = "Admin")]. If authentication fails, the API returns 401; if authentication succeeds but authorization fails, it returns 403. If both succeed, the request reaches the controller."
That is the complete JWT authentication → authorization flow from login to API response.

No comments:
Post a Comment