I'm working on a bug related to an OpenId Connect middleware (OWIN) in an ASP.NET Web Forms application which authenticates to Azure AD. I'm completely new to OWIN and OpenID, and did not write this code so bear with me.
The solution was tested and seemingly worked just fine, but once it hit production we saw multiple null reference exceptions, due to HttpContex.Current being null when the SecurityTokenValidated notification middleware was called.
The issue appear to happen roughly 30%-40% of the time the user tried to log in to production. We were initially not able to reproduce the issue locally, but we eventually found out we could reproduce 100% of the time by setting network speed in Chrome to Fast/Slow 3G speed (interestingly enough).
This is our code in the Startup.cs class to configure the Middleware:
[assembly: OwinStartup(typeof(Namespace.Startup))]
namespace Namespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var openIdConnectProvider = OpenIdConnectProviderDAO.GetOpenIdConnectProviders().Single(x => x.ClientId == "ClientIdHere");
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
app.UseStageMarker(PipelineStage.MapHandler);
app.SetDefaultSignInAsAuthenticationType(OpenIdConnectAuthenticationDefaults.AuthenticationType);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = openIdConnectProvider.ClientId,
Authority = openIdConnectProvider.Authority,
RedirectUri = ConfigurationHelper.GetAppSetting("applicationRoot").ToLower(),
AuthenticationType = openIdConnectProvider.Key,
AuthenticationMode = AuthenticationMode.Passive,
ResponseType = OpenIdConnectResponseType.IdToken,
Scope = OpenIdConnectScope.Email,
TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = openIdConnectProvider.ValidIssuer,
ValidateIssuer = true,
ValidAudience = openIdConnectProvider.ClientId,
ValidateAudience = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = OnSecurityTokenValidated,
AuthenticationFailed = OnAuthenticationFailed
}
});
}
private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> arg)
{
// This is null when connection speed is set to Slow/Fast 3G
var context = HttpContext.Current;
return Task.FromResult(0);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("Login.aspx");
return Task.FromResult(0);
}
}
}
We need to access the HttpContext.Current in order to access the Session in the SecurityTokenValidated middleware. So we added the middleware as suggested in these posts:
Can OWIN middleware use the http session?
HttpContext.Current.Session is null + OWIN
This seemingly worked great, until the issues started popping up in production.
I've tried to use various combinations of UseStageMarker in various PipelinesStages (Authenticate, PostAcquireState etc), but none of them has worked.
Any help will be greatly appreciated!