3

In my ASP.NET MVC4 application, I'm using forms authentication. One view in the app needs to get some string data via a jQuery post() call. I wrote an action that returns string. Everything works fine until the forms authentication ticket expires. After expiration, the AJAX call to the string action results in following:

  • The code within the action does not get executed
  • The action returns a HTTP 200 and not a 401
  • The action returns HTML of the Login view as string
  • Because 200 status was returned and not 401, the control ends up in jqXHR.done() instead of jqXHR.fail()

Is this expected behavior? Can I do something to make the action return a 401? Or is there something else I should be doing to handle this?

joym8
  • 4,014
  • 3
  • 50
  • 93

2 Answers2

5

Putting the code in Application_EndRequest() did not work for me. Here is what works in my case:

    protected void Application_BeginRequest()
    {
        HttpRequestBase request = new HttpRequestWrapper(Context.Request);
        if (request.IsAjaxRequest())
        {
            Context.Response.SuppressFormsAuthenticationRedirect = true;
        }
    }
Jean-François Beauchamp
  • 5,485
  • 8
  • 43
  • 77
  • This is by far the cleanest solution. I used this snippet to solve an identical problem that I was initially blaming on ServiceStack.NET. Wish I had found this 2 hours ago! – Tyler Forsythe Jan 03 '14 at 21:23
0

Yes this is the expected behaviour.

In Asp.Net 4.5 the HttpResponse.SuppressFormsAuthenticationRedirect Property has been added. But the default behaviour is still a redirect to the login page. From MSDN:

By default, forms authentication converts HTTP 401 status codes to 302 in order to redirect to the login page. This isn't appropriate for certain classes of errors, such as when authentication succeeds but authorization fails, or when the current request is an AJAX or web service request. This property provides a way to suppress the redirect behavior and send the original status code to the client.

You could use this property or try the following workaround from this answer:

protected void Application_EndRequest()
{
     if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
        Context.Response.Clear();
        Context.Response.StatusCode = 401;
    }
}

Or you could take a look at this question and answers: Forms authentication: disable redirect to the login page and pick a method that suits you to return a 401.

Community
  • 1
  • 1
Jos Vinke
  • 2,704
  • 3
  • 26
  • 45
  • Thanks for the answer. From what I've checked, no code within the string action gets executed. Then where am I supposed to insert this line of code `HttpResponse.SuppressFormsAuthenticationRedirect = true`? – joym8 Nov 04 '13 at 21:55
  • In the Application_EndRequest() which will be auto called if you add it to the global.asax. – Jos Vinke Nov 04 '13 at 21:58
  • Or you could ofcourse create an ActionFilterAttribute: http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs – Jos Vinke Nov 04 '13 at 22:03
  • Cool I see the edit thanks a lot. I think the Application_EndRequest approach is much simpler. – joym8 Nov 04 '13 at 22:06
  • You're welcome just as an addition you might want to wrap the code in an ActionFilterAttribute as I mentioned in my previous comment, for easy control over what kind of response 401/302 you want when. You could decorate one class with the attribute and leave it from another. – Jos Vinke Nov 04 '13 at 22:10