{"record":{"id":"78a0664993cd6412","repo":"elsa-workflows/elsa-core","slug":"the-identity-provider-token-response-did-not-contain-an-id","errorCode":null,"errorMessage":"The identity provider token response did not contain an ID token.","messagePattern":"The identity provider token response did not contain an ID token\\.","errorType":"exception","errorClass":"OpenIdConnectAuthenticationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs","lineNumber":182,"sourceCode":"        if (verifier is not null)\n            values[\"code_verifier\"] = verifier;\n        if (!context.Secrets.TryGetValue(\"clientSecret\", out var secret))\n            throw new OpenIdConnectAuthenticationException(\"The provider client secret is unavailable.\");\n        IReadOnlyDictionary<string, string>? headers = null;\n        if (settings.ClientAuthenticationMethod == OpenIdConnectClientAuthenticationMethod.ClientSecretPost)\n        {\n            values[\"client_id\"] = settings.ClientId;\n            values[\"client_secret\"] = secret.Value.Reveal();\n        }\n        else\n            headers = new Dictionary<string, string> { [\"Authorization\"] = $\"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($\"{FormUrlEncode(settings.ClientId)}:{FormUrlEncode(secret.Value.Reveal())}\"))}\" };\n\n        var response = await providerHttpClient.PostFormAsync(metadata.TokenEndpoint, values, headers, ProviderResponseKind.Token, cancellationToken);\n        if (!response.IsSuccessStatusCode)\n            throw new OpenIdConnectAuthenticationException(\"The identity provider token exchange failed.\");\n        using var payload = ParseProviderJson(response.Body, \"The identity provider token response was invalid.\");\n        if (!payload.RootElement.TryGetProperty(\"id_token\", out var idToken) || idToken.ValueKind != JsonValueKind.String || string.IsNullOrWhiteSpace(idToken.GetString()))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider token response did not contain an ID token.\");\n        return idToken.GetString()!;\n    }\n\n    private static string FormUrlEncode(string value) => Uri.EscapeDataString(value).Replace(\"%20\", \"+\", StringComparison.Ordinal);\n\n    private async Task<System.Security.Claims.ClaimsPrincipal> ValidateIdTokenAsync(string idToken, OpenIdConnectConnectionSettings settings, ProviderMetadata metadata, CancellationToken cancellationToken)\n    {\n        var signingKeys = metadata.SigningKeys.ValueKind == JsonValueKind.Object\n            ? new JsonWebKeySet(metadata.SigningKeys.GetRawText()).Keys\n            : await GetSigningKeysAsync(metadata.JwksUri, cancellationToken);\n        var validation = await new JsonWebTokenHandler { MapInboundClaims = false }.ValidateTokenAsync(idToken, new()\n        {\n            ValidateIssuer = true,\n            ValidIssuer = metadata.Issuer,\n            ValidateAudience = true,\n            ValidAudience = settings.ClientId,\n            ValidateIssuerSigningKey = true,\n            IssuerSigningKeys = signingKeys,","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs#L164-L200","documentation":"ParseProviderJson succeeded (valid JSON) but the token response body has no \"id_token\" property that is a non-empty string, so the adapter cannot proceed to ID-token validation. OpenID Connect providers should always return id_token for the authorization_code flow; its absence means the grant type or scopes are misconfigured on the provider side.","triggerScenarios":"ExchangeCodeAsync receives a 2xx token response whose JSON lacks an id_token member, has it as a non-string JSON value, or as an empty/whitespace string.","commonSituations":"Provider returning a plain OAuth2 access-token response because the authorization request omitted the \"openid\" scope; hybrid/response-type misconfiguration; provider configured for token-only response types; custom token endpoint stubs in tests returning only access_token.","solutions":["Add the \"openid\" scope (and any required claims scopes) to the authorization request so the provider issues an ID token.","Verify the provider is configured as an OpenID Connect provider and supports the authorization_code flow with ID tokens.","Inspect the token response body to see what is returned instead of id_token (often only access_token/token_type).","If the provider cannot return id_token, switch to a userinfo-based flow instead of relying on this adapter."],"exampleFix":"// before\nscopes: [\"profile\", \"email\"]\n// after\nscopes: [\"openid\", \"profile\", \"email\"]","handlingStrategy":"validation","validationCode":"// Ensure 'openid' scope is present in the authorization request before redirecting\nif (!requestedScopes.Contains(\"openid\", StringComparer.Ordinal))\n    throw new InvalidOperationException(\"The authorization request must include the 'openid' scope to receive an ID token.\");","typeGuard":null,"tryCatchPattern":"try { await broker.ExchangeCodeAsync(transaction, code, ct); }\ncatch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains(\"ID token\")) { logger.LogError(\"Provider returned no id_token; check scopes/flow config\"); return Results.Problem(\"Provider did not issue an ID token.\", statusCode: 502); }","preventionTips":["Always include the 'openid' scope in OIDC authorization requests","Verify the response type triggers an ID token (authorization_code flow)","Smoke-test the token response shape in a staging environment"],"tags":["openid-connect","id-token","token-exchange","unexpected-response","identity-provider"],"backgroundTag":"unexpected-response-shape","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"}