How to Implement JWT Authentication in C# Web API

This article provides a straightforward guide on implementing JSON Web Token (JWT) authentication and bearer token validation in an ASP.NET Core Web API. You will learn which NuGet packages to install, how to store configuration settings, how to configure the authentication middleware in your application pipeline, how to generate a token, and how to secure API endpoints using authorization attributes.

1. Install the Required NuGet Package

To validate JWTs, install the Microsoft.AspNetCore.Authentication.JwtBearer package via the .NET CLI or the NuGet Package Manager:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

2. Configure Token Settings in appsettings.json

Define your token parameters in appsettings.json. The secret key must be sufficiently long (at least 256 bits / 32 characters) to satisfy cryptographic requirements.

{
  "Jwt": {
    "Key": "YourSuperSecretKeyWithSufficientLength32CharactersLong!",
    "Issuer": "https://yourdomain.com",
    "Audience": "https://yourdomain.com",
    "DurationInMinutes": 60
  }
}

3. Register Authentication Services in Program.cs

Add the authentication service and configure the JWT Bearer validation options. This tells the application how to inspect and validate incoming tokens.

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var jwtSettings = builder.Configuration.GetSection("Jwt");
var key = Encoding.UTF8.GetBytes(jwtSettings["Key"]!);

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = true;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = true,
        ValidIssuer = jwtSettings["Issuer"],
        ValidateAudience = true,
        ValidAudience = jwtSettings["Audience"],
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero
    };
});

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseHttpsRedirection();

// Place UseAuthentication before UseAuthorization
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

4. Generate the JWT Bearer Token

Create a method or an authentication endpoint to validate user credentials and issue a signed token containing user claims.

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;

[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)
    {
        // Replace with actual database user verification
        if (request.Username != "testuser" || request.Password != "password123")
        {
            return Unauthorized("Invalid credentials.");
        }

        var jwtSettings = _configuration.GetSection("Jwt");
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Key"]!));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, request.Username),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(ClaimTypes.Role, "User")
        };

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

        return Ok(new
        {
            Token = new JwtSecurityTokenHandler().WriteToken(token)
        });
    }
}

public record LoginRequest(string Username, string Password);

5. Secure Endpoints with the [Authorize] Attribute

Apply the [Authorize] attribute to controllers or action methods to restrict access to requests containing a valid JWT.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class DataController : ControllerBase
{
    [HttpGet]
    public IActionResult GetSecureData()
    {
        var username = User.Identity?.Name;
        return Ok(new { Message = $"Access granted for {username}." });
    }
}

Clients must include the token in the Authorization header formatted as Bearer <your-token> when making requests to protected endpoints. In response, ASP.NET Core will validate the token against the defined TokenValidationParameters before granting access.