Securing REST APIs is a critical requirement in modern distributed systems, microservices architectures, and cloud-native applications. JWT (JSON Web Token) authentication is one of the most widely used mechanisms for securing ASP.NET Core Web APIs because it enables stateless, scalable, and secure authentication across clients and services.
This article provides a complete implementation guide for JWT authentication in .NET, including token generation, configuration, validation, role-based authorization integration, security best practices, and real-world architectural considerations.
What Is JWT Authentication?
JWT (JSON Web Token) is a compact, URL-safe token format used to securely transmit claims between two parties. It consists of three parts:
Header
Payload (claims)
Signature
Structure:
Header.Payload.Signature
The token is digitally signed using a secret key (HMAC) or public/private key pair (RSA), ensuring integrity and authenticity.
Why Use JWT for REST APIs?
JWT authentication is preferred for REST APIs because:
It is stateless (no server-side session storage required)
It scales well in distributed environments
It works across domains and microservices
It integrates easily with mobile and SPA clients
It supports role-based and claims-based authorization
Unlike cookie-based authentication, JWT is ideal for APIs consumed by external clients.
How JWT Authentication Works
User logs in with credentials.
Server validates credentials.
Server generates a JWT token.
Client stores the token (usually in memory or secure storage).
Client sends token in Authorization header.
Server validates token for each request.
Authorization header format:
Authorization: Bearer {token}
Step 1: Install Required Packages
Install JWT authentication package:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Step 2: Configure JWT Authentication in Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
var jwtKey = builder.Configuration["Jwt:Key"];
var issuer = builder.Configuration["Jwt:Issuer"];
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = issuer,
ValidAudience = issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Step 3: Add JWT Configuration in appsettings.json
{
"Jwt": {
"Key": "YourSuperSecretKeyHere",
"Issuer": "YourAppIssuer"
}
}
Always store secrets securely using environment variables or secret managers in production.
Step 4: Generate JWT Token
Create a login endpoint that generates a token after validating credentials.
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
private readonly IConfiguration _configuration;
public AuthController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpPost("login")]
public IActionResult Login(LoginModel model)
{
if (model.Username != "admin" || model.Password != "password")
return Unauthorized();
var claims = new[]
{
new Claim(ClaimTypes.Name, model.Username),
new Claim(ClaimTypes.Role, "Admin")
};
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Issuer"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(30),
signingCredentials: creds);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token)
});
}
}
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}
In real applications, credentials must be validated against a database with hashed passwords.
Step 5: Protect API Endpoints
Use the Authorize attribute to secure controllers.
[Authorize]
[ApiController]
[Route("api/secure")]
public class SecureController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("This endpoint is protected.");
}
}
Role-based protection:
[Authorize(Roles = "Admin")]
JWT vs Cookie-Based Authentication
| Parameter | JWT Authentication | Cookie Authentication |
|---|
| State Management | Stateless | Stateful |
| Scalability | High | Limited |
| API Suitability | Excellent | Not ideal |
| Mobile Support | Strong | Weak |
| Server Storage | Not required | Required |
| Cross-Domain Support | Easy | Complex |
JWT is more suitable for REST APIs and distributed systems.
Token Expiration and Refresh Tokens
Best practice:
Refresh token flow:
Access token expires.
Client sends refresh token.
Server validates refresh token.
New access token is issued.
Never store refresh tokens in local storage without protection.
Security Best Practices
Use HTTPS only
Store secrets securely
Use strong signing keys
Validate issuer and audience
Implement token expiration
Avoid storing sensitive data in payload
Implement refresh token mechanism
Revoke tokens on logout
Apply rate limiting on login endpoint
Common Mistakes
Hardcoding secret keys
Using long-lived tokens without expiration
Not validating issuer or audience
Storing tokens in insecure browser storage
Not hashing passwords
Proper configuration prevents security vulnerabilities.
Real-World Production Scenario
In a microservices architecture:
Authentication service issues JWT tokens.
API Gateway validates tokens.
Downstream services trust validated claims.
Role-based access is enforced per service.
This enables centralized authentication with decentralized authorization.
Summary
Securing REST APIs using JWT authentication in .NET involves configuring the JwtBearer middleware, generating signed tokens after credential validation, protecting endpoints using the Authorize attribute, and validating tokens for each request. JWT provides a stateless, scalable authentication mechanism suitable for distributed systems, microservices, and cloud-native applications. By implementing strong signing keys, token expiration policies, HTTPS enforcement, refresh token strategies, and role-based authorization, developers can build secure and production-ready ASP.NET Core APIs.