elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException

The identity provider callback did not contain an…

Error message

The identity provider callback did not contain an authorization code.

What it means

ExchangeCodeAsync expects the authorization-code callback parameters to contain a 'code' value to redeem at the token endpoint. If the callback has no 'code' parameter (and passed the earlier error/state checks), the adapter throws this exception because there is nothing to exchange.

Solutions

  1. Check the callback URL the provider used — confirm a 'code' query parameter is present on the redirect.
  2. Ensure the authorization request uses response_type=code (authorization code flow) so the provider returns a code.
  3. Guard against duplicate callback processing: each code can only be exchanged once, so reloads of the callback will lack a fresh code — restart the login flow.

Example fix

// before: initiating with implicit/hybrid flow
new Dictionary<string, string> { ["response_type"] = "id_token", ["client_id"] = clientId }
// after: authorization code flow
new Dictionary<string, string> { ["response_type"] = "code", ["client_id"] = clientId, ["scope"] = "openid" }
Defensive patterns

Strategy: validation

Validate before calling

if (!Request.Query.ContainsKey("code")) return Results.Redirect("/login?reason=missing-code");

Try / catch

try { await adapter.AuthenticateCallbackAsync(context); } catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("authorization code")) { logger.LogWarning(ex, "Callback without code; restarting login"); return Challenge(); }

Prevention

When it happens

Trigger: Calling AuthenticateCallbackAsync (which invokes ExchangeCodeAsync) where TryGetParameter(context.Parameters, "code") fails.

Common situations: Provider redirected back with response_mode that puts the code somewhere not captured; response_type misconfigured (e.g. id_token-only flow); a spurious callback hit (bot refresh, user reloading the callback URL after the code was already consumed and removed).

Related errors


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

Appendix: source

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

        if (settings.TrustMode == OpenIdConnectTrustMode.Manual)
            return new(settings.Issuer!, settings.AuthorizationEndpoint!, settings.TokenEndpoint!, settings.UserInfoEndpoint, settings.EndSessionEndpoint, settings.JwksUri, settings.SigningKeys);

        var address = settings.DiscoveryUrl ?? throw new OpenIdConnectAuthenticationException("The OpenID Connect discovery URL is required.");
        var response = await providerHttpClient.GetAsync(address, ProviderResponseKind.Discovery, cancellationToken);
        if (!response.IsSuccessStatusCode)
            throw new OpenIdConnectAuthenticationException("The identity provider metadata could not be resolved.");
        using var document = ParseProviderJson(response.Body, "The identity provider metadata could not be resolved.");
        var root = document.RootElement;
        var issuer = GetRequiredHttpsUri(root, "issuer").AbsoluteUri.TrimEnd('/');
        var authorizationEndpoint = GetRequiredHttpsUri(root, "authorization_endpoint");
        var tokenEndpoint = GetRequiredHttpsUri(root, "token_endpoint");
        return new(issuer, authorizationEndpoint, tokenEndpoint, GetOptionalHttpsUri(root, "userinfo_endpoint"), GetOptionalHttpsUri(root, "end_session_endpoint"), GetOptionalHttpsUri(root, "jwks_uri"), default);
    }

    private async Task<string> ExchangeCodeAsync(OpenIdConnectConnectionSettings settings, ProviderMetadata metadata, ExternalCallbackContext context, string? verifier, CancellationToken cancellationToken)
    {
        if (!TryGetParameter(context.Parameters, "code", out var code))
            throw new OpenIdConnectAuthenticationException("The identity provider callback did not contain an authorization code.");

        var values = new Dictionary<string, string>
        {
            ["grant_type"] = "authorization_code",
            ["code"] = code,
            ["redirect_uri"] = GetCallbackUri(context.Connection, context.Transaction.Purpose).AbsoluteUri
        };
        if (verifier is not null)
            values["code_verifier"] = verifier;
        if (!context.Secrets.TryGetValue("clientSecret", out var secret))
            throw new OpenIdConnectAuthenticationException("The provider client secret is unavailable.");
        IReadOnlyDictionary<string, string>? headers = null;
        if (settings.ClientAuthenticationMethod == OpenIdConnectClientAuthenticationMethod.ClientSecretPost)
        {
            values["client_id"] = settings.ClientId;
            values["client_secret"] = secret.Value.Reveal();
        }
        else

View on GitHub (pinned to fe9217bdfa)