elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException

The identity provider ID token was not authorized for this…

Error message

The identity provider ID token was not authorized for this client.

What it means

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.

Solutions

  1. Ensure the ID token's azp claim equals the configured ClientId; on Keycloak add an audience protocol mapper or fix the client ID.
  2. Remove unnecessary audience mappers so the token has a single aud value.
  3. Verify settings.ClientId matches the client configured at the provider (case-sensitive, ordinal comparison).
  4. Inspect the decoded token's aud/azp claims to confirm the exact mismatch before changing config.

Example fix

// before: token aud = ["api", "other-client"], no azp for this client
// after (Keycloak): add Audience protocol mapper with Included Client Audience = "my-app"
// so token aud = ["api", "my-app"], azp = "my-app"
Defensive patterns

Strategy: validation

Validate before calling

// Decode the token (payload only) before the flow to check aud/azp
var payload = Base64UrlDecode(idToken.Split('.')[1]);
using var doc = JsonDocument.Parse(payload);
var aud = doc.RootElement.GetProperty("aud");
bool multiAud = aud.ValueKind == JsonValueKind.Array && aud.GetArrayLength() > 1;
if (multiAud && (!doc.RootElement.TryGetProperty("azp", out var azp) || azp.GetString() != clientId))
    throw new InvalidOperationException("Provider must emit azp equal to this client when the token has multiple audiences.");

Try / catch

try { var principal = await adapter.ValidateIdTokenAsync(idToken, settings, metadata, ct); }
catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("not authorized for this client"))
{ logger.LogError(ex, "Token aud/azp mismatch for client {ClientId}", settings.ClientId); return Results.Unauthorized(); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

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

            : await GetSigningKeysAsync(metadata.JwksUri, cancellationToken);
        var validation = await new JsonWebTokenHandler { MapInboundClaims = false }.ValidateTokenAsync(idToken, new()
        {
            ValidateIssuer = true,
            ValidIssuer = metadata.Issuer,
            ValidateAudience = true,
            ValidAudience = settings.ClientId,
            ValidateIssuerSigningKey = true,
            IssuerSigningKeys = signingKeys,
            RequireSignedTokens = true,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromMinutes(1)
        });
        if (!validation.IsValid || validation.ClaimsIdentity is null)
            throw new OpenIdConnectAuthenticationException("The identity provider ID token was invalid.");

        var audiences = validation.ClaimsIdentity.FindAll("aud").Select(x => x.Value).Distinct(StringComparer.Ordinal).ToArray();
        if (audiences.Length > 1 && !string.Equals(validation.ClaimsIdentity.FindFirst("azp")?.Value, settings.ClientId, StringComparison.Ordinal))
            throw new OpenIdConnectAuthenticationException("The identity provider ID token was not authorized for this client.");
        return new(validation.ClaimsIdentity);
    }

    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)

View on GitHub (pinned to fe9217bdfa)