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
- Verify the configured issuer and audience for the connection exactly match the values in the ID token (inspect the token at jwt.io).
- Ensure the JWKS endpoint is reachable and the provider's current signing keys are returned; restart/clear cached keys after a provider rotation.
- Synchronize server clocks (NTP) if the failure is lifetime-related.
- Confirm the provider signs tokens with an algorithm supported by the configured signing keys (e.g. RS256, not HS256 with a client secret).
- 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
- Keep issuer/audience settings copied exactly from a decoded token
- Refresh JWKS cache promptly when providers rotate keys
- Run NTP time sync on servers to avoid lifetime validation failures
- Confirm the provider's signing algorithm is supported (RS256 etc.)
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
- The identity provider ID token was not authorized for this…
- Access denied.
- C# workflow expression execution is disabled. Set…
- DestinationRejected
- OpenID Connect settings must be an object.
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)