R – Forms Authentication and POST requests from AJAX

asp.netforms-authentication

We have an ASP.NET app protected by forms authentication. The app uses MS AJAX heavily to call its web-services.

When the forms authentication times out, and a GET-request happens – all is fine (the user is redirected to a login page).

BUT when the forms authentication times out and a POST-request happens (ajax) – no redirect happens, instead the app returns "401 unathorized" and the browser prompts for username and password (not a login form, but a browsers built-in dialog). Of course entering ANY username/password never helps.

How do I handle this?

UPDATE: After looking with firebug, I just found out that regular POST requests redirect to login fine, it's only web-service calls that throw "401 Unauthorizes".
The difference between a regular request and web-service is URL. Which is "page.aspx" for a regular post-request and "service.asmx/MethodName" for webservices…

Best Answer

Ok, answering my own questin.

After looking into this issue and researching a bit more I found that when a web-app is protected by Forms-Authentication and the user is not authenticated, this is what happens:

  • If it's a GET-request - the user is redirected to the login page.
  • If it's a POST-request to a page - the user is redirected to the login page.
  • If it's a POST-request to a web-service - the user gets 401-unauthorized

Thats how ASP.NET works

And if a web-service is called by AJAX (xmlHttpRequest object) and returns 401 - of course the browser shows a pop-up login box.

Now, what should you do is add some code to Application_PostAuthenticateRequest that will prevent throwing 401 for webservices.

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
    if (Request.RequestType == "POST" //if its POST
        && !User.Identity.IsAuthenticated //if user NOT authed
        && !HasAnonymousAccess(Context) //if it's not the login page
        )
    {
        //lets get the auth type
        Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
        SystemWebSectionGroup grp = (SystemWebSectionGroup)config.GetSectionGroup("system.web");
        AuthenticationSection auth = grp.Authentication;
        //if it FORMS auth
        if(auth.Mode== AuthenticationMode.Forms)
        {

            //then redirect... this redirect won't work for AJAX cause xmlHttpRequest can't handle redirects, but anyway...
            Response.Redirect(FormsAuthentication.LoginUrl, true);
            Response.End();

        }
    }
}
public static bool HasAnonymousAccess(HttpContext context)
{
    return UrlAuthorizationModule.CheckUrlAccessForPrincipal(
        context.Request.Path,
        new GenericPrincipal(new GenericIdentity(string.Empty), null),
        context.Request.HttpMethod);
}