I have a fairly standard setup that involves an ASP.NET Core app that is hosted by an Azure App Service, proxied through Cloudflare.
This extension method is called after the usual boilerplate ASP.NET Core identity code, which also configures cookies:
internal static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder authenticationBuilder, IConfiguration config)
{
void configureMicrosoftIdentityOptions(OpenIdConnectOptions options)
{
options.Authority = config["xxx:Authority"];
options.ClientId = config["xxx:ClientId"];
options.ClientSecret = config["xxx:ClientSecret"];
options.SignInScheme = IdentityConstants.ApplicationScheme;
options.ResponseType = OpenIdConnectResponseType.IdToken;
options.Prompt = "select_account";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidAudience = config["xxx:ClientId"],
};
};
return authenticationBuilder.AddOpenIdConnect("AzureAD", configureMicrosoftIdentityOptions);
}
public static AuthenticationBuilder AddCookieLogin(this AuthenticationBuilder authenticationBuilder)
{
void configureCookieAuthOptions(CookieAuthenticationOptions options)
{
options.Cookie.Name = "MyApp";
options.Cookie.IsEssential = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.SlidingExpiration = true;
options.LoginPath = "/signin";
}
return authenticationBuilder.AddCookie(configureCookieAuthOptions);
}
The entire process is kicked off in a simple controller action:
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, scheme);
Nothing special to report in the Azure AD App Registration. The redirect URI is set to {MYURIHERE}/signin-oidc
. This all works jolly well locally. In my logs, I find that Azure AD does comes back to the app and signs in the user ('AuthenticationScheme: xxx signed in.') and redirects from the signin-oidc
endpoint to the requested redirect URI.
With the proxy enabled on the Azure App Service, however, 'sometimes' it happens that the browser seems to be stuck after logging into Azure AD. This is the URL in question that freezes until a timeout is thrown:
https://login.microsoftonline.com/common/reprocess?ctx=xxx0&sessionid=yyy
The logs still indicate 'AuthenticationScheme: xxx signed in.' but stop logging after this entry: Request finished HTTP/1.1 POST XXX/signin-oidc - 302 0 - 1014.6661ms
For some unknown reason, hitting CTRL + F5 before starting the Azure AD authentication process seems to fix it. I've tried all sorts of combinations to set the cookies just so and even gone so far as to set '"Clear-Site-Data' headers before the login page loads.
To no avail, so I am out of ideas and would like to know if there is anything else I could try. The odd thing is that another ASP.NET Core web app, which uses the built-in authentication methods on Azure, doesn't have this problem as it also happens to be proxied by Cloudflare. So it definitely seems to be an issue in my code rather than Cloudflare, Azure App Service, or anything else.
In a desperate attempt, I scrapped the code above and swapped AddOpenIdConnect
with AddMicrosoftAccount
. The result is the same, but now the browser freezes on the https://login.microsoftonline.com/common/oauth2/v2.0/authorize
page. At the bottom left corner of Edge, there's a box that says 'waiting for MYREDIRECTURIDOMAIN.com'.
While everybody else was just downvoting the ChatGPT-generated answer, I've come to my own rescue. The desperate attempt led me to believe that it solved the problem.
I replaced AddOpenIdConnect
with AddMicrosoftAccount
with the following configuration:
options.ClientId = config["ClientId"];
options.ClientSecret = config["ClientSecret"];
options.Events = new OAuthEvents()
{
OnTicketReceived = OAuthEventHandlers.OnTicketReceived(ClaimTypes.Email),
OnRedirectToAuthorizationEndpoint = ctx =>
{
ctx.HttpContext.Response.Redirect(ctx.RedirectUri + "&prompt=select_account");
return Task.CompletedTask;
}
};
Forwarded headers:
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
At some I also had specified XForwardedHost
but it seems to work without (and maybe because of that, who knows).
Cookie auth options:
options.Cookie.IsEssential = true;
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
No idea if is this is fireproof, but it seems to be working for now.