{"record":{"id":"c30b783d6005979f","repo":"elsa-workflows/elsa-core","slug":"the-identity-provider-signing-keys-were-invalid","errorCode":null,"errorMessage":"The identity provider signing keys were invalid.","messagePattern":"The identity provider signing keys were invalid\\.","errorType":"exception","errorClass":"OpenIdConnectAuthenticationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs","lineNumber":239,"sourceCode":"    {\n        var baseUri = options.Value.Redirects.ExternalCallbackBaseUri ?? throw new OpenIdConnectAuthenticationException(\"The deployment callback base URI is not configured.\");\n        return ExternalAuthenticationCallbackUris.GetLogoutCallbackUri(baseUri, connection.Connection.Key);\n    }\n\n    private async Task<IEnumerable<SecurityKey>> GetSigningKeysAsync(Uri? jwksUri, CancellationToken cancellationToken)\n    {\n        if (jwksUri is null)\n            throw new OpenIdConnectAuthenticationException(\"The identity provider did not provide signing keys.\");\n        var response = await providerHttpClient.GetAsync(jwksUri, ProviderResponseKind.SigningKeys, cancellationToken);\n        if (!response.IsSuccessStatusCode)\n            throw new OpenIdConnectAuthenticationException(\"The identity provider signing keys could not be resolved.\");\n        try\n        {\n            return new JsonWebKeySet(response.ReadBodyAsUtf8()).Keys;\n        }\n        catch (JsonException)\n        {\n            throw new OpenIdConnectAuthenticationException(\"The identity provider signing keys were invalid.\");\n        }\n    }\n\n    private static IReadOnlyDictionary<string, IReadOnlyCollection<string>> ProjectClaims(System.Security.Claims.ClaimsPrincipal principal, ClaimProjection projection)\n    {\n        if (projection.MaximumClaimCount <= 0 || projection.MaximumValueLength <= 0 || projection.MaximumTotalBytes <= 0)\n            return new Dictionary<string, IReadOnlyCollection<string>>(StringComparer.Ordinal);\n\n        var allowed = projection.AllowedClaimTypes ?? new HashSet<string>();\n        var result = new Dictionary<string, List<string>>(StringComparer.Ordinal);\n        var count = 0;\n        var bytes = 0;\n\n        foreach (var claim in principal.Claims)\n        {\n            if (!allowed.Contains(claim.Type) || claim.Value.Length > projection.MaximumValueLength || count == projection.MaximumClaimCount)\n                continue;\n","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs#L221-L257","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Fetch the jwks_uri from the app host and verify the body is a valid JWK set: { \"keys\": [ ... ] }.","Inspect and fix any proxy/intermediary that could return HTML 200 pages (WAF block pages, login portals).","Confirm the provider's JWKS content type is application/json and the response is not truncated.","If using a stub/mock JWKS in tests, correct the fixture JSON to a well-formed key set.","Validate content-encoding (gzip/br) handling on any custom proxy in front of the provider."],"exampleFix":"// before (bad JWKS served with 200)\n<html><body>Blocked</body></html>\n// after (valid JWKS)\n{ \"keys\": [ { \"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"abc\", \"n\": \"...\", \"e\": \"AQAB\" } ] }","handlingStrategy":"validation","validationCode":"using var probe = new HttpClient();\nvar body = await probe.GetStringAsync(jwksUri, ct);\nusing var doc = JsonDocument.Parse(body);\nif (!doc.RootElement.TryGetProperty(\"keys\", out var keys) || keys.ValueKind != JsonValueKind.Array || keys.GetArrayLength() == 0)\n    throw new InvalidOperationException($\"{jwksUri} did not return a valid JWK set.\");","typeGuard":"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; } }","tryCatchPattern":"try { await adapter.ValidateIdTokenAsync(idToken, settings, metadata, ct); }\ncatch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains(\"signing keys were invalid\"))\n{ logger.LogError(ex, \"JWKS body from {Uri} is not valid JSON\", metadata.JwksUri); return Results.Problem(\"Identity provider returned malformed signing keys.\", statusCode: 502); }","preventionTips":["Check for proxies/WAFs serving HTML 200 pages in place of JSON","Verify the JWKS response content-type is application/json and encoding is intact","Validate mock/stub JWKS fixtures in tests are well-formed JWK sets"],"tags":["openid-connect","jwks","json","invalid-response","identity-provider"],"backgroundTag":"json-parse-error","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}