views:

498

answers:

2

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...

+1  A: 

I see two solutions:

(1) "Heart beat" mechanism. On each page include a script that will "ping" the server by some dummy ajax request, like:

<script>
   setInterval(ping, 60000); // based on comment by John
   function ping()
   {
       $.get('/do/nothing');
   }
</script>

This way the session shouldn't expire as long as the browser window is open.

(2) On each ajax request check the status of the response. If the response has "401 unauthorized" code (or any other code different that 200), that means that the session expired and instead of loading the response into some dialog box in the page redirect the user to login page.

Conclusion based on comments:

The best solution would be to combine the two above mechanisms. Heartbeat mechanism will help to keep the session alive as long as the page is displayed in the browser. But in doesn't guarantee that for sure. The connection to the server can be broke and reopened when the session is expired. So you should check the response status anyway.

PanJanek
you can do just `setInterval(ping,60000);` passing a string to setInterval is not recommended as it's the equivalent of eval()
John Sheehan
You'll also need to call setInterval again within ping() if you want it to run more than once
John Sheehan
Any other code than 200 doesn't mean that the session has expired.
Darin Dimitrov
preventing timeouts is not a very secure option. Timeouts are OK. We just need them to work properly :)
jitbit
Darin, that's true, but code other than 200 means (in most cases) that some unexpected situation occured. Anyway, the script executing ajax request should resolve the situation somehow then.
PanJanek
A: 

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);
}
jitbit
@jazbit, can you point to some doucmentation which talks about the 401 status code for web services, i have a scenario where i am checking for session ecpiry and am doing a redirect which results 401 to the client
Dinesh Manne