OIDC middleware will not use forwarded headers in challenge construction #58455
Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
Having ASP.NET Core application running in a container on Azure Kubernetes Services cluster behind an ingress-nginx with this program.cs
...
var authOptions = builder.Configuration.GetRequiredOptions<Auth0Options>(Auth0Options.ConfigKey);
builder.Services
.AddAuthentication(cfg =>
{
cfg.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
cfg.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(cfg =>
{
cfg.Authority = authOptions.Authority;
cfg.ClientId = authOptions.ClientId;
cfg.ClientSecret = authOptions.ClientSecret;
cfg.ResponseType = "code";
cfg.UsePkce = true;
cfg.SaveTokens = true;
cfg.Scope.Clear();
foreach (var scope in authOptions.Scopes.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
{
cfg.Scope.Add(scope);
}
});
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
var app = builder.Build();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor
});
// mind the order
// #1
app.UseAuthentication();
// #2
app.Use(async (context, next) =>
{
if (context.User.Identity is null || !context.User.Identity.IsAuthenticated)
{
// here I verify that correct x-forwarded-* headers are currently present
Log.Information("Headers: {Headers}", context.Request.Headers);
await context.ChallengeAsync();
return;
}
await next();
});
// #3
app.UseAuthorization();
...
I observe that when ChallengeAsync() is called and the redirect is made to Auth0 Identity Provider the redirect_uri inserted into the request URL will lose the https scheme and use http.
Headers:
["[Accept, text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7]", "[Host, hangfire.dev.domain.com]", "[User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0]", "[Accept-Encoding, gzip, deflate, br, zstd]", "[Accept-Language, en]", "[Upgrade-Insecure-Requests, 1]", "[X-Request-ID, a2eae22eec38ed62a5a35cf4bcd0a449]", "[X-Real-IP, ****]", "[X-Forwarded-For, ****]", "[X-Forwarded-Host, hangfire.dev.domain.com]", "[X-Forwarded-Port, 443]", "[X-Forwarded-Proto, https]", "[X-Forwarded-Scheme, https]", "[X-Scheme, https]", "[sec-ch-ua, "Microsoft Edge";v="129", "Not=A?Brand";v="8", "Chromium";v="129"]", "[sec-ch-ua-mobile, ?0]", "[sec-ch-ua-platform, "Windows"]", "[DNT, 1]", "[sec-fetch-site, none]", "[sec-fetch-mode, navigate]", "[sec-fetch-user, ?1]", "[sec-fetch-dest, document]", "[priority, u=0, i]"]
The result is that Auth0 denies the request because URL with a http scheme has not been added into the Allowed redirect URLs with Auth0 (correctly so and won't be added).
The nginx ingress correctly provides the x-forwarded-* headers and those are present in the current context/request. So the app knows that the original request was made to the https scheme and terminated by the nginx proxy which also correctly forwarded this information on.
I found this SO question with an answer that offers a workaround but imo this shouldn't need a workaround, this is a bug within the OIDC flow in ASP.NET Core.
Expected Behavior
The ASP.NET Core OIDC auth provider should construct the redirect_uri query parameter for the OIDC IdP call with the correct/original scheme even though the app is running within the http scheme BUT it is behind a proxy which handles SSL termination (and also enforces https).
Steps To Reproduce
Take a sample ASP.NET Core project and copy the provided code into Program.cs and you'll also need an application registered with one of the OIDC providers (Azure Entra, Auth0, Okta, Keycloak).
Exceptions (if any)
No response
.NET Version
8.0.403
Anything else?
No response