{"record":{"id":"4d19bae14e68afc4","repo":"elsa-workflows/elsa-core","slug":"the-identity-provider-nonce-did-not-match-the-initiated","errorCode":null,"errorMessage":"The identity provider nonce did not match the initiated request.","messagePattern":"The identity provider nonce did not match the initiated request\\.","errorType":"exception","errorClass":"OpenIdConnectAuthenticationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs","lineNumber":98,"sourceCode":"    {\n        if (TryGetParameter(context.Parameters, \"error\", out _))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider rejected the authentication request.\");\n\n        if (!TryGetParameter(context.Parameters, \"state\", out var state) || !FixedTimeEquals(state, context.CorrelationState))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider callback could not be correlated.\");\n\n        var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);\n        var metadata = await ResolveMetadataAsync(settings, cancellationToken);\n        var adapterState = ReadAdapterState(context.Transaction.ProtectedPayload);\n        if (adapterState is not null && !string.Equals(adapterState.Issuer, metadata.Issuer, StringComparison.Ordinal))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider callback issuer did not match the initiated request.\");\n\n        var idToken = await ExchangeCodeAsync(settings, metadata, context, adapterState?.CodeVerifier, cancellationToken);\n        var principal = await ValidateIdTokenAsync(idToken, settings, metadata, cancellationToken);\n        var nonce = principal.FindFirst(\"nonce\")?.Value;\n        var expectedNonce = context.Transaction.ProviderNonce ?? adapterState?.Nonce;\n        if (string.IsNullOrWhiteSpace(expectedNonce) || !FixedTimeEquals(nonce, expectedNonce))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider nonce did not match the initiated request.\");\n\n        var issuer = principal.FindFirst(\"iss\")?.Value ?? metadata.Issuer;\n        var subject = principal.FindFirst(\"sub\")?.Value;\n        if (string.IsNullOrWhiteSpace(subject))\n            throw new OpenIdConnectAuthenticationException(\"The identity provider response did not contain a subject.\");\n\n        var projectedClaims = ProjectClaims(principal, context.Connection.Connection.ClaimProjection);\n        return new(new(issuer, subject, projectedClaims), projectedClaims, [], new(idToken));\n    }\n\n    public async ValueTask<ConnectionTestResult> TestAsync(ConnectionTestContext context, CancellationToken cancellationToken = default)\n    {\n        var settings = await GetSettingsAsync(context.Connection.Connection.AdapterSettings, cancellationToken);\n        _ = await ResolveMetadataAsync(settings, cancellationToken);\n        return new(ConnectionObservationStatus.Succeeded, \"reachable\", \"Provider metadata was resolved.\", []);\n    }\n\n    public async ValueTask<ExternalLogoutRequest?> CreateLogoutRequestAsync(ExternalLogoutContext context, CancellationToken cancellationToken = default)","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs#L80-L116","documentation":"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.","triggerScenarios":"AuthenticateCallbackAsync where the id_token lacks a 'nonce' claim, expectedNonce is null/whitespace, or FixedTimeEquals(nonce, expectedNonce) returns false.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"var token = DecodeJwt(idToken); if (string.IsNullOrEmpty(token.Payload.Nonce)) throw new InvalidOperationException(\"Provider id_token is missing nonce; flow cannot be verified.\");","typeGuard":null,"tryCatchPattern":"try { await adapter.AuthenticateCallbackAsync(context); } catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains(\"nonce\")) { return Results.Redirect(\"/login?reason=nonce-mismatch\"); }","preventionTips":["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."],"tags":["openid-connect","authentication","nonce","replay-protection"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}