elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException
The identity provider nonce did not match the initiated…
Error message
The identity provider nonce did not match the initiated request.
What it means
After exchanging the code and validating the id_token, the adapter reads the 'nonce' claim from the validated principal and compares it (constant-time) with the expected nonce from the transaction's ProviderNonce or the adapter state. If the nonce is absent in the token or does not match, replay protection fails and this exception is thrown.
Solutions
- Verify the provider includes the nonce claim in id_tokens (test with jwt.io); if not, use a provider or trust mode that supports nonce.
- Initiate a fresh login flow rather than retrying a stale callback URL, ensuring nonce is captured at initiation.
- Check that the protected transaction payload is not being truncated or altered by middleware between initiation and callback.
Defensive patterns
Strategy: validation
Validate before calling
var token = DecodeJwt(idToken); if (string.IsNullOrEmpty(token.Payload.Nonce)) throw new InvalidOperationException("Provider id_token is missing nonce; flow cannot be verified."); Try / catch
try { await adapter.AuthenticateCallbackAsync(context); } catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("nonce")) { return Results.Redirect("/login?reason=nonce-mismatch"); } Prevention
- Verify with the provider that nonce is echoed in id_tokens before going live.
- Never reuse callback URLs or id_tokens across login attempts; always initiate a fresh flow.
- Ensure the protected transaction payload is stored and transmitted intact.
When it happens
Trigger: AuthenticateCallbackAsync where the id_token lacks a 'nonce' claim, expectedNonce is null/whitespace, or FixedTimeEquals(nonce, expectedNonce) returns false.
Common situations: Provider does not echo the nonce into the id_token; a replayed id_token from an earlier authorization; nonce lost because the transaction payload was corrupted or the flow's ProviderNonce and adapterState.Nonce were both missing.
Related errors
- The identity provider rejected the authentication request.
- The identity provider callback could not be correlated.
- The identity provider callback issuer did not match the…
- The identity provider response did not contain a subject.
- The identity provider callback did not contain an…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/4d19bae14e68afc4.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs:98
{
if (TryGetParameter(context.Parameters, "error", out _))
throw new OpenIdConnectAuthenticationException("The identity provider rejected the authentication request.");
if (!TryGetParameter(context.Parameters, "state", out var state) || !FixedTimeEquals(state, context.CorrelationState))
throw new OpenIdConnectAuthenticationException("The identity provider callback could not be correlated.");
var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);
var metadata = await ResolveMetadataAsync(settings, cancellationToken);
var adapterState = ReadAdapterState(context.Transaction.ProtectedPayload);
if (adapterState is not null && !string.Equals(adapterState.Issuer, metadata.Issuer, StringComparison.Ordinal))
throw new OpenIdConnectAuthenticationException("The identity provider callback issuer did not match the initiated request.");
var idToken = await ExchangeCodeAsync(settings, metadata, context, adapterState?.CodeVerifier, cancellationToken);
var principal = await ValidateIdTokenAsync(idToken, settings, metadata, cancellationToken);
var nonce = principal.FindFirst("nonce")?.Value;
var expectedNonce = context.Transaction.ProviderNonce ?? adapterState?.Nonce;
if (string.IsNullOrWhiteSpace(expectedNonce) || !FixedTimeEquals(nonce, expectedNonce))
throw new OpenIdConnectAuthenticationException("The identity provider nonce did not match the initiated request.");
var issuer = principal.FindFirst("iss")?.Value ?? metadata.Issuer;
var subject = principal.FindFirst("sub")?.Value;
if (string.IsNullOrWhiteSpace(subject))
throw new OpenIdConnectAuthenticationException("The identity provider response did not contain a subject.");
var projectedClaims = ProjectClaims(principal, context.Connection.Connection.ClaimProjection);
return new(new(issuer, subject, projectedClaims), projectedClaims, [], new(idToken));
}
public async ValueTask<ConnectionTestResult> TestAsync(ConnectionTestContext context, CancellationToken cancellationToken = default)
{
var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);
_ = await ResolveMetadataAsync(settings, cancellationToken);
return new(ConnectionObservationStatus.Succeeded, "reachable", "Provider metadata was resolved.", []);
}
public async ValueTask<ExternalLogoutRequest?> CreateLogoutRequestAsync(ExternalLogoutContext context, CancellationToken cancellationToken = default)View on GitHub (pinned to fe9217bdfa)