Friday, August 21, 2026

JWT Authentication & Authorization — Complete Flow


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
Response

Now 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/orders

The 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/login

Request:

{
    "username": "mahesh",
    "password": "Password123"
}

The request reaches the Authentication API.


3. Step 1 — User sends credentials

The frontend sends:

Username
Password

to:

POST /api/auth/login

For example:

Angular Application
       |
       | username + password
       v
ASP.NET Core Web API

The 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 Unauthorized

If authentication succeeds:

UserId       = 101
Username     = mahesh
Role         = Customer

Now the API can create a JWT.


5. Step 3 — JWT is created

A JWT normally looks like this:

xxxxx.yyyyy.zzzzz

There are three parts:

HEADER.PAYLOAD.SIGNATURE

For 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:

HS256

or:

RS256

typ

This tells us the token type:

JWT

Conceptually:

HEADER
   |
   +-- Algorithm
   |
   +-- Token Type

7. 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:

ClaimMeaning
subSubject/User ID
nameUser name
emailEmail
roleUser role
issToken issuer
audIntended audience
iatIssued-at time
expExpiration time

For example:

{
   "sub": "101",
   "role": "Admin"
}

means:

User ID = 101
Role = Admin

Important 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 keys

inside 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.SIGNATURE

9. 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 Unauthorized

So 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
Frontend

For 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/orders

But 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 Token

Bearer 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             403

14. 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
Controller

15. 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-server

Audience

Is this token intended for my API?

Example:

my-ecommerce-api

Expiration

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 authenticated

Authentication establishes the user's identity.


17. Authorization

Authorization answers:

What are you allowed to do?

Suppose we have:

Admin
Customer
Manager

JWT:

{
    "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 Forbidden

18. 401 vs 403

This is another important interview question.

401 Unauthorized

Usually means:

Authentication failed / no valid authentication

Examples:

No token
Invalid token
Expired token
Invalid signature

Conceptually:

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 = Admin

Result:

403 Forbidden

Think:

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 Forbidden

20. 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 API

Don't confuse these two.


23. Complete E-Commerce Example

Let's imagine an e-commerce application.

User:

Mahesh
UserId = 101
Role = Customer

Step 1 — Login

POST /api/auth/login
{
   "username": "mahesh",
   "password": "********"
}

Step 2 — Database validation

Username exists?
        ↓
Password valid?
        ↓
Role = Customer
        ↓
YES

Step 3 — JWT creation

Payload:

{
   "sub": "101",
   "name": "Mahesh",
   "role": "Customer",
   "exp": "..."
}

JWT:

HEADER.PAYLOAD.SIGNATURE

Step 4 — JWT returned

Authentication API
       ↓
      JWT
       ↓
    Frontend

Step 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 valid

Step 7 — Authentication

User = Mahesh
UserId = 101
Role = Customer

Step 8 — Authorization

Suppose:

[Authorize]

Customer is allowed.

Therefore:

Controller executes

24. 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
       ↓
401

This 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 Unauthorized

The 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
Claims

without querying the user database on every request.

For example:

Request
   ↓
JWT
   ↓
Signature validation
   ↓
Claims
   ↓
Authorization
   ↓
Controller

However, 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 settings

28. 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
                    │
                    ▼
                 Response

29. 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/authenticity

30. 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:

Don't Copy

Protected by Copyscape Online Plagiarism Checker