Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling session timeout in ajax calls

I'm making an ajax call using jquery to an asp.net mvc controller action:

[AcceptVerbs(HttpVerbs.Post)]         public ActionResult GetWeek(string startDay)         {             var daysOfWeek = CompanyUtility.GetWeek(User.Company.Id, startDay);             return Json(daysOfWeek);         } 

When session times out, this call will fail, as the User object is stored in session. I created a custom authorize attribute in order to check if session was lost and redirect to the login page. This works fine for page requests, however it doesn't work for ajax requests, as you can't redirect from an ajax request:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]     public class AuthorizeUserAttribute : AuthorizeAttribute     {         protected override bool AuthorizeCore(HttpContextBase httpContext)         {             if (!httpContext.Request.IsAjaxRequest())             {//validate http request.                 if (!httpContext.Request.IsAuthenticated                     || httpContext.Session["User"] == null)                 {                     FormsAuthentication.SignOut();                     httpContext.Response.Redirect("~/?returnurl=" + httpContext.Request.Url.ToString());                     return false;                 }             }             return true;         }     } 

I read on another thread that when the user isn't authenticated and you make an ajax request, you should set the status code to 401 (unauthorized) and then check for that in js and redirect them to the login page. However, I can't get this working:

protected override void OnActionExecuting(ActionExecutingContext filterContext)         {             if (Request.IsAjaxRequest() && (!Request.IsAuthenticated || User == null))             {                 filterContext.RequestContext.HttpContext.Response.StatusCode = 401;             }             else             {                 base.OnActionExecuting(filterContext);             }         } 

Basically, it'll set it to 401, but then it'll continue into the controller action and throw an object ref not set to an instance of an object error, which then returns error 500 back to the client-side js. If I change my custom Authorize attribute to validate ajax requests as well and return false for those that aren't authenticated, that makes the ajax request return my login page, which obviously doesn't work.

How do I get this working?

like image 430
Justin Avatar asked Mar 08 '11 21:03

Justin


People also ask

Does AJAX call timeout?

Session timeout has been a very common feature in Ajax-based web applications. In responsive interface, the programmer needs to delay the ajax request to achieve some task before the response. This can be achieved by using jQuery setTimeout() function.

Do AJAX calls keep session alive?

Yes it's safe. As far as load, that's up to your hardware and how you write it, but it has no worse effect than users refreshing the page (arguably less considering the overhead of an AJAX call over a standard page load). You can adjust the timeout in the web.

What is the default timeout for AJAX call?

The default value is 0 , which means there is no timeout.


2 Answers

You could write a custom [Authorize] attribute which would return JSON instead of throwing a 401 exception in case of unauthorized access which would allow client scripts to handle the scenario gracefully:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class MyAuthorizeAttribute : AuthorizeAttribute {     protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)     {         if (filterContext.HttpContext.Request.IsAjaxRequest())         {             filterContext.Result = new JsonResult             {                 Data = new                  {                      // put whatever data you want which will be sent                     // to the client                     message = "sorry, but you were logged out"                  },                 JsonRequestBehavior = JsonRequestBehavior.AllowGet             };         }         else         {             base.HandleUnauthorizedRequest(filterContext);         }     } } 

then decorate your controller/actions with it and on the client:

$.get('@Url.Action("SomeAction")', function (result) {     if (result.message) {         alert(result.message);     } else {         // do whatever you were doing before with the results     } }); 
like image 186
Darin Dimitrov Avatar answered Sep 21 '22 06:09

Darin Dimitrov


I wouldn't change JsonRequestBehavior to AllowGet. Instead I suggest:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public sealed class MyAuthorizeAttribute : AuthorizeAttribute {     public override void OnAuthorization(AuthorizationContext filterContext)     {         base.OnAuthorization(filterContext);         OnAuthorizationHelp(filterContext);     }      internal void OnAuthorizationHelp(AuthorizationContext filterContext)     {          if (filterContext.Result is HttpUnauthorizedResult)         {             if (filterContext.HttpContext.Request.IsAjaxRequest())             {                 filterContext.HttpContext.Response.StatusCode = 401;                 filterContext.HttpContext.Response.End();             }         }     } } 

and add global js ajax errors handler:

   $(document).ajaxError(function (xhr, props) {         if (props.status === 401) {             location.reload();          }    } 
like image 26
free4ride Avatar answered Sep 23 '22 06:09

free4ride