elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException

The identity provider signing keys were invalid.

Error message

The identity provider signing keys were invalid.

What it means

The JWKS endpoint returned 2xx, but its body failed to parse as a JSON Web Key Set (JsonException from new JsonWebKeySet(body)), so the adapter throws. The provider's JWKS response is malformed, empty in a bad way, or not JSON at all.

Solutions

  1. Fetch the jwks_uri from the app host and verify the body is a valid JWK set: { "keys": [ ... ] }.
  2. Inspect and fix any proxy/intermediary that could return HTML 200 pages (WAF block pages, login portals).
  3. Confirm the provider's JWKS content type is application/json and the response is not truncated.
  4. If using a stub/mock JWKS in tests, correct the fixture JSON to a well-formed key set.
  5. Validate content-encoding (gzip/br) handling on any custom proxy in front of the provider.

Example fix

// before (bad JWKS served with 200)
<html><body>Blocked</body></html>
// after (valid JWKS)
{ "keys": [ { "kty": "RSA", "use": "sig", "kid": "abc", "n": "...", "e": "AQAB" } ] }
Defensive patterns

Strategy: validation

Validate before calling

using var probe = new HttpClient();
var body = await probe.GetStringAsync(jwksUri, ct);
using var doc = JsonDocument.Parse(body);
if (!doc.RootElement.TryGetProperty("keys", out var keys) || keys.ValueKind != JsonValueKind.Array || keys.GetArrayLength() == 0)
    throw new InvalidOperationException($"{jwksUri} did not return a valid JWK set.");

Type guard

bool IsValidJwks(string body) { try { using var d = JsonDocument.Parse(body); return d.RootElement.TryGetProperty("keys", out var k) && k.ValueKind == JsonValueKind.Array && k.GetArrayLength() > 0; } catch (JsonException) { return false; } }

Try / catch

try { await adapter.ValidateIdTokenAsync(idToken, settings, metadata, ct); }
catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("signing keys were invalid"))
{ logger.LogError(ex, "JWKS body from {Uri} is not valid JSON", metadata.JwksUri); return Results.Problem("Identity provider returned malformed signing keys.", statusCode: 502); }

Prevention

When it happens

Trigger: GetSigningKeysAsync wraps JsonWebKeySet construction in try/catch(JsonException); the fetched body is not valid JSON or not a JWK set shape (e.g. HTML error page served with 200, truncated response, proxy interstitial).

Common situations: Reverse proxy or captive portal returning an HTML 200 page instead of the JWKS; provider misconfiguration serving JSON with wrong content/keys; gzip/encoding corruption through a broken intermediary; test stubs returning invalid JWKS fixtures.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/c30b783d6005979f. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs:239

    {
        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;
        var bytes = 0;

        foreach (var claim in principal.Claims)
        {
            if (!allowed.Contains(claim.Type) || claim.Value.Length > projection.MaximumValueLength || count == projection.MaximumClaimCount)
                continue;

View on GitHub (pinned to fe9217bdfa)