<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" 
      xmlns:thr="http://purl.org/syndication/thread/1.0">
  <link rel="alternate" type="text/html" href="http://insideria.com/2009/04/detecting-an-end-of-session-ev.html" />
  <link rel="self" type="application/atom+xml" href="http://insideria.com/atom.xml" />
  <id>tag:insideria.com,2010://34/tag:www.insideria.com,2009://34.36078-</id>
  <updated>2010-07-16T16:11:02Z</updated>
  <title>Comments for Detecting an End of Session event with jQuery (http://insideria.com/2009/04/detecting-an-end-of-session-ev.html)</title>
  <generator uri="http://www.sixapart.com/movabletype/">Movable Type 4.21-en</generator>
  <entry>
    <id>tag:www.insideria.com,2009://34.36078</id>
    <link rel="alternate" type="text/html" href="http://insideria.com/2009/04/detecting-an-end-of-session-ev.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blogs.oreilly.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=34/entry_id=36078" title="Detecting an End of Session event with jQuery" />
    <published>2009-04-29T01:19:07Z</published>
    <updated>2009-04-29T01:19:07Z</updated>
    <title>Detecting an End of Session event with jQuery</title>
    <summary>A look at how you can use jQuery and ColdFusion to detect when an session has ended while using an AJAX call.</summary>
    <author>
      <name>Raymond Camden</name>
      <uri>http://www.coldfusionjedi.com</uri>
    </author>
    
    <category term="Blogs" />
    
    <content type="html" xml:lang="en" xml:base="http://insideria.com/">
      <![CDATA[Last week I discussed jQuery's <a href="http://www.insideria.com/2009/04/playing-with-jquerys-ajaxsetup.html">ajaxSetup</a> feature. This is a way to setup global event handlers for all AJAX based events on a page. Today I whipped up what I think is a pretty good practical example of this - handling session expiration.
<br><br>
With AJAX applications giving us a lot more power than an "old school" web 1.0 page, it isn't unusual for someone to just sit on one page and fire off various operations that use HTTP to fetch and present data. This works fine until you leave the site alone for too long and your session times out. (I'm assuming most folks use a time based session, much like how ColdFusion works.) The question is - what happens in your current AJAX based application when a user's session times out? For a site that requires authentication, the results can be unexpected. If your working with JSON, then a response that contains the HTML of a login form certainly will not be a valid result. Even if you are working with simple HTML strings back and forth, it's going to be a bit wonky if all of a sudden a login form appears in the middle of a div tag that is supposed to show other results. Let's look at a simple example of this. 
<p/>
I've created a simple ColdFusion application that requires a login before using the site. You can view this demo <a href="http://www.coldfusionjedi.com/demos/sessionend/">here</a>. The login form is prepopulated with the right username and password, but just in case it doesn't work, use "admin" and "paris". Once logged in, you can enter a simple string into the text box. Hit submit, and jQuery will do a quick AJAX call to a ColdFusion file that reverses the string. (Yes, this is overkill, and yes, you can do this in JavaScript, but I wanted something quick and dirty to test with.) 
<p/>
The session is set to timeout after one minute, so if you wait a good 60 seconds and try to search again, you will see something weird...
<p/>
<a href="http://www.insideria.com/upload/2009/04/rkcPicture%201.png" class="highslide" onclick="return hs.expand(this)"><img src="http://www.insideria.com/upload/2009/04/rkcPicture%201.png" alt="rkcPicture 1.png" title="Click to enlarge" width="148"/></a>
<p/>
The login screen is now embedded in the page itself. While it may actually work in there, it probably isn't desirable. So how can we go about handling this? My first attempt was to use a method I saw used in the <a href="http://labs.adobe.com/technologies/spry/">Spry</a> AJAX framework. They have support for looking for a particular string response from the server. If that response occurs, then it is assumed that a session has ended, and you can register an event handler to support that. What's cool is - we can use another jQuery feature that I <a href="http://www.insideria.com/2009/04/jqueryserver-side-tip-on-detec.html">blogged</a> about a few weeks ago to detect an AJAX based response. The following code is ColdFusion, but could obviously be done in other server side languages as well. It runs with the Application.cfc/onRequestStart context, which basically means before every request:
<p/>
<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre>
&lt;cfif <span class="category1">not</span> structKeyExists(session, "<span class="quote">loggedin</span>") <span class="category1">and</span> listLast(<span class="category2">arguments</span>.thePage,"<span class="quote">/</span>") neq "<span class="quote">login.cfm</span>"&gt;
			
	&lt;!--- is it jquery? ---&gt;
	&lt;cfset reqData = getHTTPRequestData()&gt;
	&lt;cfif structKeyExists(reqData.headers,"<span class="quote">X-Requested-With</span>") <span class="category1">and</span> reqData.headers["<span class="quote">X-Requested-With</span>"] <span class="category1">eq</span> "<span class="quote">XMLHttpRequest</span>"&gt;
		&lt;cfoutput&gt;session expired&lt;/cfoutput&gt;&lt;cfabort&gt;
	&lt;cfelse&gt;
		&lt;cfinclude template="<span class="quote">login.cfm</span>"&gt;
		&lt;cfabort /&gt;
	&lt;/cfif&gt;
			
&lt;/cfif&gt;</pre>
</code>

</div></div>
<p/>
This code basically says the following: If we need to login, check and see if the jQuery request header, "X-Requested-With", exists and is the right value. If so, output "session expired" and kill the request. Otherwise, respond with a login form. Now we can handle expired sessions coming from both 'normal' and Ajax-based requests, and both will securely be aborted.
<p/>
Let's look at the front end code:
<p/>
<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre>
&lt;<span class="category2">html</span>&gt;

&lt;head&gt;
&lt;script <span class="category2">type</span>="<span class="quote">text/javascript</span>" src="<span class="quote">http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js</span>"&gt;&lt;/script&gt;
&lt;script&gt;
$(document).ready(<span class="category1">function</span>() {
 	
 	$.ajaxSetup({
  		success:<span class="category1">function</span>(<span class="category2">data</span>,textstatus) {
   			<span class="category2">data</span> = $.trim(<span class="category2">data</span>)
   			<span class="category1">if</span>(<span class="category2">data</span> == '<span class="quote">session expired</span>') {
    				alert('<span class="quote">Sorry, your session has expired.</span>')
    				window.location.reload(<span class="category1">true</span>)	
    			}
   		}
  	})
 
 	$("<span class="quote">#mainForm</span>").submit(<span class="category1">function</span>() {
  		<span class="category1">var</span> str = $("<span class="quote">#string</span>").val()
  		console.<span class="category2">log</span>('<span class="quote">will reverse </span>'+str)
  		$("<span class="quote">#response</span>").<span class="category2">load</span>('<span class="quote">reverse.cfm</span>',{string:str})
  		<span class="category1">return</span> <span class="category1">false</span>
  	})	
})
&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;

&lt;h2&gt;Ajax Test&lt;/h2&gt;
	
&lt;p&gt;
Enter a string <span class="category1">in</span> the field <span class="category1">and</span> I'<span class="quote">ll make an AJAX request to reverse it.
&lt;/p&gt;
	
&lt;form id="mainForm"&gt;
	&lt;input type="text" name="string" id="string"&gt; &lt;input type="submit" value="Reverse"&gt;
&lt;/form&gt;

&lt;div id="response"&gt;&lt;/div&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>
</code>

</div></div>
<p/>
Obviously you can ignore the vanllia HTML stuff, as well as the submit handler. It just calls my ColdFusion code to handle the reverse operation. But pay attention to the ajaxSetup call. I've defined a success function that will run when the AJAX request successfully completes. If the response is "session expired", then we alert the user and reload the entire page. (Alerts are evil - normally I'd use a jQuery UI Dialog.) You can demo this <a href="http://www.coldfusionjedi.com/demos/sessionend3/index.cfm">here</a>. Again, you want to login with admin/paris, play with the reverser a bit, and then sit idle for a good minute or two. Two things bug me about this though. One, it is certainly possible someone may want to reverse the reverse of session expired. Who would? Probably me. Secondly, you do actually see 'session expired' in the result for a split second before the page reloads. All together it just feels hackish.
<p/>
I did some more digging and it occurred to me - I'm using the presence of a header on the server side to detect a jQuery call. Why not use a header to detect a session expired response? I modified my ColdFusion code to do just that:
<p/>
<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre>

&lt;!--- is it jquery? ---&gt;
&lt;cfset reqData = getHTTPRequestData()&gt;
&lt;cfif structKeyExists(reqData.headers,"<span class="quote">X-Requested-With</span>") <span class="category1">and</span> reqData.headers["<span class="quote">X-Requested-With</span>"] <span class="category1">eq</span> "<span class="quote">XMLHttpRequest</span>"&gt;
	&lt;cfheader <span class="category2">name</span>="<span class="quote">sessionexpired</span>" value="<span class="quote">1</span>"&gt;
	&lt;cfabort&gt;
&lt;cfelse&gt;
	&lt;cfinclude template="<span class="quote">login.cfm</span>"&gt;
	&lt;cfabort /&gt;
&lt;/cfif&gt;
 </pre>
</code>

</div></div>
<p/>
Again, even if you aren't a ColdFusion person, you can probably read that well enough to see that for jQuery responses that have timed out, we now respond with a custom header and no text at all. 
<p/>
On the front end, I have a slighly modified ajaxSetup call:
<p/>
<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<code language="perl">
<pre>

$.ajaxSetup({
 	complete:<span class="category1">function</span>(req) {
  		<span class="category1">if</span>(req.getResponseHeader('<span class="quote">sessionexpired</span>') != <span class="category1">null</span>) {
   			alert('<span class="quote">Sorry, your session has expired.</span>')
   			window.location.reload(<span class="category1">true</span>)					
   		}
  	}
})</pre>
</code>

</div></div>
<p/>
I'm using the complete method to look for the sessionexpired response header. If it exists, I fire off the alert and do the page reload. You can demo this <a href="http://www.coldfusionjedi.com/demos/sessionend4/index.cfm">here</a>.
<p/>
Thoughts? Useful example? ]]>
      
    </content>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.36078-comment:2322575</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.36078" type="text/html" href="http://insideria.com/2009/04/detecting-an-end-of-session-ev.html"/>
    <link rel="alternate" type="text/html" href="http://insideria.com/2009/04/detecting-an-end-of-session-ev.html#comment-2322575" />
    <title>Comment from Pete Kucera on 2010-02-09</title>
    <author>
        <name>Pete Kucera</name>
        <uri></uri>
    </author>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[<p>Ray,</p>

<p>Does an AJAX call to a local component refresh the ColdFusion session?  At this time, my component simply returns a string.  I created a session timer using JavaScript and JQuery that puts an alert on the screen with the users session is about to expire.  Of course the timer is a JS variable set a minute before the actual CF session expires.  The alert has a continue button that fires the AJAX call and restarts the timer.  The problem is that my CF session does not seem to extended.  Any help?</p>

<p>Thanks,</p>

<p>Pete</p>]]>
    </content>
    <published>2010-02-09T13:13:10Z</published>
  </entry>

  <entry>
    <id>tag:www.insideria.com,2009://34.36078-comment:2322639</id>
    <thr:in-reply-to ref="tag:www.insideria.com,2009://34.36078" type="text/html" href="http://insideria.com/2009/04/detecting-an-end-of-session-ev.html"/>
    <link rel="alternate" type="text/html" href="http://insideria.com/2009/04/detecting-an-end-of-session-ev.html#comment-2322639" />
    <title>Comment from Raymond Camden on 2010-02-09</title>
    <author>
        <name>Raymond Camden</name>
        <uri></uri>
    </author>
    <content type="html" xml:lang="en" xml:base="">
        <![CDATA[<p>Yes, it does keep the same session. The HTTP request includes the same cookies from your main browser request. I'd say you have another issue in play. If you would like, I can send you some files I just wrote to confirm this.</p>]]>
    </content>
    <published>2010-02-09T14:01:38Z</published>
  </entry>

</feed
