LowCodeHub.Keycloak.Authentication.JwtBearer 0.0.11

dotnet add package LowCodeHub.Keycloak.Authentication.JwtBearer --version 0.0.11
                    
NuGet\Install-Package LowCodeHub.Keycloak.Authentication.JwtBearer -Version 0.0.11
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LowCodeHub.Keycloak.Authentication.JwtBearer" Version="0.0.11" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LowCodeHub.Keycloak.Authentication.JwtBearer" Version="0.0.11" />
                    
Directory.Packages.props
<PackageReference Include="LowCodeHub.Keycloak.Authentication.JwtBearer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LowCodeHub.Keycloak.Authentication.JwtBearer --version 0.0.11
                    
#r "nuget: LowCodeHub.Keycloak.Authentication.JwtBearer, 0.0.11"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LowCodeHub.Keycloak.Authentication.JwtBearer@0.0.11
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LowCodeHub.Keycloak.Authentication.JwtBearer&version=0.0.11
                    
Install as a Cake Addin
#tool nuget:?package=LowCodeHub.Keycloak.Authentication.JwtBearer&version=0.0.11
                    
Install as a Cake Tool

LowCodeHub.Keycloak.Authentication.JwtBearer

Keycloak JWT Bearer authentication for ASP.NET Core. The package wires up AddJwtBearer, validates Keycloak options at startup, configures OpenID Connect metadata and JWKS retrieval, and maps Keycloak realm/client roles into normal ASP.NET Core role claims.

NuGet License: MIT

Why This Library?

Feature LowCodeHub.Keycloak.Authentication.JwtBearer Manual JwtBearer setup
Keycloak URLs Derived from KeycloakBaseUrl and Realm Build authority, metadata, and JWKS URLs manually
Startup validation Required options fail fast Usually discovered on first request
Role mapping Realm roles and client roles mapped automatically Custom claims transformation required
JWKS retrieval Metadata and JWKS configured together Default behavior may need extra setup behind proxies
Query token support Optional ?access_token= support for SSE/WebSocket scenarios Custom OnMessageReceived code
Failure headers Optional token failure headers for SPA clients Custom OnAuthenticationFailed code

Installation

dotnet add package LowCodeHub.Keycloak.Authentication.JwtBearer

Quick Start

using LowCodeHub.Keycloak.Authentication.JwtBearer.Extensions;

builder.Services.AddKeycloakJwtBearerAuthentication(options =>
{
    options.KeycloakBaseUrl = "https://keycloak.example.com";
    options.Realm = "my-realm";
    options.Audience = "my-api";
});

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/me", (ClaimsPrincipal user) => new
{
    Name = user.Identity?.Name,
    Roles = user.FindAll("role").Select(c => c.Value)
}).RequireAuthorization();

app.Run();

Tokens issued by Keycloak are validated against the configured issuer and audience. Realm roles from realm_access.roles and client roles from resource_access.{Audience}.roles are copied to the configured role claim type, so standard ASP.NET Core authorization works:

app.MapGet("/admin", () => "ok")
   .RequireAuthorization(policy => policy.RequireRole("admin"));

Configuration

Code-Based Configuration

builder.Services.AddKeycloakJwtBearerAuthentication(options =>
{
    options.KeycloakBaseUrl = "https://keycloak.example.com";
    options.Realm = "my-realm";
    options.Audience = "orders-api";
    options.RequireHttpsMetadata = true;
});

appsettings.json

builder.Services.AddKeycloakJwtBearerAuthentication(builder.Configuration);
{
  "Keycloak": {
    "KeycloakBaseUrl": "https://keycloak.example.com",
    "Realm": "my-realm",
    "Audience": "orders-api",
    "RequireHttpsMetadata": true
  }
}

Use a custom section name when needed:

builder.Services.AddKeycloakJwtBearerAuthentication(
    builder.Configuration,
    sectionName: "Authentication:Keycloak");

Options

Option Type Default Required Description
KeycloakBaseUrl string - Yes Base URL of the Keycloak server, for example https://keycloak.example.com
Realm string - Yes Keycloak realm name
Audience string - Yes Expected token audience, usually your API/client ID
Issuer string Authority No Expected issuer. Override when Keycloak is behind a reverse proxy with a different external issuer URL
RequireHttpsMetadata bool true No Requires HTTPS for metadata and JWKS endpoints
ClockSkew TimeSpan? 15 seconds No Allowed clock skew for token lifetime validation
RoleClaimType string role No Claim type used for mapped roles
NameClaimType string name No Claim type used as ClaimsIdentity.NameClaimType
SaveToken bool false No Stores the bearer token in authentication properties
AcceptTokenFromQueryString bool false No Accepts ?access_token= as a bearer token source
EmitFailureResponseHeaders bool false No Emits token failure headers on authentication failures
JwksOption JwksOptions new() No JWKS refresh and backchannel settings

Computed URLs:

Property Value
Authority {KeycloakBaseUrl}/realms/{Realm}
MetadataAddress {Authority}/.well-known/openid-configuration
CertsAddress {Authority}/protocol/openid-connect/certs

Role Mapping

Keycloak stores roles in JSON claims, not as simple repeated role claims:

{
  "realm_access": {
    "roles": ["admin", "support"]
  },
  "resource_access": {
    "orders-api": {
      "roles": ["orders.read", "orders.write"]
    }
  }
}

The package registers IClaimsTransformation and copies those roles into the configured RoleClaimType (role by default). It is idempotent, so repeated transformations do not duplicate role claims.

builder.Services.AddKeycloakJwtBearerAuthentication(options =>
{
    options.KeycloakBaseUrl = "https://keycloak.example.com";
    options.Realm = "my-realm";
    options.Audience = "orders-api";
    options.RoleClaimType = ClaimTypes.Role;
});

Named Authentication Scheme

By default, the package registers the normal Bearer scheme as the default authenticate/challenge scheme.

For apps with multiple authentication schemes, pass a custom scheme name:

builder.Services.AddKeycloakJwtBearerAuthentication(options =>
{
    options.KeycloakBaseUrl = "https://keycloak.example.com";
    options.Realm = "my-realm";
    options.Audience = "orders-api";
}, authenticationScheme: "KeycloakBearer");

app.MapGet("/secure", () => "ok")
   .RequireAuthorization(new AuthorizeAttribute
   {
       AuthenticationSchemes = "KeycloakBearer"
   });

JWKS Options

Use JwksOption to tune metadata/JWKS refresh behavior or supply a custom backchannel handler.

builder.Services.AddKeycloakJwtBearerAuthentication(options =>
{
    options.KeycloakBaseUrl = "https://keycloak.example.com";
    options.Realm = "my-realm";
    options.Audience = "orders-api";

    options.JwksOption = new JwksOptions
    {
        AutomaticRefreshInterval = TimeSpan.FromHours(6),
        RefreshInterval = TimeSpan.FromMinutes(2)
    };
});

For local development with self-signed certificates:

options.JwksOption.BackchannelHttpHandler = new HttpClientHandler
{
    ServerCertificateCustomValidationCallback =
        HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};

Do not use that certificate bypass in production.

Query String Tokens

Bearer tokens should normally be sent in the Authorization header. Some browser-based transports, such as Server-Sent Events or WebSockets, may need query string tokens.

options.AcceptTokenFromQueryString = true;

When enabled, the package reads ?access_token= during OnMessageReceived.

Failure Headers

For SPA clients that need a simple refresh signal, enable failure headers:

options.EmitFailureResponseHeaders = true;

Authentication failures can emit:

Header Meaning
Token-Expired: true Token lifetime validation failed because the token expired
Token-Validation: false Token validation failed

This can reveal authentication details, so keep it disabled for public APIs unless clients require it.

Common Gotchas

RequireHttpsMetadata now defaults to true

Starting with version 0.0.3, RequireHttpsMetadata defaults to true, matching the ASP.NET Core framework default. This is a behavioral change: plain-HTTP development setups (e.g., http://localhost:8080 Keycloak) must now opt out explicitly:

options.RequireHttpsMetadata = false; // development only

Keycloak audience must match Audience

ASP.NET Core validates the JWT aud claim. Many Keycloak clients do not include your API/client ID in aud by default.

If validation fails with an audience error, add an audience mapper in Keycloak:

  1. Open your client in Keycloak Admin Console.
  2. Go to Client scopes or Mappers.
  3. Add an Audience mapper.
  4. Include your API/client ID in the access token audience.

azp and aud are different claims. This package validates aud.

Reverse proxies can change the issuer

If Keycloak is reached internally at one URL but tokens contain an external issuer URL, override Issuer:

options.KeycloakBaseUrl = "http://keycloak:8080";
options.Issuer = "https://auth.example.com/realms/my-realm";

Middleware order matters

Use authentication before authorization:

app.UseAuthentication();
app.UseAuthorization();

How It Works

  1. AddKeycloakJwtBearerAuthentication binds and validates KeycloakOptions.
  2. AddJwtBearer is configured with authority, metadata address, issuer, audience, lifetime, name claim, and role claim settings.
  3. JWKS retrieval uses the configured certs endpoint and refresh intervals.
  4. KeycloakRolesClaimsTransformation maps Keycloak realm/client roles into standard role claims.
  5. Optional events handle query string tokens and failure response headers.

Requirements

  • .NET 10 or later
  • ASP.NET Core
  • Keycloak access tokens with a valid issuer and audience

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.11 204 7/13/2026
0.0.10 159 6/21/2026
0.0.2 186 5/18/2026
0.0.1 125 4/23/2026