In the previous article, we learned that after a successful login, an ASP.NET Core Web API generates a JWT and sends it to the client. The client then includes that token with every protected request.

But have you ever wondered what is actually inside that long string?

Many developers use JWT every day without understanding its structure. They know how to generate a token, but when authentication fails or a token is rejected, troubleshooting becomes difficult.

To use JWT confidently, you should first understand how it is built internally.

What Is Inside a JWT?

A JWT (JSON Web Token) is simply a string made up of three parts separated by dots (.).

Header.Payload.Signature

A real JWT looks something like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMDEiLCJlbWFpbCI6ImFqYXlAZXhhbXBsZS5jb20ifQ
.
N2xvTnB6a3Q4T0RtRzN...

Each section has a different responsibility.

PartResponsibility
HeaderDescribes the token
PayloadContains user information (claims)
SignatureProtects the token from modification

Think of a JWT like an official ID card.

Why Does JWT Have Three Parts?

Imagine a university issues student ID cards.

The card contains:

If someone changes the student name or ID number, the security seal no longer matches.

JWT works in a similar way.

The first two parts store information, while the third part protects that information from unauthorized changes.

Part 1: Header

The Header contains metadata about the token.

A decoded header usually looks like this:

{
  "alg": "HS256",
  "typ": "JWT"
}

Let's understand these properties.

PropertyMeaning
algAlgorithm used to sign the token
typToken type

The value:

HS256

means the token is signed using the HMAC SHA-256 algorithm.

The header is small because its only job is to describe the token.

It does not contain user information.

Part 2: Payload

The Payload is the most important section of a JWT.

It contains information about the authenticated user.

Example:

{
  "sub": "101",
  "name": "Ajay",
  "email": "[email protected]",
  "role": "Customer",
  "exp": 1784500000
}

The values stored inside the payload are called Claims.

A claim is simply a piece of information about the authenticated user.

For example:

The payload tells the API who the user is.

Understanding Claims

Claims are small pieces of identity information stored inside the JWT.

There are three common types of claims.

Registered Claims

These are standard claim names defined by the JWT specification.

Some common examples are:

ClaimPurpose
subUser identifier
issToken issuer
audIntended audience
expExpiration time
iatIssued time
jtiUnique token identifier

Using standard claims makes tokens easier for different systems to understand.

Public Claims

These are commonly agreed claim names used by applications.

Example:

{
    "email": "[email protected]"
}

Private Claims

Private claims are created by your own application.

Example:

{
    "membership": "Gold"
}

These claims are useful when your application needs additional information that isn't part of the standard specification.

Can Anyone Read the Payload?

Yes.

This is one of the biggest misconceptions about JWT.

The payload is Base64Url encoded, not encrypted.

That means anyone who has the token can decode it and view its contents.

For this reason, never store sensitive information inside the payload.

Avoid storing:

Only include information that is necessary for authentication and authorization.

Part 3: Signature

The Signature is the security mechanism of JWT.

Its purpose is to detect whether someone has modified the token.

Conceptually, it is created like this:

HMACSHA256(
    Header + Payload,
    SecretKey
)

Don't worry about the implementation yet.

For now, remember one important point:

The signature depends on both the token content and the secret signing key.

If either one changes, the signature also changes.

Internal Flow of JWT Validation

Suppose a client sends this request:

GET /api/profile

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Before executing the API, ASP.NET Core performs a series of checks.

Receive JWT
      ↓
Read Header
      ↓
Read Payload
      ↓
Recalculate Signature
      ↓
Compare Signatures
      ↓
Validate Expiration
      ↓
Validate Issuer
      ↓
Validate Audience
      ↓
Request Accepted

If any validation fails, the request is rejected.

Practical Example

Let's continue with our SecureShop API.

A customer logs in successfully.

The API returns a JWT.

The decoded payload looks like this:

{
    "sub": "101",
    "name": "John",
    "email": "[email protected]",
    "role": "Customer"
}

Later, the customer requests:

GET /api/orders

The JWT is sent with the request.

The API reads the payload to identify the user and validates the signature to ensure the token hasn't been modified.

Only after successful validation does the API continue processing the request.

Notice that the user's password is not included in the token.

The password is used only during login.

What Happens If Someone Changes the Token?

Imagine an attacker changes this payload:

{
    "role": "Customer"
}

to:

{
    "role": "Admin"
}

The payload now looks different.

However, the attacker cannot generate a valid signature because they don't have the secret signing key.

When the API validates the token, it recalculates the signature.

The signatures no longer match.

Result:

Token Validation Failed

The request is rejected immediately.

This is why modifying a JWT manually does not grant additional permissions.

Common Mistakes

Mistake 1: Thinking Base64 Encoding Means Encryption

Encoding and encryption are completely different.

Base64 encoding only changes the format of the data.

It does not hide the data.

Anyone can decode a JWT payload.

Mistake 2: Storing Sensitive Data

Sometimes developers store confidential information inside JWT claims.

For example:

{
    "password": "Admin@123"
}

or

{
    "creditCard": "4111111111111111"
}

This is a serious security mistake.

JWT should contain only the information required for identifying and authorizing the user.

Mistake 3: Assuming the Signature Encrypts the Token

The signature does not encrypt the payload.

Its purpose is to verify integrity.

It answers one question:

"Has this token been modified after it was issued?"

Key Takeaways

Now that you understand what a JWT looks like internally, the next step is learning how it travels through an ASP.NET Core Web API. We'll follow the complete authentication journey—from user login to accessing a protected endpoint—and see where JWT fits into the request pipeline.