In the previous article, we integrated ASP.NET Core Identity Roles with JWT authentication. Our SecureShop API now generates JWTs containing user roles, enabling Role-Based Authorization with attributes like [Authorize(Roles = "Admin")].

Although our authentication system is now production-ready, there is still one usability problem.

JWT access tokens usually have a short expiration time (for example, 15–30 minutes).

When the token expires, the user is forced to log in again.

This creates a poor user experience.

The solution is Refresh Token Authentication.

In this article, you'll learn what refresh tokens are, why they are needed, and how they work alongside JWT access tokens.

Implement Refresh Token Authentication in ASP.NET Core Web API

What Is a Refresh Token?

A Refresh Token is a long-lived token used to request a new JWT Access Token without asking the user to log in again.

Unlike an access token:

The access token continues to protect API endpoints, while the refresh token keeps the user signed in.


Why Do We Need Refresh Tokens?

Let's continue with our SecureShop API.

Suppose John logs in.

The API issues:

John continues shopping.

After 30 minutes:

John continues using the application without interruption.

Access Token vs Refresh Token

Access TokenRefresh Token
Short lifetimeLong lifetime
Sent with every API requestUsed only to obtain a new access token
Contains user claimsUsually contains a random value
Protects API endpointsRenews authentication

Each token has a different purpose.

Authentication Flow with Refresh Tokens

The complete flow looks like this.

User Login
    │
    ▼
Validate Credentials
    │
    ▼
Generate Access Token
Generate Refresh Token
    │
    ▼
Client Stores Both Tokens

Later, when the access token expires:

Expired Access Token
        │
        ▼
Send Refresh Token
        │
        ▼
Validate Refresh Token
        │
        ▼
Generate New Access Token
        │
        ▼
Return New Tokens

The user does not need to enter their username and password again.

Step 1: Generate a Refresh Token

Unlike JWTs, refresh tokens are usually random strings.

Example:

public static string GenerateRefreshToken()
{
    return Convert.ToBase64String(
        RandomNumberGenerator.GetBytes(64));
}

Each login creates a unique refresh token.

Step 2: Store the Refresh Token

After generating it, save the refresh token in the database.

For example:

UserRefresh TokenExpiry
JohnXy82...Ab97 Days

Storing it allows the server to verify that the token is still valid.

Step 3: Return Both Tokens

After a successful login, return both tokens.

{
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "refreshToken": "Xy82GhL9..."
}

The client stores both values securely.

Step 4: Create a Refresh Endpoint

Create a new endpoint.

POST /api/auth/refresh

The client sends the refresh token.

{
    "refreshToken": "Xy82GhL9..."
}

The API validates the refresh token.

If valid, it generates a new access token.

Internal Refresh Flow

Client
   │
   │ Refresh Token
   ▼
Refresh Endpoint
   │
   ▼
Database
   │
   ▼
Refresh Token Valid?
      │
 Yes  │ No
      │
      ▼
Generate New JWT
      │
      ▼
Return New Tokens

If the refresh token has expired or is invalid, the API returns 401 Unauthorized, and the user must log in again.

Practical Example

Let's continue with the SecureShop API.

John logs in.

The API returns:

Access Token
 Expires : 30 Minutes

 Refresh Token
 Expires : 7 Days

Thirty minutes later:

John calls:

POST /api/auth/refresh

The API verifies the refresh token stored in the database.

Token is valid.

A new JWT access token is generated.

John continues using the application without logging in again.

Security Best Practices

When implementing refresh tokens:

These practices help reduce the impact of a stolen refresh token.

Common Mistakes

Mistake 1: Using Long-Lived JWTs Instead of Refresh Tokens

Making JWTs valid for several months increases security risks.

Keep access tokens short-lived and use refresh tokens for renewal.

Mistake 2: Not Storing Refresh Tokens

If the server doesn't store refresh tokens, it cannot revoke or validate them.

Always persist refresh tokens.

Mistake 3: Reusing the Same Refresh Token Forever

A refresh token should be replaced with a new one after successful use.

This is called Refresh Token Rotation and improves security.

Mistake 4: Forgetting to Remove Refresh Tokens on Logout

When a user logs out, invalidate or delete their refresh token so it cannot be used again.

Key Takeaways

Our SecureShop API now provides a smoother and more secure authentication experience using Refresh Tokens. In the next article, we'll implement Logout and Refresh Token Revocation, ensuring that refresh tokens become invalid immediately after a user signs out or when an administrator revokes access.