RA.Utilities.Authentication.JwtBearer
RA.Utilities.Authentication.JwtBearer streamlines JWT Bearer authentication setup in ASP.NET Core by moving configuration out of your Program.cs and into appsettings.json. A single extension method reads your configuration, applies smart type conversions, and registers everything the framework needs — no boilerplate, no hardcoded secrets.
🎯 Purpose
Configuring JWT Bearer authentication in ASP.NET Core typically requires 20+ lines of setup in Program.cs: binding configuration sections, converting string keys to SymmetricSecurityKey, setting clock skew as a TimeSpan, wiring up authorization, and registering middleware in the correct order. This package collapses all of that into two calls:
builder.Services.AddJwtBearerAuthentication(builder.Configuration);
// ...
app.UseAuth();
The configuration lives entirely in appsettings.json, making it environment-aware and easy to audit. This approach gives you:
- Configuration-driven security: JWT validation parameters (authority, audience, key material, clock skew) are managed through config files, not code — different environments can use different identity providers without rebuilding.
- Type coercion handled for you:
ClockSkewInSeconds(a number) is automatically converted to aTimeSpan, andIssuerSigningKeyString(a plain string) is converted to aSymmetricSecurityKeywith a minimum-length safety check. - Guaranteed callback ordering: An optional
configureOptionscallback lets you programmatically extend or override any setting — and it always runs last, so your overrides always win. - Fail-fast validation: If your signing key is too short for HMAC-SHA256, the app fails at startup with a clear error message — not at runtime with a cryptic token validation failure.
✨ Key Features
- One-call setup —
AddJwtBearerAuthentication()configures authentication, authorization, and JWT Bearer options fromIConfiguration - Convenience middleware —
UseAuth()chainsUseAuthentication()+UseAuthorization()in the correct order - Smart type conversion —
ClockSkewInSeconds(double) →TimeSpan,IssuerSigningKeyString(string) →SymmetricSecurityKey - Minimum key enforcement — Keys shorter than 32 bytes (256 bits) are rejected at startup with a descriptive error
- Programmatic override hook — The
configureOptionscallback runs after all config binding, guaranteeing consumer overrides take effect - Named-scheme aware — Only applies to the
JwtBearerDefaults.AuthenticationSchemescheme, leaving custom named schemes untouched
🛠️ Getting Started
Prerequisites
- .NET 10.0 or later
- An understanding of JWT Bearer authentication in ASP.NET Core
Installation
.NET CLI
dotnet add package RA.Utilities.Authentication.JwtBearer
Package Manager Console
Install-Package RA.Utilities.Authentication.JwtBearer
PackageReference (csproj)
<PackageReference Include="RA.Utilities.Authentication.JwtBearer" Version="10.0.2" />
Hello World
The minimal setup in Program.cs:
using RA.Utilities.Authentication.JwtBearer.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddJwtBearerAuthentication(builder.Configuration);
var app = builder.Build();
app.UseAuth();
app.MapGet("/", () => "Hello World!");
app.Run();
And a minimal appsettings.json for a self-issued token:
{
"Authentication": {
"Schemes": {
"Bearer": {
"TokenValidationParameters": {
"ValidateIssuerSigningKey": true,
"IssuerSigningKeyString": "this-is-a-secret-key-at-least-32-bytes-long!!"
}
}
}
}
}
🧠 Core Concepts
Configuration flow
When you call AddJwtBearerAuthentication(configuration, configureOptions), the library registers a single IConfigureNamedOptions<JwtBearerOptions> implementation that applies settings in a fixed, documented order:
┌──────────────────────────────────────────────────┐
│ ConfigureJwtBearerOptions.Configure() │
│ │
│ Step 1: Bind standard JwtBearerOptions │
│ from Authentication:Schemes:Bearer │
│ (Authority, Audience, ValidIssuer, etc.) │
│ ↓ │
│ Step 2: Apply special conversions │
│ ClockSkewInSeconds → TimeSpan │
│ IssuerSigningKeyString → SecurityKey │
│ ↓ │
│ Step 3: Invoke configureOptions callback │
│ (user overrides — ALWAYS wins) │
└──────────────────────────────────────────────────┘
This ordering guarantees that configuration files set the baseline, the library applies its smart conversions, and the consumer has the final say via the callback.
Configuration keys
The library reads from Authentication:Schemes:Bearer and its children. The two non-standard keys are:
| Config Key | Type | Purpose | Default |
|---|---|---|---|
TokenValidationParameters:ClockSkewInSeconds | double | Token expiration tolerance | 300 (5 minutes) |
TokenValidationParameters:IssuerSigningKeyString | string | Symmetric key material for HMAC-SHA256 | null (not set) |
All other keys under Authentication:Schemes:Bearer follow the standard ASP.NET Core JwtBearerOptions binding — Authority, Audience, ValidIssuer, ValidateLifetime, etc.
Terminology
- Clock skew: The allowed time difference between the token issuer's clock and your server's clock. Default is 5 minutes — if a token expires at 12:00, it's still accepted until 12:05.
- Issuer signing key: The symmetric key used to verify the token's signature. Must be at least 32 bytes (256 bits) for HMAC-SHA256.
configureOptionscallback: An optionalAction<JwtBearerOptions>parameter onAddJwtBearerAuthentication(). It runs after all config binding and conversions, so you can setEvents, overrideTokenValidationParameters, or add custom handlers.
📖 How-To Guides
Use a self-issued token (no external identity provider)
Configure the key in appsettings.json — everything else is optional:
{
"Authentication": {
"Schemes": {
"Bearer": {
"TokenValidationParameters": {
"ValidateIssuerSigningKey": true,
"IssuerSigningKeyString": "your-secret-key-at-least-32-bytes-long!!"
}
}
}
}
}
Use an external identity provider (Azure AD, Auth0, Keycloak, etc.)
Set the Authority and Audience. The library discovers signing keys automatically via the OIDC metadata endpoint:
{
"Authentication": {
"Schemes": {
"Bearer": {
"Authority": "https://login.microsoftonline.com/{tenant-id}/v2.0",
"Audience": "api://your-client-id"
}
}
}
}
Tighten clock skew for stricter token expiration
Reduce the 5-minute default to something tighter:
{
"Authentication": {
"Schemes": {
"Bearer": {
"TokenValidationParameters": {
"ClockSkewInSeconds": 30
}
}
}
}
}
Add custom JWT event handlers
Use the configureOptions callback to hook into authentication events:
builder.Services.AddJwtBearerAuthentication(builder.Configuration, options =>
{
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
_logger.LogWarning(context.Exception, "JWT authentication failed");
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
_logger.LogInformation("Token validated for {Subject}",
context.Principal?.FindFirst("sub")?.Value);
return Task.CompletedTask;
}
};
});
Override a config-driven setting programmatically
The callback runs after config binding, so you can selectively override:
builder.Services.AddJwtBearerAuthentication(builder.Configuration, options =>
{
// Always validate lifetime, regardless of what's in appsettings.json
options.TokenValidationParameters.ValidateLifetime = true;
// Use a different key per environment
if (builder.Environment.IsDevelopment())
{
options.TokenValidationParameters.ValidateIssuerSigningKey = false;
}
});
Validate the issuer and audience for self-issued tokens
{
"Authentication": {
"Schemes": {
"Bearer": {
"TokenValidationParameters": {
"ValidateIssuer": true,
"ValidIssuer": "https://my-api.example.com",
"ValidateAudience": true,
"ValidAudience": "my-api",
"ValidateIssuerSigningKey": true,
"IssuerSigningKeyString": "your-secret-key-at-least-32-bytes-long!!"
}
}
}
}
}
Exclude specific endpoints from authentication
The package doesn't add [Authorize] attributes — it only configures the JWT Bearer handler. To exclude endpoints, use the standard ASP.NET Core approach:
app.MapGet("/health", () => "Healthy").AllowAnonymous();
⚙️ Configuration
This library reads JWT Bearer options from the Authentication:Schemes:Bearer section of your configuration file.
Full reference
Here is a complete appsettings.json with all supported settings:
{
"Authentication": {
"Schemes": {
"Bearer": {
"Authority": "https://your-identity-provider.com/",
"Audience": "your-api-audience",
"ClaimsIssuer": null,
"Challenge": "Bearer",
"TokenValidationParameters": {
"ValidateIssuer": true,
"ValidIssuer": "https://your-identity-provider.com/",
"ValidateAudience": true,
"ValidAudience": "your-api-audience",
"ValidateLifetime": true,
"ValidateIssuerSigningKey": true,
"ClockSkewInSeconds": 30,
"IssuerSigningKeyString": "your-super-secret-key-that-is-at-least-32-bytes-long!!"
}
}
}
}
}
When using an identity provider with an Authority, you typically don't need to specify ValidIssuer, ValidAudience, or IssuerSigningKey — these are discovered from the OIDC metadata endpoint. The IssuerSigningKeyString is only needed for self-issued tokens.
Special config keys
| Key | Type | Required | Notes |
|---|---|---|---|
TokenValidationParameters:ClockSkewInSeconds | double | No | Defaults to 300 (5 minutes). Converted to TimeSpan automatically. |
TokenValidationParameters:IssuerSigningKeyString | string | No | Must be ≥ 32 bytes. Converted to SymmetricSecurityKey automatically. |
All other keys under Authentication:Schemes:Bearer are bound directly to JwtBearerOptions using the standard ASP.NET Core configuration binding. See the Microsoft JwtBearerOptions documentation for the full list of bindable properties.
Programmatic overrides
The configureOptions callback gives you the final word on any option. It runs after all configuration binding and special conversions:
builder.Services.AddJwtBearerAuthentication(builder.Configuration, options =>
{
// This runs LAST — your changes always take effect
options.TokenValidationParameters.ValidateIssuer = false;
options.BackchannelHttpHandler = new HttpClientHandler
{
Proxy = new WebProxy("http://corporate-proxy:8080")
};
});
✨ Available Components
Extension Methods
| Method | Declared in | Type | Purpose |
|---|---|---|---|
AddJwtBearerAuthentication() | ServiceCollectionExtensions | IServiceCollection extension | Registers JWT Bearer authentication, authorization, and configuration |
UseAuth() | ApplicationBuilderExtensions | IApplicationBuilder extension | Chains UseAuthentication() + UseAuthorization() |
Both methods were previously declared in a single DependencyInjectionExtensions class. In v10.0.2 the class was split into ServiceCollectionExtensions (the Add method) and ApplicationBuilderExtensions (the Use method), and the methods were refactored to C# 14 extension members. Method names, signatures, and the RA.Utilities.Authentication.JwtBearer.Extensions namespace are unchanged — see the migration guide for details.
Configuration Classes
| Class | Purpose |
|---|---|
ConfigureJwtBearerOptions | IConfigureNamedOptions<JwtBearerOptions> — binds config, applies special conversions, invokes user callback |
Internal Types (not part of the public API)
| Type | Purpose |
|---|---|
KeyConstants | Configuration key strings for Authentication:Schemes:Bearer and sub-keys |
🙌 Contributing & Support
Found a bug or have a suggestion?
- GitHub Issues: Report a bug or request a feature
- Discussions: Ask questions or start a discussion
Contributing
Contributions are welcome! Please follow the standard workflow:
- Fork the RA.Utilities repository
- Create a branch from
mainwith a descriptive name (e.g.,feature/oidc-discovery-caching) - Make your changes — follow the existing coding style and add XML documentation for new public APIs
- Submit a pull request to the
mainbranch with a clear description
Links
| Resource | URL |
|---|---|
| NuGet | RA.Utilities.Authentication.JwtBearer |
| Source | GitHub — RedonAlla/RA.Utilities |
| License | MIT |
| Build Status | GitHub Actions |