elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException
The provider client secret is unavailable.
Error message
The provider client secret is unavailable.
What it means
ExchangeCodeAsync builds the token-endpoint form body to swap the authorization code for tokens, but requires the configured client secret from the broker transaction's Secrets dictionary. When the transaction context does not carry an entry under the key "clientSecret", the adapter cannot authenticate the token request and throws OpenIdConnectAuthenticationException. This is a configuration/transaction-state error thrown before any HTTP call to the identity provider.
Solutions
- Configure a client secret on the OpenIdConnect connection so it is stored under the "clientSecret" key when the broker transaction is created.
- Verify the code path that creates the broker transaction (authorize redirect) actually populates context.Secrets from the connection settings.
- If using ClientSecretPost or Basic auth, confirm settings.ClientAuthenticationMethod matches a confidential client and that the secret is present.
- Wrap the callback handling in a try-catch for OpenIdConnectAuthenticationException and surface a configuration error to the operator.
Example fix
// before: connection without secret
new OpenIdConnectConnection { ClientId = "my-app", ClientAuthenticationMethod = OpenIdConnectClientAuthenticationMethod.ClientSecretPost };
// after: secret configured
new OpenIdConnectConnection { ClientId = "my-app", ClientSecret = Secret.Create("s3cret"), ClientAuthenticationMethod = OpenIdConnectClientAuthenticationMethod.ClientSecretPost }; Defensive patterns
Strategy: validation
Validate before calling
if (!transaction.Secrets.TryGetValue("clientSecret", out var _))
throw new InvalidOperationException("OpenIdConnect connection requires a configured client secret before starting the callback exchange."); Type guard
bool HasClientSecret(IReadOnlyDictionary<string, Secret> secrets) => secrets.ContainsKey("clientSecret"); Try / catch
try { await broker.ExchangeCodeAsync(transaction, code, cancellationToken); }
catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("client secret")) { logger.LogError(ex, "Connection {Key} is missing its client secret", connectionKey); return Results.Problem("Authentication is misconfigured.", statusCode: 503); } Prevention
- Always configure ClientSecret on confidential-client OpenIdConnect connections
- Validate connection completeness (clientId + clientSecret + issuer) when saving connection settings
- Add a startup health check that inspects registered connections for required secrets
When it happens
Trigger: Calling ExchangeCodeAsync (via the external authentication broker's idToken path) when context.Secrets lacks the "clientSecret" entry - e.g. the transaction was created without storing the connection's client secret, or the connection has no secret configured.
Common situations: OpenIdConnect connection configured without a client secret (public-client-style config used with a confidential token exchange); PKCE-only setup where the deployment assumes code_verifier alone suffices; secrets stored under a different key by a custom connection source; transaction state lost between the authorize redirect and the callback.
Related errors
- The identity provider rejected the authentication request.
- The identity provider callback did not contain an…
- The identity provider callback could not be correlated.
- The identity provider callback issuer did not match the…
- The identity provider nonce did not match the initiated…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/746b5281c321085d.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs:167
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
headers = new Dictionary<string, string> { ["Authorization"] = $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{FormUrlEncode(settings.ClientId)}:{FormUrlEncode(secret.Value.Reveal())}"))}" };
var response = await providerHttpClient.PostFormAsync(metadata.TokenEndpoint, values, headers, ProviderResponseKind.Token, cancellationToken);
if (!response.IsSuccessStatusCode)
throw new OpenIdConnectAuthenticationException("The identity provider token exchange failed.");
using var payload = ParseProviderJson(response.Body, "The identity provider token response was invalid.");
if (!payload.RootElement.TryGetProperty("id_token", out var idToken) || idToken.ValueKind != JsonValueKind.String || string.IsNullOrWhiteSpace(idToken.GetString()))
throw new OpenIdConnectAuthenticationException("The identity provider token response did not contain an ID token.");
return idToken.GetString()!;
}
View on GitHub (pinned to fe9217bdfa)