Fix JWT validation by configuring custom signing key resolver
- Added IssuerSigningKeyResolver to fetch JWKS directly from internal Keycloak URL - This bypasses the localhost:8080 URLs in Keycloak's discovery document - Ensures JWT tokens are validated against correct signing keys
This commit is contained in:
@@ -51,21 +51,53 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|||||||
options.RequireHttpsMetadata = false;
|
options.RequireHttpsMetadata = false;
|
||||||
options.MapInboundClaims = false;
|
options.MapInboundClaims = false;
|
||||||
|
|
||||||
// For Docker internal communication, use the direct Keycloak URL for metadata
|
// For Docker internal communication, configure metadata and signing key resolution
|
||||||
// This bypasses the hostname mismatch in Keycloak's discovery endpoint
|
// to bypass the hostname mismatch in Keycloak's discovery endpoint
|
||||||
var keycloakAuthority = builder.Configuration["Keycloak:Authority"];
|
var keycloakAuthority = builder.Configuration["Keycloak:Authority"];
|
||||||
|
var keycloakInternalUrl = "http://keycloak:8081";
|
||||||
|
|
||||||
if (keycloakAuthority?.Contains("keycloak:") == true)
|
if (keycloakAuthority?.Contains("keycloak:") == true)
|
||||||
{
|
{
|
||||||
|
// Set metadata address to internal Keycloak URL
|
||||||
options.MetadataAddress = $"{keycloakAuthority}/.well-known/openid-configuration";
|
options.MetadataAddress = $"{keycloakAuthority}/.well-known/openid-configuration";
|
||||||
}
|
|
||||||
|
|
||||||
|
// Configure custom signing key resolver to fetch from internal Keycloak URL
|
||||||
|
// This overrides the URLs returned in the discovery document
|
||||||
|
var httpClient = new HttpClient();
|
||||||
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
|
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidateIssuer = false, // Disabled for local dev - external clients use localhost:8080, internal use keycloak:8080
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) =>
|
||||||
|
{
|
||||||
|
// Fetch JWKS from internal Keycloak URL
|
||||||
|
var jwksUrl = $"{keycloakInternalUrl}/realms/workclub/protocol/openid-connect/certs";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = httpClient.GetStringAsync(jwksUrl).GetAwaiter().GetResult();
|
||||||
|
var jwks = new Microsoft.IdentityModel.Tokens.JsonWebKeySet(response);
|
||||||
|
return jwks.Keys;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to fetch JWKS from {jwksUrl}: {ex.Message}");
|
||||||
|
return Array.Empty<Microsoft.IdentityModel.Tokens.SecurityKey>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = false,
|
||||||
ValidateAudience = true,
|
ValidateAudience = true,
|
||||||
ValidateLifetime = true,
|
ValidateLifetime = true,
|
||||||
ValidateIssuerSigningKey = true
|
ValidateIssuerSigningKey = true
|
||||||
};
|
};
|
||||||
|
}
|
||||||
options.Events = new JwtBearerEvents
|
options.Events = new JwtBearerEvents
|
||||||
{
|
{
|
||||||
OnAuthenticationFailed = context =>
|
OnAuthenticationFailed = context =>
|
||||||
|
|||||||
Reference in New Issue
Block a user