changes: Remove un used OIDC handler
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user