Mono Blog
Published on

Implementing JWT Authentication in .NET Core

Authors
  • avatar
    Name
    Allen Liu
    Twitter

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.