In the previous article, we successfully generated a JWT and returned it to the client after login. At this point, the client has an access token that represents the authenticated user.

But there's one important question:

How does the API know whether the received token is genuine or fake?

Simply receiving a JWT does not mean the API should trust it. Anyone can copy a token, modify it, or even create a fake one. Before executing a protected endpoint, ASP.NET Core must validate the token and ensure it was issued by a trusted source.

In this article, you'll learn how JWT validation works internally and how to configure JWT Bearer Authentication in an ASP.NET Core Web API.

How ASP.NET Core Validates JWT Tokens Behind the Scenes

What Is JWT Validation?

JWT validation is the process of verifying whether an incoming token is authentic, valid, and trustworthy before allowing access to a protected API.

Think of a movie ticket.

When you enter a cinema, the security staff doesn't just check whether you're holding a ticket. They verify:

Only after these checks are completed are you allowed to enter.

JWT validation follows exactly the same idea.

Why Is Token Validation Necessary?

Suppose someone sends the following request:

GET /api/orders
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

The API cannot simply trust this token because:

Without validation, anyone could gain unauthorized access to your APIs.

How JWT Validation Works

Whenever a protected endpoint receives a request, ASP.NET Core automatically performs several validation steps.

HTTP Request
      │
      ▼
Read Authorization Header
      │
      ▼
Extract JWT
      │
      ▼
Validate Token
      │
      ├── Signature
      ├── Expiration
      ├── Issuer
      ├── Audience
      │
      ▼
Create User Identity
      │
      ▼
Execute Controller

If any validation fails, the request stops immediately.

The controller action is never executed.

Step 1: Install JWT Bearer Authentication

If you haven't already installed the package, run:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

This package provides the middleware responsible for reading and validating JWT tokens.

Step 2: Register JWT Authentication

Open Program.cs.

Add the required namespaces.

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

Now register JWT authentication.

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters =
            new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,

                ValidIssuer = builder.Configuration["Jwt:Issuer"],
                ValidAudience = builder.Configuration["Jwt:Audience"],

                IssuerSigningKey =
                    new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(
                            builder.Configuration["Jwt:Key"]!))
            };
    });

This tells ASP.NET Core how every incoming JWT should be validated.

Understanding TokenValidationParameters

This class contains all the validation rules.

Let's understand each property.

ValidateIssuer

ValidateIssuer = true

This verifies that the token was generated by the expected application.

If another application creates a token, ASP.NET Core rejects it.

ValidateAudience

ValidateAudience = true

Every JWT is created for a particular audience.

This check ensures the token belongs to your application.

ValidateLifetime

ValidateLifetime = true

Every JWT has an expiration time.

If the token has expired, the request is rejected.

Expired tokens should never be accepted.

ValidateIssuerSigningKey

ValidateIssuerSigningKey = true

This is one of the most important checks.

ASP.NET Core verifies the digital signature using the secret signing key.

If someone modifies even one character inside the token, validation fails.

IssuerSigningKey

IssuerSigningKey =
    new SymmetricSecurityKey(...)

This is the same secret key that was used when generating the JWT.

If the signing key doesn't match, the token cannot be trusted.

Step 3: Enable Authentication Middleware

Registering authentication services is not enough.

The middleware must also be added to the request pipeline.

app.UseAuthentication();
 app.UseAuthorization();

The order is extremely important.

Authentication must always execute before authorization.

Otherwise, ASP.NET Core won't know who the current user is.

Internal Request Flow

Now let's see what actually happens when a request reaches the API.

Client
   │
   │ Bearer Token
   ▼
Authentication Middleware
   │
   ▼
Read Authorization Header
   │
   ▼
Extract JWT
   │
   ▼
Validate Signature
   │
   ▼
Validate Expiration
   │
   ▼
Validate Issuer
   │
   ▼
Validate Audience
   │
   ▼
Authenticated User Created
   │
   ▼
Authorization Middleware
   │
   ▼
Controller Action

Notice something important.

Your controller never validates the JWT manually.

ASP.NET Core completes all validation before your controller is executed.

Practical Example

Let's continue with the SecureShop API.

A customer sends the following request:

GET /api/orders

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

ASP.NET Core performs these checks:

If all answers are Yes, the request continues.

Otherwise, the API immediately returns:

401 Unauthorized

The controller action is never called.

What Happens After Successful Validation?

Once validation succeeds, ASP.NET Core creates an authenticated user internally.

Conceptually, the flow becomes:

JWT
   │
   ▼
Claims
   │
   ▼
Claims Identity
   │
   ▼
Claims Principal
   │
   ▼
HttpContext.User

From this point onward, your controllers can access the logged-in user's information using:

User.Identity

or

User.Claims

We'll use these in upcoming articles when protecting endpoints.

Common Mistakes

Mistake 1: Forgetting UseAuthentication()

Some developers configure JWT authentication but forget to add:

app.UseAuthentication();

Without this middleware, ASP.NET Core never validates incoming tokens.

Mistake 2: Incorrect Middleware Order

This is incorrect:

app.UseAuthorization();
 app.UseAuthentication();

Always write:

app.UseAuthentication();
 app.UseAuthorization();

Authentication identifies the user.

Authorization decides whether the identified user has permission to access the resource.

Mistake 3: Using Different Secret Keys

The secret key used to generate the JWT must be the same key used during validation.

If the keys differ, every request will fail validation.

Mistake 4: Disabling Validation Checks

Some beginners write:

ValidateLifetime = false;

or

ValidateIssuer = false;

This may make testing easier, but it significantly reduces security.

In production, always enable the required validation checks.

Key Takeaways

Now our API can generate JWTs and validate incoming tokens. The next step is to protect API endpoints using [Authorize], allowing only authenticated users to access specific resources while keeping other endpoints public.