jvance.com

.net, c#, asp.net, linq, htpc, woodworking

Simple Fix for ASP.NET FormsAuthentication Redirect when using AJAX

Posted in ASP.NET, C#, MVC, jQuery by JarrettV on 9/21/2012 5:39:00 AM - CST

There is one paticular annoying "feature" of ASP.NET forms authentication module that has kicked my butt many times. When a user loads a page but their session has expired, the module will take over the request and return a 302 redirect to the login page rather than the appropriate 401 unauthorized.

Specification of the correct behaviour has been part of the HTTP standard for more than 15 years now. HTTP 1.1, RFC 2616 (and before that, HTTP 1.0, RFC 1945) specify the correct behavior in sections 10.4.2 and 9.4 respectively (the verbiage hasn't changed significantly):

This "feature" has always been broken in ASP.NET and it has caused me countless hours of research and workarounds. Others have also dealt with the problem:

Back in 2008, I used the  HttpModule workaround as decribed in many of the above posts. I've also utilized an Application_OnError workaround. Unfortunately, many of these solutions require too much code, additional config, and/or tradeoffs.

I would prefer a fix directly to forms authentication, but Microsoft still hasn't addressed the problem. We could replace forms authentication with something like AppHarbor.Web.Security or MADAM. However, this can be scary to those who are unfamiliar with exercising options outside of those provided by Microsoft.

So that brings me to point of this post. How can we KISS and still fix the damn problem?

Add this to Global.asax.cs to fix the broken FromsAuthentication redirect behavior. This code will reset the 302 back to 401 for AJAX requests.

protected void Application_EndRequest()
{
    var context = new HttpContextWrapper(this.Context);

    // If we're an ajax request and forms authentication caused a 302, 
    // then we actually need to do a 401
    if (FormsAuthentication.IsEnabled && context.Response.StatusCode == 302 
        && context.Request.IsAjaxRequest())
    {
        context.Response.Clear();
        context.Response.StatusCode = 401;
    }
}

Add this javascript to your layout so that anytime you use jQuery ajax, get, or post, the client will detect the 401 and reload the page. This will cause the browser to redirect to login page.

$(document).ajaxError(function (xhr, props) {
  if (props.status === 401) {
    location.reload();
  }
});

That's all. Problem fixed with very few lines of code.

Comments

Gravatar
Posted by Carl Sharman on 1/16/2013 8:38:39 AM - CST
Brilliant! I was scouring the web for ages looking for a decent solution to this problem and not liking any of them. Then just as I was about to give up I stumbled on this. Simple and effective, thank you for sharing it.
Gravatar
Posted by mcessna on 2/27/2013 3:22:54 PM - CST
Note: Microsoft has added the HttpResponse.SuppressFormsAuthenticationRedirect property in .NET 4.5 to help address this issue (http://msdn.microsoft.com/en-us/library/system.web.httpresponse.suppressformsauthenticationredirect.aspx).
Gravatar
Posted by Steven on 5/1/2013 8:22:03 AM - CST
I've been trying less elegant workarounds for this issue, and your post provided a very clean solution. Thanks for posting this!
I'm curious, though, why your javascript doesn't just handle the 302 error in the first place?
Gravatar
Posted by prapoorna on 8/8/2013 3:43:01 AM - CST
Thank you so much.. it's working fine for my code..
Gravatar
Posted by www.google.com/accounts/o8/id?id=AItOawleqJU_jSQqRQY9hylnMDb-hebf6fXzdck on 10/7/2013 12:27:34 PM - CST
This fix worked for me until I needed to use RedirectToAction() in one of my ajax calls to an action. A 302 is sent to the browser in such cases, so I kept getting a 401 when I wasn't supposed to. I had to abandon this code because of it.
Gravatar
Posted by Mark on 2/19/2014 11:06:34 PM - CST
brilliant!
Trackback from Burbujas en .NET on 2/25/2014 10:45:46 AM - CST

ASP.NET MVC, [Authorize] y jQuery.load

Muy buenas! Estreno el blog este 2014… dios a finales de Febrero! A ver, si empezamos a retomar el ritmo
Gravatar
Posted by Soham on 5/21/2014 4:11:38 AM - CST
I would edit the condition on the server side slightly with more stringent option:
if (FormsAuthentication.IsEnabled && context.Response.StatusCode == 302 && context.Request.IsAjaxRequest() && !context.Request.IsAuthenticated) ....
Gravatar
Posted by Emil G on 4/22/2015 3:12:00 AM - CST
Awesome solution, works like a charm. Only problem is that the code snippet for global.asax is not visible in Chrome (v42.x...) Internet explorer v11 could show it though

Add Comment

Login using
GoogleYahooflickrAOL
and more
Or provide your details
Please enter your name. Please enter a valid email. Please enter a valid website.
Please supply a comment.
4.8 (11)
on 9/21/2012 1:15:27 PM - CST

Recent Entries

Valid XHTML 1.0 Strict
© Copyright 2024