jvance.com

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

Jarrett's Tech Blog - For September 2012

  1. Simple Fix for ASP.NET FormsAuthentication Redirect when using AJAX

    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.

    Posted by JarrettV on September 21 at 5:39 AM

© Copyright 2024