ASP.NET Core JWT Authentication: Setup, Validation, and Best Practices
JWT authentication in ASP.NET Core is a stateless, token-based approach where clients send a signed JWT (usually as a bearer token) and ASP.NET Core validates it on every request to build HttpContext.User. It’s for developers building APIs (REST, SPAs, mobile back ends, microservices) who want scalable authentication without server sessions. The key is understanding how tokens are structured, how they’re validated in the middleware pipeline, and how to handle lifetimes and refresh securely. (IETF Datatracker)
Why does JWT authentication matter for modern APIs?
Modern APIs are consumed by SPAs, mobile apps, partner services, and internal microservices, often across domains and deployments where cookie-based sessions are awkward or undesirable. JWTs are popular because the server can validate a request using only the token and its signing key (or issuer metadata) without loading per-user session state from a shared store. That “statelessness” improves horizontal scalability, but it also shifts responsibility to: strong validation rules, short token lifetimes, safe client storage, and robust monitoring. (IETF Datatracker)
What is a JWT and what problem does it solve?
A JSON Web Token (JWT) is a compact, URL-safe token format for transferring claims between parties. JWTs are typically signed (JWS) so the receiver can verify integrity and authenticity; they can also be encrypted (JWE), though most “JWT auth” in APIs refers to signed tokens.
The problem it solves
JWTs solve the problem of proving identity (or client identity) to an API repeatedly without the API maintaining server-side login session state for every caller.
What JWT does not automatically solve
JWTs do not automatically provide:
JWT is a token format; standards like OAuth 2.0 and OpenID Connect are about flows and identity delegation. (ASP.NET Core supports JWT bearer validation either way—you just need correct validation settings.) (Microsoft Learn)
What’s inside a JWT?
A JWT has three Base64URL-encoded parts separated by dots: header.payload.signature.
What is in the header?
Typically, the header includes:
Example (decoded JSON):
JSON
{
"alg": "HS256",
"typ": "JWT"
}
What is in the payload?
The payload contains claims (registered + custom). Common registered claims include:
Example:
JSON
{
"iss": "https://issuer.example",
"aud": "api://orders",
"sub": "user-123",
"exp": 1736352000,
"role": "Admin"
}
What is the signature?
The signature provides integrity/authenticity. It proves the token was issued by someone holding the signing key (HMAC) or private key (RSA/ECDSA). (IETF Datatracker)
Critical misconception: JWTs are encrypted
JWTs are encoded, not encrypted, unless you’re explicitly using JWE. Anyone who gets the token can Base64URL-decode the header and payload and read the claims. Do not put secrets or sensitive personal data in a JWT.
How does JWT authentication flow work end-to-end?
Here’s the typical flow for APIs:
Text diagram (request pipeline perspective):
How does ASP.NET Core authenticate requests with JWT bearer internally?
ASP.NET Core authentication is built around:
Authentication determines who the caller is; authorization determines what they can access. When the JWT bearer handler succeeds, ASP.NET Core populates HttpContext.User with a ClaimsPrincipal. (Microsoft Learn)
How do you configure JWT bearer authentication in ASP.NET Core?
The canonical starting point is Microsoft’s JWT bearer configuration guidance. (Microsoft Learn)
The outcome is a minimal Program.cs configuration that validates the issuer, audience, signature, or lifetime.
The example uses a symmetric key for simplicity. In production, you may validate tokens from an external authority (OIDC) or use asymmetric keys for your own issuer.
C#
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// If validating tokens from an OIDC authority, you’d often set:
// options.Authority = "https://issuer.example";
// options.Audience = "api://orders";
var signingKey = builder.Configuration["Jwt:SigningKey"];
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey!)),
RequireExpirationTime = true,
ValidateLifetime = true,
// Keep this small; clock skew is a real-world reality, not a license for long tokens.
ClockSkew = TimeSpan.FromMinutes(1),
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseHttpsRedirection();
// Order matters:
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
These switches matter because disabling core validation checks is a known insecure pattern (and even flagged by .NET analyzers). (Microsoft Learn)
What should appsettings.json look like?
JSON
{
"Jwt": {
"Issuer": "https://issuer.example",
"Audience": "api://orders",
"SigningKey": "REPLACE_WITH_A_LONG_RANDOM_SECRET"
}
}
Production note: Storing signing keys in appsettings is usually not appropriate—use a secrets manager or managed key store and rotate keys (details later). (If your org’s standard is different, this needs clarification from the security or infra team.)
Which configuration mistakes cause most 401 Unauthorized issues?
Try embedded eSignatures with BoldSign
Explore our eSignature API
How do you generate and sign JWT tokens securely?
Token generation should happen only after you authenticate the user or client (password, SSO, client credentials, etc.). Your API should not mint tokens because someone asked.
Task: Generate a short-lived access token with minimal claims
C#
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
public sealed class JwtTokenService
{
private readonly string _issuer;
private readonly string _audience;
private readonly SymmetricSecurityKey _key;
public JwtTokenService(IConfiguration config)
{
_issuer = config["Jwt:Issuer"]!;
_audience = config["Jwt:Audience"]!;
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:SigningKey"]!));
}
public string CreateAccessToken(string userId, string role)
{
var claims = new List
{
new(JwtRegisteredClaimNames.Sub, userId),
new(ClaimTypes.Role, role),
};
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _issuer,
audience: _audience,
claims: claims,
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddMinutes(10),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
The JwtSecurityTokenHandler API is widely used and documented, but note that IdentityModel guidance on NuGet indicates newer APIs exist (for some scenarios) and that some packages are considered legacy in newer IdentityModel versions. So keep an eye on library direction when upgrading. (Microsoft Learn)
When should you prefer HMAC or RSA/ECDSA?
If you’re integrating with an external identity provider, you’ll typically validate using issuer metadata and rotating signing keys (JWKS) rather than hard-coding keys. ASP.NET Core’s JWT bearer handler supports this style of configuration. (Microsoft Learn)
Recommended by LinkedIn
How does ASP.NET Core validate JWTs at runtime?
At runtime, validation is driven mainly by TokenValidationParameters. These checks are the difference between signed token parsing and secure authentication. (Microsoft Learn)
What checks run during validation?
What happens when validation fails?
The handler fails authentication, and the request will typically end up as:
How do you hook into validation events for diagnostics and hardening?
You can attach handlers like OnAuthenticationFailed or OnTokenValidated via JwtBearerOptions.Events. (Microsoft Learn)
C#
.AddJwtBearer(options =>
{
// ...TokenValidationParameters...
options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// Log context.Exception (avoid leaking details to clients)
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
// Optional: additional checks (e.g., user is disabled, token jti revoked)
return Task.CompletedTask;
}
};
});
How do claims-based authorization and policies work with JWTs?
JWT authentication gives you a ClaimsPrincipal. Authorization uses that principal to enforce access rules.
Outcome: Protect endpoints using policies (recommended over ad-hoc checks)
C#
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"));
options.AddPolicy("CanReadOrders", policy =>
policy.RequireClaim("scope", "orders.read"));
});
Controller example:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
[HttpGet]
[Authorize(Policy = "CanReadOrders")]
public IActionResult GetOrders() => Ok(new { message = "orders" });
[HttpPost]
[Authorize(Policy = "AdminOnly")]
public IActionResult CreateOrder() => Ok();
}
Minimal API example:
C#
var orders = app.MapGroup("/api/orders").RequireAuthorization("CanReadOrders");
orders.MapGet("/", () => Results.Ok(new { message = "orders" }));
Key design tip: Keep tokens small and policies expressive. If you keep adding claims to avoid a database call, you may end up with bloated tokens and brittle authorization logic.
How should you handle token expiration, refresh, and logout?
JWTs are stateless, so logout and revocation are not free.
What is the practical strategy most teams use?
If you need refresh tokens, that’s typically an application-level feature (persistence, hashing, replay detection, revocation lists). ASP.NET Core can validate bearer tokens, but refresh-token design is your responsibility unless you adopt a full identity solution. Microsoft’s guidance also emphasizes selecting the right approach if you’re acting on behalf of a user and using OIDC. (Microsoft Learn)
What’s hard about revocation in stateless systems?
Once an access token is issued, it remains valid until it expires unless you:
That’s why short-lived access tokens are the default recommendation for high-security systems.
How do you secure JWT authentication in production?
What should you do to protect signing keys?
Why is HTTPS non-negotiable?
Bearer tokens are credentials. If intercepted, an attacker can replay them until expiration.
What is RequireHttpsMetadata and why does it matter?
If you validate tokens using an authority or metadata endpoint, HTTPS is required by default; disabling it should be limited to development. (Microsoft Learn)
Where should clients store tokens safely?
OWASP’s JWT guidance emphasizes preventing common token-handling mistakes (leakage, weak validation, etc.). (OWASP Cheat Sheet Series)
Which JWT attack vectors should you explicitly design against?
What are the performance trade-offs of JWT authentication?
JWT validation happens on every request to protected endpoints, so the main performance levers are:
How do you keep per-request validation cheap?
IdentityModel’s configuration system is designed to retrieve and refresh metadata, which helps reduce repeated network calls for keys and configuration. (Microsoft Learn)
When can JWT validation become a bottleneck?
If you must do server-side checks, consider targeted enforcement (admin actions, money movement, data export) rather than all endpoints.
How do you monitor and troubleshoot JWT authentication failures?
Task: Log failures without leaking security details
What are the most common runtime causes of invalid tokens?
Using handler events is the fastest way to see what’s going wrong during validation. (Microsoft Learn)
Which common JWT mistakes cause security bugs?
What does a production-ready JWT checklist look like?
Summary: what should you do next?
The original post is available here - ASP.NET Core JWT Authentication Explained & Best Practices
Build secure, industry standard authentication into your applications and simplify document signing with BoldSign’s developer‑friendly API.
Explore our online demos and try the sandbox to learn how to embed eSignatures in your application.