Ask a Jedi: How can you timeout a session in an Ajax-based application?
Todd Sharp, who is normally the one providing me CF8-Ajax based answers, asked me this question today:
Imagine you have an Ajax-based site. The front end acts like a dashboard. In other words, the user never leaves the page, but executes various actions that do Ajaxy-type things on the back end. But if the user sits by and does nothing, how can I recognize a session timeout when the next Ajax-based call is done?
So Todd and I have been talking about this off and on all morning, so pardon the randomness of the response. In general what I recommended was this:
Build a Ping service. Your application can ping the server every minute. As you know - this will keep your session alive. However - what you can do is simply use onSessionStart to define a session variable named lasthit. In your onRequestStart - update lasthit to now(). In your ping, simply pass a URL parameter that will flag onRequestStart to not update the variable.
Your ping request can handle the timeout anyway it wants. You can return the number of minutes left till timeout, and if less than 5 minutes, use a new ColdFusion-Ajax window to prompt the user. (Much better than an alert!) You can not warn them at all, but use location.href to push them to a timeout page (and do a manual structClear on the session).
On an interesting side note - Spry has native support for this. By returning a particular string instead of your normal XML/JSON packet, you can fire off a 'Session Ended' event. (See my article on this feature here.) ColdFusion's Ajax support does not - although you can kind of do it by using the onError support in ColdFusion's various Ajax-based tags. This support gives you access to the error thrown, so you could introspect the error and run your own handler for the session ending then.
So as always - I'm open to suggestions and options. Has anyone built something like this into an Ajax-application? Everything I've built so far with Ajax only uses Ajax in parts of the site - not everywhere.
Comments
The interval can be set via setTimeout() which allows you to specify a method to call at specific intervals.
If the session has timed out, the callback function pops an alert message and redirects you to the login screen. If the session is active, then the Ajax call continues as normal.
http://www.pengoworks.com/workshop/jquery/session_...
I'd suggest just using HTTP Response Headers to notify your Ajax requests if the user's session has expired. Using this technique you don't have to worry about making additional calls to see if a session's alive, since it's all controlled via HTTP response headers.
I've blogged about the technique here:
http://blog.pengoworks.com/blogger/index.cfm?actio...
Or you can go straight to the example code:
http://www.pengoworks.com/workshop/jquery/session_...
We use a cookie and a JS global variable, and have the server write out the session timeout variable into another JS global.
We attach an event listener to dojo.io.bind and update the cookie with the current time when we make a request to the server, plus save the time to the global.
We use a cookie so multiple browser windows talking to the same server will all share the same last-update time. After updating the cookie we set a timer to session timeout - 1 minute, and if that goes off we check the cookie to see if we were the last window to talk to the server. If we weren't (cookie is newer than our global) we just go back to sleep. If we were (cookie is still the one we set) then we prompt the user that their session is about to expire and let them send a dummy request to keep the session alive. Being the last window to contact the server also implies that this window is the frontmost talking to that server (we don't do any of this for background polling calls, since we don't want those to keep the session alive).
Well, the code is on the jQuery group in case it is of interest to anyone. It does illustrate a nice trick for augmenting an existing function - the $.expire function could be applied to any JavaScript function, not just Ajax calls.
http://groups.google.com/group/jquery-en/msg/e1b80...
There is one error in the posted code - the line reading "fn.apply( this, arguments );" should be "return fn.apply( this, arguments );".
Timeouts torment your users. Many users will walk away from their computers and expect to keep working the next day. Amazon keeps your shopping cart open for weeks -- they'd be throwing away $$ if they timed out your session.
If your architecture needs timeouts in order to scale, your architecture is at odds with the interests of your users. Get a new architecture.
Back when I was involved in developing an heavy AJAX based .NET Web application for a leading Media company in the 1.1 days, this idea was added to the scope as a 'very nice to have feature'. And it has become one of 'The fundamentals things to do for an AJAX based Web Application that enforces Authentication'. We use a similar approach with the Session ping interval value being set to 19 minutes (your_session_timeout_minutes - 1 minute considering other delays). This way it does less-frequent pinging - 3 per hour which is MUCH MUCH ACCEPTABLE than having the Web application expire every 20 minutes.
Hope this helps!
Sheriff
Just a thought here. If all of your Ajax calls go through the same function at the end (which i assume it is), this problem shouldn't be too hard to solve.
This is how I would approach it:
1. When a person logs in, the javascript served carries a timeout variable
2. As soon as they land on the page, you start counting down with the timeout variable (simple as setTimeout(var_timeout, logout))
3. On every Ajax call, you reset the setTimeout
4. Every Ajax call will return a flag of if the session has in fact timed out or not on the server side
5. Step 4 is literally to protect you from a person trying to screw around with your script (let's say using firebug?)
6. If the Ajax call returns the flag saying session timed out on server side, you do a self.location and take them to the login page again.
Again, just a thought. I'm sure it's not 100% perfect. :)
I use the application.cfc onRequestStart to set a cookie containing the date/time, so any activity from a user will get the cookie stamped.
Then, on the HTML returned I provide JavaScript with the current date/time to compare against the clients date/time and find the offset using Date().getTime() in ms. Useful since everyone's clocks aren't synced. It's then just a matter of comparing the current time (accounting for the client/server offset) against the cookie time.
So if there's an Ajax request it'll update the cookie and thus the timer on the HTML page. That way there's no need for any type of "ping" service or making sure that all Ajax code resets the timer.
As mentioned, security. Even ignoring things like public terminals and credit cards etc. some industries have requirements about leaving users logged in; if a user walks away from their desk without logging out someone's medical records might get changed.

