elsa-workflows/elsa-core · error · OpenIdConnectAuthenticationException
The OpenID Connect discovery URL is required.
Error message
The OpenID Connect discovery URL is required.
What it means
ResolveMetadataAsync only fetches a discovery document when TrustMode is not Manual. In discovery mode, settings.DiscoveryUrl is the source of the provider metadata; if it is null, the adapter cannot discover the provider and throws this exception instead of attempting an HTTP request.
Solutions
- Set discoveryUrl in the connection settings (typically https://<authority>/.well-known/openid-configuration).
- Alternatively switch trustMode to "Manual" and provide issuer, authorizationEndpoint and tokenEndpoint explicitly.
- Re-test the connection with TestAsync after fixing the settings.
Example fix
// before
{ "trustMode": "Discovery", "clientId": "my-client" }
// after
{ "trustMode": "Discovery", "discoveryUrl": "https://idp.example.com/.well-known/openid-configuration", "clientId": "my-client" } Defensive patterns
Strategy: validation
Validate before calling
if (settings.TrustMode != "Manual" && string.IsNullOrWhiteSpace(settings.DiscoveryUrl)) throw new InvalidOperationException("Discovery trust mode requires a discoveryUrl."); Try / catch
try { await adapter.TestAsync(testContext); } catch (OpenIdConnectAuthenticationException ex) when (ex.Message.Contains("discovery URL is required")) { return Results.ValidationProblem(new Dictionary<string, string[]> { ["discoveryUrl"] = ["Discovery URL is required for discovery trust mode."] }); } Prevention
- Enforce discoveryUrl as a required field in your connection editor when trustMode is not Manual.
- Default discoveryUrl to <authority>/.well-known/openid-configuration when the user supplies only an authority.
- Run the connection TestAsync immediately after saving settings.
When it happens
Trigger: Calling ResolveMetadataAsync (via initiation, callback, or TestAsync) with settings.TrustMode != Manual and settings.DiscoveryUrl unset.
Common situations: Creating a connection and choosing discovery-based trust but forgetting to fill in the discovery URL; copying a Manual-mode template into a discovery-mode connection.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- The identity provider did not provide signing keys.
- The OpenID Connect connection configuration is invalid.
- The identity provider metadata could not be resolved.
- The provider client secret is unavailable.
- The deployment callback base URI is not configured.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/a12bcbfc9c22b68d.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication.OpenIdConnect/Services/OpenIdConnectExternalAuthenticationAdapter.cs:141
if (context.UpstreamLogoutHint is not null)
query["id_token_hint"] = context.UpstreamLogoutHint.Reveal();
return new(WithQuery(metadata.EndSessionEndpoint, query), []);
}
private async Task<OpenIdConnectConnectionSettings> GetSettingsAsync(JsonElement settings, CancellationToken cancellationToken)
{
if (!settingsParser.TryParse(settings, out var parsed, out _))
throw new OpenIdConnectAuthenticationException("The OpenID Connect connection configuration is invalid.");
await Task.CompletedTask;
return parsed!;
}
private async Task<ProviderMetadata> ResolveMetadataAsync(OpenIdConnectConnectionSettings settings, CancellationToken cancellationToken)
{
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>
{View on GitHub (pinned to fe9217bdfa)