elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException
The identity provider signing keys could not be resolved.
Error message
The identity provider signing keys could not be resolved.
What it means
The JWKS URI exists and was fetched, but the HTTP response was not successful, so signing keys could not be downloaded. ID-token signature validation cannot proceed without keys. The underlying status code is not embedded in the message, so check provider availability and network path.
Solutions
- Confirm the app host can reach the jwks_uri (curl it from the server; check DNS, proxy, and firewall/egress rules).
- Check the returned status code via provider/proxy logs to distinguish 404 vs 403 vs 5xx.
- Fix TLS trust (install the provider's CA chain into the host trust store) if the fetch fails on certificate validation.
- Add retry with backoff for transient 5xx during provider key rotation windows.
- Verify the jwks_uri path in the discovery document matches the provider's actual JWKS route.
Example fix
// before: IDP not reachable from container issuer: "https://internal-idp.local" // resolvable only on the corporate LAN // after: ensure egress DNS/route or use a reachable address issuer: "https://idp.internal.example.com" // with DNS + firewall entry for the app host
Defensive patterns
Strategy: retry
Validate before calling
using var ping = await httpClient.GetAsync(jwksUri, HttpCompletionOption.ResponseHeadersRead, ct);
if (!ping.IsSuccessStatusCode)
throw new InvalidOperationException($"JWKS endpoint {jwksUri} returned {ping.StatusCode}; fix reachability before sign-in."); Try / catch
try { await adapter.ValidateIdTokenAsync(idToken, settings, metadata, ct); }
catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("could not be resolved"))
{
await Task.Delay(TimeSpan.FromSeconds(2), ct); // retry once for transient provider unavailability
// retry the flow or return 502
return Results.Problem("Identity provider signing keys are temporarily unavailable.", statusCode: 502);
} Prevention
- Verify egress firewall/DNS allows the app host to reach the provider's jwks_uri
- Install the provider's TLS CA chain on the host
- Monitor the JWKS endpoint with a health probe
- Retry with backoff only on 5xx, never on 4xx
When it happens
Trigger: GetSigningKeysAsync's providerHttpClient.GetAsync to the jwks_uri returns a non-2xx status: 404 (wrong jwks path), 403 (WAF/IP blocking), 5xx (provider outage), or connection-level failure surfaced as non-success.
Common situations: Server cannot reach the identity provider (egress firewall, private IDP not routable from the app host); TLS trust issues terminating as failed fetches; provider temporarily down during key rotation; self-hosted Keycloak behind a proxy returning 404 for /.well-known/jwks.json.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- The identity provider metadata could not be resolved.
- The identity provider token exchange failed.
- The identity provider signing keys were invalid.
- The identity provider token response did not contain an ID…
- The identity provider did not provide signing keys.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/bbe3074ccd472557.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs:232
private Uri GetCallbackUri(EffectiveIdentityProviderConnection connection, BrokerTransactionPurpose purpose)
{
var baseUri = options.Value.Redirects.ExternalCallbackBaseUri ?? throw new OpenIdConnectAuthenticationException("The deployment callback base URI is not configured.");
return ExternalAuthenticationCallbackUris.GetAuthorizationCallbackUri(baseUri, connection.Connection, purpose);
}
private Uri GetLogoutCallbackUri(EffectiveIdentityProviderConnection connection)
{
var baseUri = options.Value.Redirects.ExternalCallbackBaseUri ?? throw new OpenIdConnectAuthenticationException("The deployment callback base URI is not configured.");
return ExternalAuthenticationCallbackUris.GetLogoutCallbackUri(baseUri, connection.Connection.Key);
}
private async Task<IEnumerable<SecurityKey>> GetSigningKeysAsync(Uri? jwksUri, CancellationToken cancellationToken)
{
if (jwksUri is null)
throw new OpenIdConnectAuthenticationException("The identity provider did not provide signing keys.");
var response = await providerHttpClient.GetAsync(jwksUri, ProviderResponseKind.SigningKeys, cancellationToken);
if (!response.IsSuccessStatusCode)
throw new OpenIdConnectAuthenticationException("The identity provider signing keys could not be resolved.");
try
{
return new JsonWebKeySet(response.ReadBodyAsUtf8()).Keys;
}
catch (JsonException)
{
throw new OpenIdConnectAuthenticationException("The identity provider signing keys were invalid.");
}
}
private static IReadOnlyDictionary<string, IReadOnlyCollection<string>> ProjectClaims(System.Security.Claims.ClaimsPrincipal principal, ClaimProjection projection)
{
if (projection.MaximumClaimCount <= 0 || projection.MaximumValueLength <= 0 || projection.MaximumTotalBytes <= 0)
return new Dictionary<string, IReadOnlyCollection<string>>(StringComparer.Ordinal);
var allowed = projection.AllowedClaimTypes ?? new HashSet<string>();
var result = new Dictionary<string, List<string>>(StringComparer.Ordinal);
var count = 0;View on GitHub (pinned to fe9217bdfa)