Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to handle session timeouts in ajax requests?

Tags:

jquery

django

Consider this Django view which will get a list of items associated to the current user:

@login_required
def list_items(request, page_number=0):
    items = Paginator(request.user.items, 5).page(page_number).object_list
    return HttpResponse(cjson.encode(items))

Obviously, it wants to use the login_required decorator, to restrict access to the view for logged-in users.

What does login_required do when a non-authenticated user tries to access the view? It returns a HttpResponseRedirect toward settings.LOGIN_URL.

Consider this JavaScript code, which calls the view:

var getPage = function(pageNumber) {
    $.ajax({
        url: "/list_items/" + pageNumber + "/",
        success: function(data) {
            $("#list_container").html(formatData(data))
        }
    });
};

Suppose settings.SESSION_COOKIE_AGE = 60 seconds.

If a user goes to page 1, reads it for 61 seconds, then clicks on the button for page 2, Django's login_required decorator will detect that the session is no longer active, and will return a HttpResponseRedirect(settings.LOGIN_URL), which will cause the success callback to get a HTML login page instead of the JSON-encoded list.

This is where it happens.
It's called by user_passes_test here.

What's the best way to handle this?

Here's a few things I've thought of:

1. The success callback should check the response, and see if it gets a login page, by whatever means (check if content-type is html, check contents, etc). But this means that we have to wrap all AJAX calls with a callback wrapper like so:

    $.ajax({
        url: "/list_items/" + pageNumber + "/",
        success: sessionExpiryCallbackWrapper(function(data) {
            $("#list_container").html(formatData(data))
        })
    });

But this is ugly, and developers might forget to do this everywhere.

2. Use $.ajaxComplete to handle all requests.

    $.ajaxComplete(globalCompleteCallback);
    $.ajax({
        success: successCallback,
        complete: completeCallback
    });

But this is the call order:

    successCallback(); // success is called before complete
    completeCallback();
    globalCompleteCallback(); // this is called after the local callback

So we only catch the redirect, after successCallback has failed, and possibly with JS errors due to the invalid data it received.

3. If login_required would return 403 on AJAX requests:

    if not user.is_authenticated():
        if request.is_ajax():
            # send 403 to ajax calls
            return HttpResponse403("you are not logged in")
        else:
            # regular code path
            return HttpResponseRedirect(settings.LOGIN_URL)

But login_required just uses user_passes_test which doesn't do this.

user_passes_test has a lot of functionality in there, so it's not such a good idea to reimplement it.

What's the best way to handle the timeouts for AJAX calls?

like image 399
Prody Avatar asked Mar 14 '12 01:03

Prody


People also ask

Does AJAX have a timeout?

Session timeout has been a very common feature in Ajax-based web applications.

What is default AJAX timeout?

The default value is 0. Which means there is no timeout.

Will AJAX call 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).


2 Answers

I would handle it by having your session timeout method check whether or not it is being requested with AJAX. If it is ajax, return a 401 not authorized(or 403 forbidden or whatever status makes sense) status code with an empty json string. Next, in your javascript, bind a global ajaxError handler that checks for that status code and handles it appropriately.

like image 155
Kevin B Avatar answered Oct 09 '22 14:10

Kevin B


You could use something like http://amplifyjs.com/ that lets you write a nice wrapper for your AJAX calls and then use its data mapping feature to check if the user is still logged in before doing the AJAX call.

This way you can have a client-side timer that sets the user to logged-out status and provides a hint so the login check doesn't need to be done before every AJAX call.

Alternatively you can use a custom decoder which asks the user to log in and retries the AJAX call if the user was logged out. It would need to store all the xhr data and callbacks it gets called with until the user logs in.

like image 44
w00t Avatar answered Oct 09 '22 13:10

w00t