elsa-workflows/elsa-core · error · ProviderHttpException
RedirectRejected
RedirectRejected
Error message
ProviderHttpException(ProviderHttpFailure.RedirectRejected)
What it means
ProviderHttpClient deliberately disables automatic redirects (AllowAutoRedirect = false) so every hop can be revalidated against the outbound destination policy. This ProviderHttpException(ProviderHttpFailure.RedirectRejected) is thrown when a redirect is received but cannot be followed safely: the response kind is Token or UserInfo (security-sensitive endpoints must never redirect), the Location header is missing, or the maximum redirect count configured in ProviderEgress.MaximumRedirects has been exceeded.
Solutions
- Point your configuration at the final, direct URL for the endpoint (authority, token endpoint, userinfo endpoint) so no redirect is needed — especially for Token and UserInfo calls, which are always rejected on redirect.
- If redirects are legitimate for Discovery/SigningKeys requests, raise ProviderEgress.MaximumRedirects in ExternalAuthenticationOptions.
- Check for a redirect loop: the same host responding redirect→redirect means a server-side configuration problem; fetch the URL with curl -I to see the chain and fix the provider or the configured URL.
- Verify the Location target would pass outbound-destination validation; if the hop is to a disallowed host, allowlist that host or correct the provider's redirect target.
Example fix
// before — authority URL redirects to /.well-known/openid-configuration
options.Authority = new Uri("https://idp.example.com");
// after — use the final non-redirecting discovery root
options.Authority = new Uri("https://idp.example.com/realms/main"); Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: detect redirects (and loops) for discovery URLs before using them
curl -sIL -o /dev/null -w "%{http_code} %{num_redirects} %{url_effective}" https://idp.example.com/.well-known/openid-configuration
// in C#: use HttpClientHandler(AllowAutoRedirect=false) to probe; reject if any 3xx appears for Token/UserInfo endpoints Type guard
static bool IsRedirectRejectedCase(ProviderHttpException ex, ProviderResponseKind kind) =>
ex.Failure == ProviderHttpFailure.RedirectRejected &&
kind is ProviderResponseKind.Token or ProviderResponseKind.UserInfo; Try / catch
try
{
var response = await client.GetAsync(tokenEndpoint, ProviderResponseKind.Token, ct);
}
catch (ProviderHttpException ex) when (ex.Failure == ProviderHttpFailure.RedirectRejected)
{
logger.LogWarning("Provider endpoint attempted a redirect; reconfigure to the final URL. Kind: {Kind}", kind);
throw; // never auto-follow redirects for token/userinfo — that is the security boundary
} Prevention
- Always configure the final, non-redirecting URL for token and userinfo endpoints — redirects are never followed for these kinds.
- Probe discovery URLs with curl -I (HEAD) after any provider change to catch new redirect behavior early.
- Keep ProviderEgress.MaximumRedirects small (1–2) so loops fail fast instead of silently eating redirects.
- Monitor/logs for RedirectRejected — a sudden appearance usually means the provider changed its endpoint layout.
When it happens
Trigger: Calling IProviderHttpClient.GetAsync/PostFormAsync where the provider responds 301/302/303/307/308 and any of: kind is ProviderResponseKind.Token or ProviderResponseKind.UserInfo; the response has no Location header; more than ProviderEgress.MaximumRedirects redirects are returned; or a subsequent hop fails destination validation (which itself becomes DestinationRejected, not this error).
Common situations: Provider moving its token endpoint behind a redirect (common with provider migrations) — token/userinfo redirects are rejected outright; http→https or trailing-slash redirects on discovery documents causing redirect loops that exhaust MaximumRedirects; a misconfigured authority URL pointing at a redirecting landing page instead of the OIDC discovery document.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- The identity provider token exchange failed.
- DestinationRejected
- Timeout
- Access denied.
- RequestBodyTooLargeException
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/223ef84d449e8cd2.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/ProviderHttpClientFactory.cs:100
private async ValueTask<ProviderHttpResponse> SendAsync(Uri uri, ProviderResponseKind kind, Func<Uri, HttpRequestMessage> createRequest, CancellationToken cancellationToken)
{
var redirects = 0;
var current = uri;
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeout.CancelAfter(options.Value.ProviderEgress.RequestTimeout);
try
{
while (true)
{
await destinationValidator.ValidateAsync(current, timeout.Token);
using var request = createRequest(current);
using var response = await invoker.SendAsync(request, timeout.Token);
if (IsRedirect(response.StatusCode))
{
if (kind is ProviderResponseKind.Token or ProviderResponseKind.UserInfo || response.Headers.Location is null || redirects++ >= options.Value.ProviderEgress.MaximumRedirects)
throw new ProviderHttpException(ProviderHttpFailure.RedirectRejected);
current = new(current, response.Headers.Location);
continue;
}
if (!response.IsSuccessStatusCode)
return new(response.StatusCode, []);
return new(response.StatusCode, await ReadResponseBodyAsync(response, kind, timeout.Token));
}
}
catch (OutboundDestinationException)
{
throw new ProviderHttpException(ProviderHttpFailure.DestinationRejected);
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
{
throw new ProviderHttpException(ProviderHttpFailure.Timeout);View on GitHub (pinned to fe9217bdfa)