Backend and APIs

Managing JWT Authentication in a .NET API: Complete Tutorial

A complete tutorial for implementing JWT authentication in a .NET API.

Ahmed Oumezzine Ahmed Oumezzine 6 min read
  • Table of contents unavailable
Managing JWT Authentication in a .NET API: Complete Tutorial

Introduction

"What if you could protect your API like the pros… without being an expert?"

You have just created your first .NET project, your API is running, but… how do you stop just anyone from accessing it?

What if I told you that you can secure your application using the same general principles used by major platforms, without maintaining server-side session state or adding unnecessary complexity?

The answer is JWT — or JSON Web Token. And today, I will walk you through setting up robust authentication in your .NET API, step by step, even if you are just getting started.

No unnecessary jargon. No black magic. Just you, your keyboard, and an approach used by thousands of developers every day.

What Is a JWT? A Simple Metaphor

Imagine you are going to a festival. At the entrance, you receive an RFID wristband. That wristband contains:

  • Your name
  • An expiration date
  • An invisible signature, like a hologram

From that moment on, you do not need to show your ticket at every stand. Staff scan your wristband, verify that it is valid, and let you through.

👉 That is roughly what a JWT does.

  • Header: the token type and how it was signed.
  • Payload: the claims inside the token, such as identity and expiration information.
  • Signature: proof that the token has not been altered and was signed by a trusted issuer.

And the useful part? The server can validate the token without storing a traditional session for every request. It reads the token, validates the signature and claims, and decides whether access should be allowed.

💬 "But… is that secure?"
Yes, when it is configured correctly and the signing key is properly protected. Think of that key as a highly sensitive secret known only to trusted server-side components.

Step 1: Create Your .NET API — Even If You Are a Beginner

Open your terminal or PowerShell and run:


dotnet new webapi -n MonApiSecurisee
cd MonApiSecurisee


And there you go. You now have a fresh .NET API ready to be secured.

You do not need Visual Studio. You can do everything with VS Code and the .NET CLI.

Step 2: Install the JWT Package

To configure JWT bearer authentication, add the appropriate ASP.NET Core authentication package:


dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer


Think of it as installing the system that checks whether the wristband presented at the entrance is valid.

Step 3: Configure JWT in appsettings.json

Open appsettings.json and add a section like this:

"JwtSettings": {
"SecretKey": "UneCleSuperSecreteEtLongueCommeUnRomanDeTolstoï!",
"Issuer": "MonApi",
"Audience": "MesUtilisateurs",
"ExpiryInMinutes": 60
}

⚠️ Pro tip:

This secret key is the key to your vault.
In production, never hardcode a real secret in source-controlled configuration. Use Secret Manager for local development, environment variables, or a managed secret store such as Azure Key Vault. 

Step 4: Enable JWT Authentication in Program.cs

This is the heart of the system. In Program.cs, add the configuration below:


var builder = WebApplication.CreateBuilder(args);

// Get JWT settings
var jwtSettings = builder.Configuration.GetSection("JwtSettings");

// Add JWT authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtSettings["Issuer"],
ValidateAudience = true,
ValidAudience = jwtSettings["Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSettings["SecretKey"])
),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero // No clock-skew tolerance
};
});

builder.Services.AddAuthorization();
builder.Services.AddControllers();

var app = builder.Build();

// Enable authentication and authorization
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
💬 "That is a lot all at once…"
Yes, but look closely:
  • We tell .NET: "Use JWT bearer authentication."
  • We define the validation rules: "Validate the issuer, audience, signing key, and expiration."
  • And we enable the authentication middleware with UseAuthentication().

Step 5: Create an AuthController to Generate Tokens

Create a file named AuthController.cs. In this example, we will simulate a login:


[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly IConfiguration _configuration;

public AuthController(IConfiguration configuration)
{
_configuration = configuration;
}

[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest request)
{
// In a real application, validate credentials against a proper user store
if (request.Username == "admin" && request.Password == "password")
{
var token = GenerateJwtToken(request.Username);
return Ok(new { Token = token });
}

return Unauthorized(); // 401
}

private string GenerateJwtToken(string username)
{
var jwtSettings = _configuration.GetSection("JwtSettings");
var secretKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSettings["SecretKey"])
);
var signingCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);

var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // Unique token ID
};

var token = new JwtSecurityToken(
issuer: jwtSettings["Issuer"],
audience: jwtSettings["Audience"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(
double.Parse(jwtSettings["ExpiryInMinutes"])
),
signingCredentials: signingCredentials
);

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

public class LoginRequest
{
public string Username { get; set; }
public string Password { get; set; }
}

Human-to-human advice:

This example uses static credentials — admin / password — only to demonstrate the authentication flow.

In a real application, use a proper user store and password hashing, or use ASP.NET Core Identity with Entity Framework. For learning the JWT flow itself, this simplified example keeps the focus on the token mechanics.

Step 6: Protect Your Routes with [Authorize]

Now restrict certain routes to authenticated users.

For example, in WeatherForecastController.cs:


[Authorize] // 🔒 Only users with a valid token
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok(new[] { "The weather looks great… if you are authenticated!" });
}
}

Call this route without a token → you should receive 401 Unauthorized.

Call it with a valid token → ✅ Access allowed.

Best Practices: What You Should Know — Even as a Self-Taught Developer

1. Never expose your secret key

Even in a personal project, get used to using:
  • dotnet user-secrets
  • Environment variables
  • A properly excluded local secret/configuration file when appropriate

2. Plan for token renewal when the application needs long-lived sessions

A JWT that expires after 60 minutes can be a reasonable starting point.
But forcing users to enter their credentials again every hour may create a poor user experience.
→ Many applications use refresh-token or reauthentication flows to obtain new access tokens without keeping access tokens valid indefinitely.

3. Test with Swagger or Postman

Use your API documentation/testing interface or Postman to test:
  • /api/auth/login → obtain a token
  • /weatherforecast → send the token in the request header:
  • Authorization: Bearer your_token_here

Conclusion: You Have Just Reached the Next Level

Congratulations! 🎉

You have just implemented JWT-based authentication in your .NET API.

Your API is no longer completely open: selected endpoints can now require an authenticated user with a valid token.

And the best part?

You built it step by step using standard .NET tools.

✨ What you can do next:
  • Add a database such as SQLite or SQL Server
  • Implement user registration
  • Add a secure refresh-token flow if your application requires it
  • Share the project on GitHub to document your progress — without committing secrets
Share in X