Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have a scenario where I have to handle authentication of ajax requests using "Forms Authentication". Based on some search and help from my earlier stackoverflow post, I had decided to use the method described at here.

The idea is to send back a 401 response for unauthenticated requests, and then handle that in the AJAX error handler. So I have an AJAX error handler in my ASP.net MVC3 Layout page that redirects the browser to the login page when it receives 401 response on unauthenticated ajax requests. Here is the ajax error handler.

$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
    if (jqXHR.status == "401") {
        window.location.replace(loginUrl);
    }
    ....
});

This all works well on my local IIS 7.5 Server. But on the server where my site is hosted, unfortunately, I get a basic authentication popup on unauthenticated ajax requests (for example session timed out), before the AJAX error handler runs and redirects the browser to the login page. When I cancel the "Authentication Required" popup by pressing the Cancel button, the AJAX error handler then runs and I am redirected to the login page.

So, why does the browser show the authentication popup before running the AJAX error handler?

Edit: The Hosting Server is running IIS 6.

share|improve this question

6 Answers

The solution here is to write a custom HttpModule to workaround the MVC frameworks default behavior. Once I was finally able to register the module (cheers David Ebbo) it worked for me. You may want to choose your own criteria for calling SuppressAuthenticationRedirect.

public class SuppressFormsAuthenticationRedirectModule : IHttpModule {
  private static readonly object SuppressAuthenticationKey = new Object();

  public static void SuppressAuthenticationRedirect(HttpContext context) {
    context.Items[SuppressAuthenticationKey] = true;
  }

  public static void SuppressAuthenticationRedirect(HttpContextBase context) {
    context.Items[SuppressAuthenticationKey] = true;
  }

  public void Init(HttpApplication context) {
    context.PostReleaseRequestState += OnPostReleaseRequestState;
    context.EndRequest += OnEndRequest;
  }

  private void OnPostReleaseRequestState(object source, EventArgs args) {
    var context = (HttpApplication)source;
    var response = context.Response;
    var request = context.Request;

    if (response.StatusCode == 401 && request.Headers["X-Requested-With"] == 
      "XMLHttpRequest") {
      SuppressAuthenticationRedirect(context.Context);
    }
  }

  private void OnEndRequest(object source, EventArgs args) {
    var context = (HttpApplication)source;
    var response = context.Response;

    if (context.Context.Items.Contains(SuppressAuthenticationKey)) {
      response.TrySkipIisCustomErrors = true;
      response.ClearContent();
      response.StatusCode = 401;
      response.RedirectLocation = null;
    }
  }

  public void Dispose() {
  }

  public static void Register() {
    DynamicModuleUtility.RegisterModule(
      typeof(SuppressFormsAuthenticationRedirectModule));
  }
}
share|improve this answer

Wait ! I thought you said Ajax request, how can you get a popup on ajax request ? I am pretty sure somewhere else you are triggering the call to the URL even before AJAX call. From your scenario its proved that when you cancel the popup, your actual ajax request is being made and hence you can do a ajax redirect.

The idea is to send back a 401 response for unauthenticated requests, and then handle that in the AJAX error handler

You can get an ajax response only if you send a ajax request, if you send normal http request then you will get a popup. This has nothing to do with .Net or Java :)

share|improve this answer
2  
I don't know whether an ajax request, which returns 401 response, should trigger browser to show the authentication popup, but it does occur. I am making a pure unauthenticated ajax request and sending 401 response back, and this causes browser to display "Authentication Required" popup. If I return some custom response code (for example 601) for the unauthenticated ajax request, the browser then doesn't display the "Authenticated Required" popup. The ajax error handler however runs and I could then use that to redirect the browser to login url using normal request. This approach seems to work – Nirvan Jan 23 '12 at 14:44
Really the idea was to send a valid response code through ajax and handle it in callback handler and redirect :) – Abhi Jan 23 '12 at 16:33

