ASP.NET Core  

How to Implement JWT Authentication with Refresh Tokens in ASP.NET Core

Introduction

Authentication is one of the most important parts of any application. Whether you are building a web app, mobile app, or API, you need a secure way to verify users.

JWT (JSON Web Token) is a popular method used in modern applications for authentication. But JWT alone is not enough for long-term security. That’s where Refresh Tokens come in.

In this guide, we will understand JWT and Refresh Tokens in very simple words and learn how to implement them step by step in an ASP.NET Core application.

What is JWT?

JWT (JSON Web Token) is a compact, secure way to send information between client and server.

It contains:

  • Header → Type of token

  • Payload → User data (claims)

  • Signature → Security verification

When a user logs in, the server generates a JWT and sends it to the client.

The client then sends this token in every request.

In simple words:
JWT is like a digital identity card for the user.

Why Use JWT Authentication?

  • Stateless (no server-side session)

  • Fast and scalable

  • Works well with APIs and microservices

  • Easy to integrate with frontend apps

Problem with JWT (Important)

JWT tokens usually have an expiration time.

Example:

  • Token valid for 15 minutes

After expiry:

  • User must login again ❌

This creates poor user experience.

What is a Refresh Token?

A Refresh Token is a long-lived token used to generate new JWT tokens.

Flow:

  • User logs in → gets Access Token + Refresh Token

  • Access Token expires

  • Refresh Token is used to get a new Access Token

In simple words:
Refresh Token helps users stay logged in without logging in again.

JWT vs Refresh Token

FeatureJWT (Access Token)Refresh Token
PurposeAccess APIGenerate new JWT
LifetimeShortLong
StorageClientSecure storage

Step-by-Step: Implement JWT Authentication in ASP.NET Core

Step 1: Create a .NET Project

dotnet new webapi -n JwtAuthDemo
cd JwtAuthDemo

Step 2: Install Required Packages

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Step 3: Configure JWT in appsettings.json

{
  "Jwt": {
    "Key": "ThisIsVerySecretKey12345",
    "Issuer": "yourapp",
    "Audience": "yourapp"
  }
}

Step 4: Configure Authentication in Program.cs

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "yourapp",
            ValidAudience = "yourapp",
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes("ThisIsVerySecretKey12345"))
        };
    });

builder.Services.AddAuthorization();

Step 5: Generate JWT Token

public string GenerateToken(string username)
{
    var claims = new[]
    {
        new Claim(ClaimTypes.Name, username)
    };

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("ThisIsVerySecretKey12345"));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

    var token = new JwtSecurityToken(
        issuer: "yourapp",
        audience: "yourapp",
        claims: claims,
        expires: DateTime.Now.AddMinutes(15),
        signingCredentials: creds);

    return new JwtSecurityTokenHandler().WriteToken(token);
}

Step 6: Create Login API

[HttpPost("login")]
public IActionResult Login(string username)
{
    var token = GenerateToken(username);
    return Ok(new { token });
}

Step 7: Protect API with [Authorize]

[Authorize]
[HttpGet("secure")]
public IActionResult Secure()
{
    return Ok("This is a protected API");
}

Step 8: Implement Refresh Token Logic

Create a model:

public class RefreshToken
{
    public string Token { get; set; }
    public DateTime ExpiryDate { get; set; }
}

Generate Refresh Token:

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

Step 9: Create Refresh Endpoint

[HttpPost("refresh")]
public IActionResult Refresh(string refreshToken)
{
    // Validate refresh token (from DB)

    var newAccessToken = GenerateToken("user");

    return Ok(new { token = newAccessToken });
}

Step 10: Best Practices for JWT Authentication

  • Keep JWT expiry short (10–15 mins)

  • Store refresh tokens securely (DB or HttpOnly cookies)

  • Use HTTPS always

  • Rotate refresh tokens

  • Revoke tokens on logout

Real-World Flow

  1. User logs in

  2. Server returns Access Token + Refresh Token

  3. User calls APIs using Access Token

  4. Token expires

  5. Refresh Token generates new Access Token

Conclusion

JWT with Refresh Tokens provides a secure and scalable authentication system for ASP.NET Core applications. It improves user experience while maintaining strong security.

Start with basic JWT authentication, then enhance it with refresh tokens, secure storage, and token rotation to build production-ready systems.