Aspnet Jwt
title: ASP.NET Core JWT Authentication — Complete Guide to Token Auth description: 'Learn ASP.NET Core JWT authentication: issuing tokens, bearer validation, refresh tokens, claims-based auth, secured API endpoints, and token security best practices.' date: 2026-06-28 lastmod: 2026-06-28 weight: 25 tags: [backend, aspnet]
ASP.NET Core JWT authentication uses JSON Web Tokens for stateless API authentication, with bearer token validation, claims extraction, and secure token issuance.
## What You'll Learn
By the end of this tutorial, you'll configure JWT bearer authentication, issue access and refresh tokens, implement login/register with JWT, use claims for authorization, and secure API endpoints.
## Real-World Use
A mobile app calls a .NET Web API. Users log in once, receive a JWT (15 min expiry) and refresh token (7 days). The API validates the JWT on every request without database lookups.
## JWT Learning Path
```mermaid
flowchart LR
A[Auth] --> B[JWT]
B --> C[Web API]
C --> D[Minimal API]
D --> E[SignalR]
B --> F{You Are Here}
style F fill:#f90,color:#fff
JWT Configuration
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? ""))
};
});
builder.Services.AddAuthorization();
Token Service
public interface ITokenService
{
string GenerateAccessToken(IdentityUser user);
string GenerateRefreshToken();
ClaimsPrincipal? GetPrincipalFromExpiredToken(string token);
}
public class TokenService : ITokenService
{
private readonly IConfiguration _config;
public TokenService(IConfiguration config) => _config = config;
public string GenerateAccessToken(IdentityUser user)
{
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_config["Jwt:Key"] ?? ""));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Email, user.Email ?? ""),
new Claim(ClaimTypes.Role, "User")
};
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(15),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public string GenerateRefreshToken()
{
var randomBytes = new byte[64];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomBytes);
return Convert.ToBase64String(randomBytes);
}
}
Auth Controller with JWT
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
private readonly UserManager<IdentityUser> _userManager;
private readonly ITokenService _tokenService;
public AuthController(UserManager<IdentityUser> um, ITokenService ts)
{
_userManager = um;
_tokenService = ts;
}
[HttpPost("login")]
public async Task<IActionResult> Login(LoginDto dto)
{
var user = await _userManager.FindByEmailAsync(dto.Email);
if (user == null || !await _userManager.CheckPasswordAsync(user, dto.Password))
return Unauthorized();
var accessToken = _tokenService.GenerateAccessToken(user);
var refreshToken = _tokenService.GenerateRefreshToken();
// Store refresh token (database or distributed cache)
return Ok(new AuthResponse(accessToken, refreshToken, 15 * 60));
}
[HttpPost("refresh")]
public async Task<IActionResult> Refresh(TokenRefreshDto dto)
{
// Validate refresh token from storage
// Generate new access token
// Return new token pair
}
}
Securing Endpoints
[ApiController]
[Route("api/products")]
[Authorize] // All actions require valid JWT
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var email = User.FindFirstValue(ClaimTypes.Email);
return Ok(new { UserId = userId, Email = email });
}
[HttpPost]
[Authorize(Roles = "Admin")] // Admin-only
public IActionResult Create(ProductDto product) { ... }
}
// Minimal API with JWT
app.MapGet("/api/profile", [Authorize] (HttpContext ctx) =>
{
var userId = ctx.User.FindFirstValue(ClaimTypes.NameIdentifier);
return Results.Ok(new { UserId = userId });
});
Refresh Token Storage
public class RefreshToken
{
public int Id { get; set; }
public string UserId { get; set; } = "";
public string Token { get; set; } = "";
public DateTime ExpiresAt { get; set; }
public bool IsRevoked { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
// DbSet<RefreshToken> in AppDbContext
// Store on login, validate on refresh, revoke on logout
Common Mistakes
1. Weak Signing Key
Short or predictable keys allow token forgery. Use a 256-bit cryptographically random key.
2. Long Token Expiration
JWTs can't be revoked. Short expirations (15-60 min) limit damage if tokens leak.
3. Not Using HTTPS
JWT tokens transmitted over HTTP are vulnerable to interception. Always use HTTPS.
4. Storing Sensitive Claims in JWT
JWT payload is base64 (not encrypted). Never store passwords or secrets in claims.
5. Not Handling Token Renewal
Without refresh tokens, users must re-login every 15 minutes. Implement refresh token flow.
Practice Questions
1. How does JWT authentication work in ASP.NET Core?
The server validates the Bearer token in the Authorization header, extracts claims, and creates a User principal.
2. What is contained in a JWT token?
Header (algorithm), Payload (claims, expiration), Signature (verification).
3. How do you add custom claims to a JWT?
Include new Claim() objects in the claims array when creating the JwtSecurityToken.
4. What is the purpose of refresh tokens?
Refresh tokens allow issuing new access tokens without requiring the user to re-authenticate.
5. Challenge: Create a complete JWT auth flow with login, protected endpoint, and token refresh.
[HttpPost("login")]
public async Task<IActionResult> Login(LoginDto dto)
{
// Validate credentials
// Generate access token (15 min) and refresh token (7 days)
// Store refresh token hashed
// Return both tokens
}
[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile()
{
// Extract user ID from JWT claims
// Return user data
}
FAQ
Mini Project: JWT Auth API
Build a complete JWT authentication API with login, protected endpoints, and refresh.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o => o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("super-secret-key-1234567890-32bytes!!")),
ValidateIssuer = false,
ValidateAudience = false
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapPost("/login", (LoginDto dto) => { /* return JWT */ });
app.MapGet("/protected", [Authorize] () => "This is protected");
What's Next
ASP.NET Core Web API ASP.NET Core Minimal API
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro