BTW.KeycloakJwtGenerator.NetFramework461 1.0.1

dotnet add package BTW.KeycloakJwtGenerator.NetFramework461 --version 1.0.1
                    
NuGet\Install-Package BTW.KeycloakJwtGenerator.NetFramework461 -Version 1.0.1
                    
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="BTW.KeycloakJwtGenerator.NetFramework461" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BTW.KeycloakJwtGenerator.NetFramework461" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="BTW.KeycloakJwtGenerator.NetFramework461" />
                    
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 BTW.KeycloakJwtGenerator.NetFramework461 --version 1.0.1
                    
#r "nuget: BTW.KeycloakJwtGenerator.NetFramework461, 1.0.1"
                    
#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 BTW.KeycloakJwtGenerator.NetFramework461@1.0.1
                    
#: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=BTW.KeycloakJwtGenerator.NetFramework461&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=BTW.KeycloakJwtGenerator.NetFramework461&version=1.0.1
                    
Install as a Cake Tool

BTW.KeycloakJwtGenerator.NetFramework461

NuGet License .NET Framework

Variante .NET Framework 4.6.1 de BTW.KeycloakJwtGenerator para sistemas ASP.NET Web API 2 + OWIN. Genera JWTs centralizados usando Keycloak como Identity Provider y valida tokens vía OWIN JwtBearer middleware.

Para .NET 6 / 7 / 8 / 10 usa el paquete principal BTW.KeycloakJwtGenerator.
Para .NET Core 3.1 usa BTW.KeycloakJwtGenerator.NetCore31.


¿Para qué sirve?

Tu Sistema Web API 2 (auth propia) → BTW.KeycloakJwtGenerator → Keycloak → JWT con tus claims
  • ✅ Tu sistema mantiene su autenticación (BD, LDAP, Active Directory)
  • ✅ Keycloak firma y emite el JWT
  • ✅ Claims personalizados que tú defines
  • ✅ Validación de JWT vía OWIN con JWKS cacheado (sin llamar a Keycloak por request)
  • ✅ El JWT es válido en cualquier API que apunte al mismo realm (cross-stack)

Instalación

# Package Manager Console en Visual Studio
Install-Package BTW.KeycloakJwtGenerator.NetFramework461
# dotnet CLI (proyectos SDK-style)
dotnet add package BTW.KeycloakJwtGenerator.NetFramework461

Configuración en Web.config

<configuration>
  <appSettings>
    <add key="KeycloakJwt:BaseUrl"              value="http://localhost:8080" />
    <add key="KeycloakJwt:Realm"               value="mi-realm" />
    <add key="KeycloakJwt:ClientId"            value="mi-sistema" />
    <add key="KeycloakJwt:ClientSecret"        value="mi-secret" />
    <add key="KeycloakJwt:SystemName"          value="Sistema A" />
    <add key="KeycloakJwt:UseTokenExchange"    value="true" />
    <add key="KeycloakJwt:TimeoutSeconds"      value="30" />
    <add key="KeycloakJwt:AdminTokenCacheMinutes" value="4" />
  </appSettings>
</configuration>

Registro en Startup.cs (OWIN)

using System.Web.Http;
using Owin;
using BTW.KeycloakJwtGenerator.Configuration;
using BTW.KeycloakJwtGenerator.Extensions;

[assembly: Microsoft.Owin.OwinStartup(typeof(MiApi.Startup))]
namespace MiApi
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 1. Leer opciones desde Web.config
            var options = KeycloakJwtOptions.FromAppSettings();

            // 2. Validación JWT vía OWIN (debe ir antes de UseWebApi)
            app.UseKeycloakJwtValidation(options);

            // 3. Web API 2
            var config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();

            // 4. Registrar IKeycloakJwtGenerator en tu container DI
            //    (Autofac, Unity, SimpleInjector, etc.)
            //    o manualmente:
            config.DependencyResolver = ConfigureDependencies(config, options);

            app.UseWebApi(config);
        }
    }
}

UseKeycloakJwtValidation descarga y cachea automáticamente el JWKS desde {BaseUrl}/realms/{Realm}/.well-known/openid-configuration. Las claves se actualizan cuando Keycloak rota su par de claves.


Uso — Controlador de login

using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Http;
using BTW.KeycloakJwtGenerator.Exceptions;
using BTW.KeycloakJwtGenerator.Services;

[RoutePrefix("api/auth")]
public class AuthController : ApiController
{
    private readonly IMyAuthService _myAuth;
    private readonly IKeycloakJwtGenerator _jwt;

    public AuthController(IMyAuthService myAuth, IKeycloakJwtGenerator jwt)
    {
        _myAuth = myAuth;
        _jwt    = jwt;
    }

    [HttpPost, Route("login"), AllowAnonymous]
    public async Task<IHttpActionResult> Login(LoginRequest req)
    {
        // 1. Tu autenticación propia (BD, LDAP, etc.)
        var user = await _myAuth.ValidateAsync(req.Username, req.Password);
        if (user == null) return Unauthorized();

        // 2. Claims que decides poner en el JWT
        var claims = new Dictionary<string, object>
        {
            ["departamento"] = user.Departamento,
            ["sucursal"]     = user.Sucursal,
            ["permisos"]     = user.Permisos   // string[]
        };

        // 3. Generar JWT via Keycloak
        var token = await _jwt.GenerateTokenAsync(req.Username, claims);
        return Ok(token);
    }