This is a common question with an easy answer. the 401 is transformed into a 302 to the login page by the .net authorization module. The browser never see the 401 only the 302.

Of course this is not playing nicely with ajax calls.

The best solution i tryed and i'm currently using involve writing a new attribute which is catching 401 and tranform it into ... 409 which is catched by the jquery ajax handler.

It is part of a paid product so i can not give any code.

share|improve this answer
I have already handled the suppressing of 401 transformation to 302. But in the end I was returning 401, and that is triggering the authentication required popup in browser. Today, I tried with returning a custom error code, say 601 instead of 401 and then handling it in the jquery ajax error handler. This seems to work on my staging server, but can there be any pitfalls in this approach? – Nirvan Jan 20 '12 at 14:57
1  
Yes, use 409 instead it is safer. 4xx are authentication errors (normalized). 6xx is not normalized. "The 4xx class of status code is intended for cases in which the client seems to have errored." see en.wikipedia.org/wiki/List_of_HTTP_status_codes. I use 409 because i prefer to use an error code which exists as there may be no support in some browsers for non existing http return codes. My tests shows that 409 is well supported in IE, Chrome, FF and Safari browsers, and does not trigger any special browser feature (302 does). – Softlion Jan 25 '12 at 9:51

.net won't catch the 401 errors. What I did was to set the IIS error page from the default 401 page to my own static 401 page. From that page I used javascript to redirect to another handler.

share|improve this answer

IIS 6 in integrated mode? I don't believe there is any such thing, unless you're talking about integrated authentication.

My guess is that you're using a non-aspx extension, so on IIS6 this means that it's not even hitting the .net process. So, IIS is using it's own 401 error response page.

Likely, the solution is to force all requests to be handled by the .net process.

Your host will have to go into IIS properties > configuration > wildcard mappings - and map everything to the .net process.

share|improve this answer
ScottE, Sorry about the incorrect information, indeed IIS 6 does not have integrated pipeline mode. Anyway, regarding your guess, I do have a IHttpModule implementation, but it is doing what it is supposed to do, that is, returning 401 response for unauthenticated ajax requests. There is no other non-aspx extension for handling requests, so I guess the request is being handled by .net process. You can find the IHttpModule implementation that I am using in the link provided in my question. Thanks for reply. – Nirvan Jan 17 '12 at 16:50
No, in IIS 6, by default the .net process does not handle extensionless urls. So, the request isn't even hitting your httpmodule. If .net handles all requests, then you should be fine. – ScottE Jan 17 '12 at 17:15
The authentication works fine for non-ajax requests. I am using forms authentication and if there is an unauthenticated (non-ajax) request, I am correctly redirected to the Login Page. After login, in I am able to use access all the asp.net MVC Controller's action methods. The problem only occurs for unauthenticated ajax-requests, where I am sending a 401 response from my module. Unfortunately, the IIS 6 is adding two headers "WWW-Authenticate" with value of "Negotiate" and "NTLM", which is causing the browser to trigger the authentication popup. – Nirvan Jan 17 '12 at 19:04
Ok, gotcha. This is indeed a strange behaviour! Have you tried <remove/> inside the httpModules section in web config, adding your httpmodule, then adding back the relevant .net modules? Perhaps a file auth module is jumping in at the wrong place. I've had issues with this and forms auth and url rewriting. – ScottE Jan 17 '12 at 20:43

Try to remove WWW-Authenticate header from response.

share|improve this answer
I tried to remove the header "WWW-Authenticate" from the response using response.Headers.Remove("WWW-Authenticate") in the "EndRequest" event handler of the IHttpModule, but the code throws [PlatformNotSupportedException: This operation requires IIS integrated pipeline mode.]. So I doubt whether we could remove headers from the response. – Nirvan Jan 17 '12 at 18:48
1  
You can use response.Headers methods only in IIS7 integrated pipeline mode, use response.ClearHeaders() and response.AppendHeader() methods in IIS 6 – ladoch Jan 18 '12 at 15:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.