changes: Remove un used OIDC handler

This commit is contained in:
2025-10-14 22:35:21 -06:00
parent aafb84f77c
commit 3ad7ecf559
3 changed files with 19 additions and 198 deletions

View File

@@ -192,6 +192,25 @@ INSERT INTO "AbpSettings" ("Name", "Value")
VALUES ('App.ExternalAuth.RequireEmailVerified', 'false');
```
### ✅ 3. Eliminación de esquema DynamicOidc no utilizado
**Razón**: Simplificar la arquitectura y eliminar código que genera confusión.
**Cambios realizados**:
- `AuthConfigurer.cs`: Eliminado esquema "DynamicOidc" y su handler
- `AuthConfigurer.cs`: Removida política de autorización dual
- Simplificado: Solo esquema "JwtBearer" para el JWT interno del backend
**Flujo actual (explícito y controlado)**:
1. Frontend envía token de proveedor externo a `/api/TokenAuth/AuthenticateExternal`
2. Backend valida token manualmente con JWKS del proveedor
3. Backend genera su **propio JWT** firmado con su SecurityKey
4. Cliente usa ese JWT con esquema "JwtBearer" para requests subsecuentes
**Beneficio**:
- Código más simple y mantenible
- Un solo flujo de autenticación claro
- No hay ambigüedad sobre qué esquema se usa
## 🎯 Próximas Mejoras Sugeridas
### Alta Prioridad

View File

@@ -1,183 +0,0 @@
using ASPBaseOIDC.Authorization.ExternalAuth;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
namespace ASPBaseOIDC.Authentication.ExternalAuth
{
/// <summary>
/// Dynamic OIDC authentication handler that validates tokens from multiple external providers
/// Automatically detects provider by token issuer and validates accordingly
/// </summary>
public class DynamicOidcHandler : AuthenticationHandler<JwtBearerOptions>
{
private readonly ExternalAuthProviderManager _providerManager;
private readonly IHttpClientFactory _httpClientFactory;
public DynamicOidcHandler(
IOptionsMonitor<JwtBearerOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
ExternalAuthProviderManager providerManager,
IHttpClientFactory httpClientFactory)
: base(options, logger, encoder, clock)
{
_providerManager = providerManager;
_httpClientFactory = httpClientFactory;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
try
{
// 1. Extract token from Authorization header
var token = ExtractToken();
if (token == null)
{
return AuthenticateResult.NoResult();
}
// 2. Read token without validation to get issuer
var handler = new JwtSecurityTokenHandler();
if (!handler.CanReadToken(token))
{
return AuthenticateResult.NoResult();
}
var jwtToken = handler.ReadJwtToken(token);
var issuer = jwtToken.Issuer;
// 3. Find provider by issuer (cached)
var provider = await _providerManager.GetByIssuerAsync(issuer);
if (provider == null || !provider.IsEnabled)
{
Logger.LogWarning($"No enabled provider found for issuer: {issuer}");
return AuthenticateResult.NoResult();
}
// 4. Validate token with provider configuration
var validationParams = await BuildValidationParametersAsync(provider);
var principal = handler.ValidateToken(token, validationParams, out var validatedToken);
// 5. Create authentication ticket
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
catch (SecurityTokenException ex)
{
Logger.LogWarning(ex, "Token validation failed");
return AuthenticateResult.Fail(ex.Message);
}
catch (Exception ex)
{
Logger.LogError(ex, "Unexpected error during authentication");
return AuthenticateResult.Fail("Authentication failed");
}
}
/// <summary>
/// Extract bearer token from Authorization header
/// </summary>
private string ExtractToken()
{
var authorization = Request.Headers["Authorization"].ToString();
if (string.IsNullOrEmpty(authorization))
{
return null;
}
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
return authorization.Substring("Bearer ".Length).Trim();
}
return null;
}
/// <summary>
/// Build token validation parameters for specific provider
/// </summary>
private async Task<TokenValidationParameters> BuildValidationParametersAsync(
ASPBaseOIDC.Authorization.ExternalAuth.ExternalAuthProvider provider)
{
// Download OIDC discovery document
var discoveryUrl = provider.Authority.TrimEnd('/') + "/.well-known/openid-configuration";
var httpClient = _httpClientFactory.CreateClient();
var discoveryResponse = await httpClient.GetStringAsync(discoveryUrl);
var discoveryDoc = JsonDocument.Parse(discoveryResponse);
var issuer = discoveryDoc.RootElement.GetProperty("issuer").GetString();
var jwksUri = discoveryDoc.RootElement.GetProperty("jwks_uri").GetString();
// Download JWKS
var jwksResponse = await httpClient.GetStringAsync(jwksUri);
var jwks = JsonDocument.Parse(jwksResponse);
return new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = issuer,
ValidateAudience = true,
ValidAudience = provider.ClientId,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (token, securityToken, kid, parameters) =>
{
return ResolveSigningKeys(jwks, kid);
},
ClockSkew = TimeSpan.FromMinutes(5)
};
}
/// <summary>
/// Resolve signing keys from JWKS
/// </summary>
private IEnumerable<SecurityKey> ResolveSigningKeys(JsonDocument jwks, string kid)
{
var keys = jwks.RootElement.GetProperty("keys");
var signingKeys = new List<SecurityKey>();
foreach (var key in keys.EnumerateArray())
{
var keyId = key.GetProperty("kid").GetString();
if (keyId == kid)
{
var kty = key.GetProperty("kty").GetString();
if (kty == "RSA")
{
var e = key.GetProperty("e").GetString();
var n = key.GetProperty("n").GetString();
var rsa = new RsaSecurityKey(new System.Security.Cryptography.RSAParameters
{
Exponent = Base64UrlEncoder.DecodeBytes(e),
Modulus = Base64UrlEncoder.DecodeBytes(n)
})
{
KeyId = keyId
};
signingKeys.Add(rsa);
}
}
}
return signingKeys;
}
}
}

View File

@@ -1,7 +1,5 @@
using Abp.Runtime.Security;
using ASPBaseOIDC.Authentication.ExternalAuth;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
@@ -53,19 +51,6 @@ namespace ASPBaseOIDC.Web.Host.Startup
{
OnMessageReceived = QueryStringTokenResolver
};
})
// Dynamic OIDC authentication for external providers (Authentik, Keycloak, etc.)
.AddScheme<JwtBearerOptions, DynamicOidcHandler>("DynamicOidc", options =>
{
// Options are handled dynamically by DynamicOidcHandler
});
// Configure authorization to accept both local and external tokens
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder("JwtBearer", "DynamicOidc")
.RequireAuthenticatedUser()
.Build();
});
}
}