Implementing JWT Authentication in .NET Core
in .NET
This guide demonstrates JWT authentication implementation in .NET Core with custom configurations and error handling.
1. NuGet Package Installation
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
2. Configuration Settings
appsettings.json
{
"JWT": {
"ClockSkew": 10,
"ValidAudience": "https://meowv.com",
"ValidIssuer": "StarPlus",
"IssuerSigningKey": "6Zi/5pifUGx1c+... (base64 key)",
"Expires": 30
}
}
3. Service Configuration
Startup.cs
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(
Convert.ToInt32(Configuration["JWT:ClockSkew"])),
ValidAudience = Configuration["JWT:ValidAudience"],
ValidIssuer = Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["JWT:IssuerSigningKey"]))
};
});
services.AddAuthorization();
4. Token Generation Endpoint
AuthController.cs
[HttpGet("Token")]
public string GenerateToken(string username, string password)
{
if (username == "meowv" && password == "123")
{
var claims = new[] {
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Email, "123@meowv.com"),
new Claim(JwtRegisteredClaimNames.Exp,
DateTimeOffset.UtcNow.AddMinutes(30).ToUnixTimeSeconds().ToString())
};
var credentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:IssuerSigningKey"])),
SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: Configuration["JWT:ValidIssuer"],
audience: Configuration["JWT:ValidAudience"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(30),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
throw new UnauthorizedAccessException("Invalid credentials");
}
Sample Output:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
5. Protected Endpoints
[Authorize]
[HttpGet("secure-data")]
public IActionResult GetSecureData() => Ok("Protected resource");
[AllowAnonymous]
[HttpGet("public-data")]
public IActionResult GetPublicData() => Ok("Public resource");
Authorization Test Results:
/secure-data
without token: 401 Unauthorized/secure-data
with valid token: 200 OK/public-data
: Always accessible
6. Custom Error Handling
Custom 401 Response
options.Events = new JwtBearerEvents {
OnChallenge = async context => {
context.HandleResponse();
context.Response.StatusCode = 401;
await context.Response.WriteAsync(
JsonSerializer.Serialize(new {
success = false,
message = "Authentication required"
}));
}
};
7. Alternative Token Locations
Query Parameter Support
options.Events = new JwtBearerEvents {
OnMessageReceived = context => {
context.Token = context.Request.Query["access_token"];
return Task.CompletedTask;
}
};
Usage:
GET /api/data?access_token=eyJhbGciOiJIUzI1NiIs...
Key Features
- Configurable expiration and clock skew
- Customizable error responses
- Multiple token location support (Header/Query/Cookie)
- Claims-based authorization
- Standard-compliant JWT validation
Always store signing keys securely and rotate them periodically for production systems.