Web API  

🔐 How to Secure .NET Web API Endpoints with JWT and [Authorize]

As modern applications increasingly rely on stateless, scalable backends, securing your API is essential. JSON Web Tokens (JWT) are one of the most common methods for securing RESTful APIs in .NET. In this article, you'll learn how to secure .NET Web API endpoints using JWT and the [Authorize] attribute.

📌 Why Use JWT?

JWTs are compact, URL-safe tokens that contain claims (user data) and are signed to ensure data integrity. They're ideal for stateless authentication because:

  • They don't require server-side sessions.
  • They're easy to pass between the frontend and the backend.
  • They scale well for distributed applications (e.g., microservices).

🛠 Prerequisites

To follow along, you'll need:

  • .NET 6 or later
  • Basic knowledge of ASP.NET Core Web API
  • Postman or another tool to test API endpoints

1. Create a New .NET Web API Project

You can start by creating a new project:

dotnet new webapi -n JwtAuthDemo
cd JwtAuthDemo

2. Add JWT Authentication NuGet Package

Install the JWT Bearer authentication middleware:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

3. Configure JWT in Program.cs

Modify Program.cs to set up JWT authentication:

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    var key = Encoding.UTF8.GetBytes("YourSuperSecretKey123!"); // Use a secure key in production
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false,
        ValidateAudience = false,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key)
    };
});

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

var app = builder.Build();

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

app.MapControllers();
app.Run();

4. Create a Token Generator Endpoint

In Controllers/AuthController.cs:

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

[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginRequest request)
    {
        if (request.Username == "admin" && request.Password == "password") // Dummy check
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes("YourSuperSecretKey123!");
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, request.Username) }),
                Expires = DateTime.UtcNow.AddHours(1),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            return Ok(new { token = tokenHandler.WriteToken(token) });
        }

        return Unauthorized();
    }
}

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

5. Protect an Endpoint with [Authorize]

In Controllers/SecureController.cs:

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

[ApiController]
[Route("[controller]")]
public class SecureController : ControllerBase
{
    [Authorize]
    [HttpGet("data")]
    public IActionResult GetSecureData()
    {
        return Ok("This is a secure response only visible with a valid JWT!");
    }
}

6. Test Your API

  1. Login to get a token:
    POST to /auth/login with:

    {
      "username": "admin",
      "password": "password"
    }
    

Call the secure endpoint

GET /secure/data with header:

Authorization: Bearer <your-token>

You should receive a successful response only when the token is valid.

✅ Best Practices

  • Always store the signing key securely (use environment variables or Azure Key Vault).
  • Use HTTPS in production.
  • Add role-based authorization with [Authorize(Roles = "Admin")].
  • Rotate keys periodically.