ASP.NET Core Securing APIs with JWT Step by step Implementation and Top 10 Questions and Answers
 Last Update: April 01, 2025      10 mins read      Difficulty-Level: beginner

Securing APIs with JWT in ASP.NET Core: A Detailed Guide

Securing APIs is a crucial aspect of modern web development, ensuring that only authorized users can access sensitive data. JSON Web Tokens (JWT) are a widely-used method to secure APIs due to their simplicity, standardization, and effectiveness. In this detailed guide, we will walk through the process of securing APIs using JWT in ASP.NET Core. This guide assumes you have a basic understanding of ASP.NET Core, C#, and RESTful APIs.

Step 1: Setting Up the ASP.NET Core Project

First, create a new ASP.NET Core Web API project using Visual Studio or the .NET CLI.

Using Visual Studio:

  1. Open Visual Studio and select "Create a new project."
  2. Choose "ASP.NET Core Web API" as the project type.
  3. Configure your project by providing a name, location, and solution name.
  4. Select the target framework (e.g., .NET 6.0).
  5. Click "Create."

Using .NET CLI: Open your terminal or command prompt and run the following command:

dotnet new webapi -n JWTAuthApi
cd JWTAuthApi

Step 2: Configuring JWT in appsettings.json

JWT requires some configuration settings to work. Open the appsettings.json file and add the following configuration:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "JwtSettings": {
    "SecretKey": "your_secret_key_here",
    "Issuer": "your_issuer_name_here",
    "Audience": "your_audience_name_here"
  }
}

Make sure to create a strong, unique SecretKey that is at least 32 characters long. The Issuer and Audience values can be any string that identifies your application.

Step 3: Creating the JWT Authentication Service

Create a new folder named Services in your project and add a new class AuthService.cs within this folder. This service will be responsible for generating JWT tokens.

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JWTAuthApi.Services
{
    public class AuthService
    {
        private readonly IConfiguration _configuration;

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

        public string GenerateJwtToken(string userId, string role)
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var claims = new[]
            {
                new Claim(ClaimTypes.NameIdentifier, userId),
                new Claim(ClaimTypes.Role, role)
            };

            var token = new JwtSecurityToken(
                issuer: _configuration["JwtSettings:Issuer"],
                audience: _configuration["JwtSettings:Audience"],
                claims: claims,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: credentials
            );

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

Step 4: Registering the JWT Authentication Service

Open the Startup.cs or Program.cs file (depending on your ASP.NET Core version), and register the AuthService and configure JWT authentication.

For ASP.NET Core 3.1:

In Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    // Add AuthService
    services.AddSingleton<AuthService>();

    // Configure JWT Authentication
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "JwtBearer";
        options.DefaultChallengeScheme = "JwtBearer";
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["JwtSettings:Issuer"],
            ValidAudience = Configuration["JwtSettings:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSettings:SecretKey"]))
        };
    });

    // Add authorization
    services.AddAuthorization();
}

For ASP.NET Core 6.0 (Program.cs):

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

// Add AuthService
builder.Services.AddSingleton<AuthService>();

// Configure JWT Authentication
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = "JwtBearer";
    options.DefaultChallengeScheme = "JwtBearer";
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["JwtSettings:Issuer"],
        ValidAudience = builder.Configuration["JwtSettings:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:SecretKey"]))
    };
});

// Add authorization
builder.Services.AddAuthorization();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Step 5: Creating the Authentication Controller

Create a new folder named Controllers in your project if it doesn't already exist. Add a new controller named AuthController.cs within this folder. This controller will handle user authentication and provide JWT tokens.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace JWTAuthApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly AuthService _authService;
        public AuthController(AuthService authService)
        {
            _authService = authService;
        }

        [HttpPost("login")]
        public IActionResult Login([FromBody] UserLoginModel user)
        {
            // Perform user validation here (e.g., check username and password in the database)
            if (user.Username == "user" && user.Password == "password")
            {
                var token = _authService.GenerateJwtToken(user.Username, "Admin");
                return Ok(new { token });
            }

            return Unauthorized();
        }
    }

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

Note: This example uses hardcoded credentials for demonstration purposes. In a real application, you should validate the user's credentials against a database.

Step 6: Protecting APIs with JWT

To protect your APIs with JWT, apply the [Authorize] attribute to the controllers or actions you want to secure. For example, to protect all actions in a controller, add the [Authorize] attribute to the controller class.

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

namespace JWTAuthApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    public class ValuesController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("This is a protected resource");
        }
    }
}

You can also specify the required roles or policies for authorization:

[Authorize(Roles = "Admin")]
[HttpGet("admin")]
public IActionResult GetAdminData()
{
    return Ok("This is an admin-only resource");
}

Step 7: Testing the Protected API

To test the protected API, first, obtain a JWT token by sending a POST request to the login endpoint. You can use tools like Postman to easily test your API.

Request:

POST /api/auth/login
Content-Type: application/json

{
    "username": "user",
    "password": "password"
}

Response:

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Use the obtained token to access the protected resource by including it in the Authorization header with the Bearer scheme.

Request:

GET /api/values
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Response:

"This is a protected resource"

If you try to access the protected resource without a valid token, you will receive a 401 Unauthorized response.

Step 8: Handling Token Expiration and Refresh Tokens

JWT tokens typically have an expiration time (e.g., 30 minutes). Once the token expires, the user needs to obtain a new token by re-authenticating. Implementing token refresh is a common practice to enhance user experience and security.

This involves creating a new endpoint for refreshing tokens and issuing new tokens when the existing token is about to expire. Implementing refresh tokens involves securely storing and validating refresh tokens on the server, which can be more complex.

For simplicity, this guide does not cover token refresh, but understanding the basics of token expiration and handling it can help you design a secure and efficient authentication flow.

Conclusion

Securing APIs with JWT in ASP.NET Core involves configuring JWT settings, creating an authentication service to generate tokens, protecting your APIs using the [Authorize] attribute, and testing the secured API. JWT provides a robust, flexible, and scalable solution for API security, making it a popular choice among developers.

By following this detailed guide, you should now have a good understanding of how to implement JWT-based authentication and authorization in ASP.NET Core. As you gain more experience, you can explore advanced topics such as token refresh, role-based authorization, and custom JWT claims.