{"record":{"id":"97636c7ab69fc07d","repo":"elsa-workflows/elsa-core","slug":"the-identity-provider-id-token-was-not-authorized-for-this","errorCode":null,"errorMessage":"The identity provider ID token was not authorized for this client.","messagePattern":"The identity provider ID token was not authorized for this client\\.","errorType":"exception","errorClass":"OpenIdConnectAuthenticationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs","lineNumber":210,"sourceCode":"            : 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,\n            RequireSignedTokens = true,\n            ValidateLifetime = true,\n            ClockSkew = TimeSpan.FromMinutes(1)\n        });\n        if (!validation.IsValid || validation.ClaimsIdentity is null)\n            throw new OpenIdConnectAuthenticationException(\"The identity provider ID token was invalid.\");\n\n        var audiences = validation.ClaimsIdentity.FindAll(\"aud\").Select(x => x.Value).Distinct(StringComparer.Ordinal).ToArray();\n        if (audiences.Length > 1 && !string.Equals(validation.ClaimsIdentity.FindFirst(\"azp\")?.Value, settings.ClientId, StringComparison.Ordinal))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider ID token was not authorized for this client.\");\n        return new(validation.ClaimsIdentity);\n    }\n\n    private Uri GetCallbackUri(EffectiveIdentityProviderConnection connection, BrokerTransactionPurpose purpose)\n    {\n        var baseUri = options.Value.Redirects.ExternalCallbackBaseUri ?? throw new OpenIdConnectAuthenticationException(\"The deployment callback base URI is not configured.\");\n        return ExternalAuthenticationCallbackUris.GetAuthorizationCallbackUri(baseUri, connection.Connection, purpose);\n    }\n\n    private Uri GetLogoutCallbackUri(EffectiveIdentityProviderConnection connection)\n    {\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)","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs#L192-L228","documentation":"After base validation succeeds, the adapter enforces the OIDC 'azp' (authorized party) rule: if the token carries more than one distinct 'aud' value, the 'azp' claim must equal the configured ClientId. This prevents accepting a token issued (by a multi-audience provider) for a different client than the one redeeming the code.","triggerScenarios":"ValidateIdTokenAsync receives a token whose aud claim contains multiple audiences and the azp claim is absent or set to a different client_id than settings.ClientId.","commonSituations":"Provider issuing multi-audience tokens (e.g. Keycloak with multiple mappers or audience protocol mappers adding other clients); azp not emitted because the client is not in the provider's front-channel logout/audience config; ClientId typo or renamed client on the provider.","solutions":["Ensure the ID token's azp claim equals the configured ClientId; on Keycloak add an audience protocol mapper or fix the client ID.","Remove unnecessary audience mappers so the token has a single aud value.","Verify settings.ClientId matches the client configured at the provider (case-sensitive, ordinal comparison).","Inspect the decoded token's aud/azp claims to confirm the exact mismatch before changing config."],"exampleFix":"// before: token aud = [\"api\", \"other-client\"], no azp for this client\n// after (Keycloak): add Audience protocol mapper with Included Client Audience = \"my-app\"\n// so token aud = [\"api\", \"my-app\"], azp = \"my-app\"","handlingStrategy":"validation","validationCode":"// Decode the token (payload only) before the flow to check aud/azp\nvar payload = Base64UrlDecode(idToken.Split('.')[1]);\nusing var doc = JsonDocument.Parse(payload);\nvar aud = doc.RootElement.GetProperty(\"aud\");\nbool multiAud = aud.ValueKind == JsonValueKind.Array && aud.GetArrayLength() > 1;\nif (multiAud && (!doc.RootElement.TryGetProperty(\"azp\", out var azp) || azp.GetString() != clientId))\n    throw new InvalidOperationException(\"Provider must emit azp equal to this client when the token has multiple audiences.\");","typeGuard":null,"tryCatchPattern":"try { var principal = await adapter.ValidateIdTokenAsync(idToken, settings, metadata, ct); }\ncatch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains(\"not authorized for this client\"))\n{ logger.LogError(ex, \"Token aud/azp mismatch for client {ClientId}\", settings.ClientId); return Results.Unauthorized(); }","preventionTips":["Configure audience/azp mappers on the provider so tokens declare this client","Remove unneeded audience mappers that create multi-aud tokens","Compare ClientId spelling/case against the provider's client definition"],"tags":["openid-connect","jwt","audience","azp","token-validation"],"backgroundTag":"jwt-token-expired","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}