    [HttpPost, Route("refresh"), AllowAnonymous]
    public async Task<IHttpActionResult> Refresh(RefreshRequest req)
    {
        try
        {
            var token = await _jwt.RefreshTokenAsync(req.RefreshToken);
            return Ok(token);
        }
        catch (KeycloakAuthenticationException)
        {
            return Unauthorized();
        }
    }

    [HttpPost, Route("logout"), Authorize]
    public async Task<IHttpActionResult> Logout(LogoutRequest req)
    {
        await _jwt.RevokeTokenAsync(req.RefreshToken);
        return StatusCode(System.Net.HttpStatusCode.NoContent);
    }
}

Importante: usa System.Web.Http.AuthorizeAttribute (Web API 2), no System.Web.Mvc.AuthorizeAttribute.


Uso — Controlador protegido

using System.Security.Claims;
using System.Web.Http;

[Authorize]   // System.Web.Http.AuthorizeAttribute
[RoutePrefix("api/recurso")]
public class RecursoController : ApiController
{
    [HttpGet, Route("")]
    public IHttpActionResult Get()
    {
        var principal    = User as ClaimsPrincipal;
        var sub          = principal?.FindFirst("sub")?.Value;
        var departamento = principal?.FindFirst("departamento")?.Value;
        var sucursal     = principal?.FindFirst("sucursal")?.Value;
        return Ok(new { sub, departamento, sucursal });
    }
}

Registrar IKeycloakJwtGenerator en el container DI

Con Microsoft.Extensions.DependencyInjection (recomendado)

// En Startup.cs, dentro de Configuration():
var services = new ServiceCollection();
var options  = KeycloakJwtOptions.FromAppSettings();

services.AddKeycloakJwtGenerator(opt =>
{
    opt.BaseUrl      = options.BaseUrl;
    opt.Realm        = options.Realm;
    opt.ClientId     = options.ClientId;
    opt.ClientSecret = options.ClientSecret;
    opt.SystemName   = options.SystemName;
});

var provider = services.BuildServiceProvider();
config.DependencyResolver = new MicrosoftDependencyResolver(provider);

Con Autofac

var builder = new ContainerBuilder();
builder.Register(_ => options).As<KeycloakJwtOptions>().SingleInstance();
builder.RegisterType<KeycloakJwtGenerator>().As<IKeycloakJwtGenerator>();
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

API completa

Método Descripción
GenerateTokenAsync(username, claims) Genera JWT: admin token → sync user → Token Exchange
GenerateTokenAsync(GenerateTokenRequest) Igual, con email/nombre/roles opcionales
RefreshTokenAsync(refreshToken) Renueva access + refresh token
ValidateTokenAsync(token) Introspección remota en Keycloak
RevokeTokenAsync(refreshToken) Logout en Keycloak
KeycloakJwtOptions.FromAppSettings() Lee configuración desde Web.config / App.config
app.UseKeycloakJwtValidation(options) Configura OWIN JwtBearer con JWKS cacheado

Opciones de configuración

Opción (KeycloakJwt:*) Descripción Default
BaseUrl URL base de Keycloak http://localhost:8080
Realm Nombre del Realm master
ClientId Client ID del service account
ClientSecret Client Secret
SystemName Nombre del sistema (claim sistema_origen) null
TimeoutSeconds Timeout HTTP 30
EnableAdminTokenCache Cachear el admin token true
AdminTokenCacheMinutes Tiempo de caché del admin token 4
UseTokenExchange Usar Token Exchange (fallback a service account) true
MaxRetries Reintentos en fallo 3

Interoperabilidad cross-stack

El JWT lo firma Keycloak, no la librería. Esto significa que un token emitido desde este paquete (net461) es válido en cualquier API que valide contra el mismo realm:

Emisor Validador ¿Funciona?
net461 (este paquete) net8 (BTW.KeycloakJwtGenerator)
net461 ASP.NET Core 3.1
net8 net461 (este paquete)
Java (Spring Security + Keycloak) net461

Requisito: BaseUrl y Realm deben ser idénticos entre emisor y validador.


Excepciones

Excepción Cuándo se lanza
KeycloakAuthenticationException Credenciales del service account inválidas
KeycloakConnectionException No se puede conectar con Keycloak (retornar 503 al cliente)
KeycloakUserSyncException Error al crear/actualizar el usuario en el realm
KeycloakConfigurationException Opciones inválidas (BaseUrl, Realm, ClientId, ClientSecret vacíos)

Limitaciones net461

  • ServerCertificateCustomValidationCallback no disponible. Para certificados auto-firmados en desarrollo: ServicePointManager.ServerCertificateValidationCallback = (s,c,ch,e) => true; (no usar en producción).
  • La validación de tokens es local (JWKS cacheado). No se llama a Keycloak en cada request.

Configuración mínima de Keycloak

  1. Crear un Client con Client authentication: ON y Service accounts roles: ✅
  2. Asignar roles al service account: manage-users, view-users, impersonation
  3. Protocol Mappers por cada claim custom: tipo User Attribute, Add to access token: ✅
  4. (Opcional) Habilitar Token Exchange: KC_FEATURES=token-exchange

Licencia

MIT — ver licencia


Desarrollado por By The Wave (BTW)

Product Compatible and additional computed target framework versions.
.NET Framework net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 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
1.0.1 103 4/28/2026
1.0.0 99 4/28/2026