elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException

The identity provider ID token was invalid.

Error message

The identity provider ID token was invalid.

What it means

ValidateIdTokenAsync validates the ID token's JWT signature, issuer, audience, and lifetime via TokenValidationResult. If validation fails (IsValid false) or no ClaimsIdentity is produced, the token cannot be trusted and this exception is thrown. RequireSignedTokens and ValidateLifetime are enforced, with a 1-minute clock skew.

Solutions

  1. Verify the configured issuer and audience for the connection exactly match the values in the ID token (inspect the token at jwt.io).
  2. Ensure the JWKS endpoint is reachable and the provider's current signing keys are returned; restart/clear cached keys after a provider rotation.
  3. Synchronize server clocks (NTP) if the failure is lifetime-related.
  4. Confirm the provider signs tokens with an algorithm supported by the configured signing keys (e.g. RS256, not HS256 with a client secret).
  5. Increase ClockSkew only if you have verified the root cause is benign clock drift.

Example fix

// before: issuer mismatch
settings = new OpenIdConnectConnectionSettings { Issuer = "https://idp.example.com/" };
// after: match the iss claim exactly
settings = new OpenIdConnectConnectionSettings { Issuer = "https://idp.example.com/realms/main" };
Defensive patterns

Strategy: try-catch

Validate before calling

var metadata = await discovery.GetMetadataAsync(settings, ct);
var keys = await GetSigningKeysAsync(metadata.JwksUri, ct); // pre-fetch to confirm keys are available before validating

Try / catch

try { var principal = await adapter.ValidateIdTokenAsync(idToken, settings, metadata, ct); }
catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("ID token was invalid"))
{ logger.LogError(ex, "ID token failed signature/issuer/lifetime validation"); return Results.Unauthorized(); }

Prevention

When it happens

Trigger: ValidateIdTokenAsync (the principal path) is called with an ID token whose signature doesn't verify against the fetched signing keys, whose iss doesn't match the configured issuer, whose exp is in the past (beyond 1 min skew), whose nbf is in the future, or that is unsigned/alg=none.

Common situations: Provider rotated signing keys but the JWKS fetch is stale or the jwks_uri changed; configured Issuer mismatch (http vs https, trailing slash); server clock skew beyond 1 minute; token encrypted rather than signed (provider uses a different client authentication/alg expectation); token tampered or replayed.

Related errors


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

Appendix: source

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

    private async Task<System.Security.Claims.ClaimsPrincipal> ValidateIdTokenAsync(string idToken, OpenIdConnectConnectionSettings settings, ProviderMetadata metadata, CancellationToken cancellationToken)
    {
        var signingKeys = metadata.SigningKeys.ValueKind == JsonValueKind.Object
            ? new JsonWebKeySet(metadata.SigningKeys.GetRawText()).Keys
            : 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);
    }

View on GitHub (pinned to fe9217bdfa)