ColdFusion Session Timeout with Warning and jQuery Session Refresh

There are times when a user needs to sit on a page for a while either to read or fill out a long form. If their visit is controlled by a session timeout, for example a member site, then the session needs to be refreshed without refreshing the page. My previous post on user session warning used a page refresh to renew the session. This example will refresh the session with a jquery .post so the browser maintains state.

Much of what happens here is very similar to the previous page reload session refresh so you will see some of the same explanatory text. Changes to the code mainly affect the jquery/javascript since the variables and timers need to be reset without refreshing the page.

Javascript is needed to keep track of the time the user has been sitting on the page. The server does not know how long they have been sitting there. It only knows whether or not a request comes in during a session or after the session has expired and acts accordingly at that time. Too late for a warning.

Session Defined: Start to Finish

A session is defined as when a user begins and ends using or visiting a web site. It can be unlimited in length or strictly defined by a timeout period. If the site requires a log in or accesses sensitive data, it should time out after a period of inactivity. They can end a session by logging out or closing the browser.

Inactivity means the user has done nothing, made no requests of the web server, during a specified time. Ajax requests usually do not count.

Demo

The session time left is determined by the server, and, if you want to poll the server with an Ajax request, go for it. Javascript is used to keep track of the time left in the session.

The demo uses a simple log in with session timing handled by jquery and javascript. When the session expiration approaches, the user is warned and given an opportunity to restart the session. If the session time limit is reached, the user is prompted to log in again. If they ignore that prompt, the page automatically redirects to the log in form. In the demo this sequence of events takes 40 seconds to complete and is broken down as follows:

  1. Session timeout: 30 seconds
  2. Timeout warning: 20 seconds
  3. Session expired warning: 10 seconds
  4. Redirect to log in page: 10 seconds

Interrupting the User

The user’s attention can be diverted away from other open windows to the eminent session expiration by using a javascript alert in place of the jquery dialog box. Personal preference.

Code Breakdown

The application.cfc controls the session by creating non-persistent cookies for CFID and CFTOKEN so the session expires when the user’s browser closes. It also sets the session variable sessionStartTime. The sessionStartTime variable is used to illustrate the fact that the application.cfc function OnSessionStart only fires once. It does not fire every time a session is renewed or restarted.

RequestStartTime is set in the OnRequestStart function to provide a reference for comparison later. See “Just for Fun” section below.

<cfcomponent
    displayname="Application"
    output="false"
    hint="Handle the application.">
 
 
    <!--- Set up the application. --->
    <cfset THIS.Name = "sessionrefreshtest" />
    <cfset THIS.ApplicationTimeout = CreateTimeSpan(0,1,0,0) />
    <!--- CreateTimeSpan(days, hours, minutes, seconds) --->
    <cfset THIS.SessionTimeout = CreateTimeSpan(0,0,0,30) />
    <cfset THIS.SessionManagement = true />
    <cfset THIS.SetClientCookies = false />
 
    <cffunction
        name="OnSessionStart"
        access="public"
        returntype="void"
        output="false"
        hint="Fires ONLY ONCE when session first created and not when session renewed/restarted.">       
       
        <!---set cfid/cftoken as non-persistent cookies so session ends on browser close --->
        <cfif not IsDefined("Cookie.CFID")>
            <cflock scope="session" type="readonly" timeout="5">
                <cfcookie name="CFID" value="#session.CFID#">
                <cfcookie name="CFTOKEN" value="#session.CFTOKEN#">
                 <cfset session.SessionStartTime = Now() />
            </cflock>
        </cfif>
        
        <cfreturn />
    </cffunction>
 
    <cffunction
        name="OnRequestStart"
        access="public"
        returntype="boolean"
        output="true"
        hint="Fires at first part of page processing.">
            
        <!--- Define arguments. --->
        <cfargument
            name="TargetPage"
            type="string"
            required="true"
            />
            
        <cfset session.RequestStartTime = Now() />
                
        <cfreturn true />
    
    </cffunction>    
    
</cfcomponent>    

The log in page checks for a query string variable called ‘expired’ and, if present, deletes the session loggedin variable. This is there because the code is going to control the expiration of the session eliminating the need to compensate for browser latency. The actual session start time the time the page loads can differ by several seconds. To avoid having to add time to the session or any other fancy guesswork, when the allotted session time has expired according to the javascript timer on the page, they are done – session over.

If they are logged in, they get bumped to the index page. The rest is the logic that handles the log in form.

Note: I would not recommend handling a log in form this way. This is for demonstration only.

<cfif isDefined("url.expired") AND url.expired>
    <cfset StructDelete(session,"loggedin") />
</cfif>

<cfif isDefined("form.username") AND isDefined("form.pw") AND form.username EQ "session" AND form.pw EQ "test">
    <cfset session.loggedin = true />
</cfif>

<cfif StructKeyExists(session, "loggedin") AND session.loggedin>
    <cflocation url="index.cfm" addToken="no" />
</cfif>

Other than the log in form and a message for the user, that’s all there is to the log in page.

Handling Session Timeout

The index page handles the session timeout code. This could be a separate javascript included in every page. The first block simply determines if they are logged in. If they are not, send them to the login page. If they are, load the index page.

<!---if not logged in, send them to login page, else load the index page--->
<cfif NOT StructKeyExists(session, "loggedin") OR NOT session.loggedin>
    <cflocation url="login.cfm" addToken="no" />
<cfelse>
 <!---Load the page --->
</cfif>

Now the time variables are set and the a javascript timer is set to check the session every 10 seconds.
Javascript uses milliseconds so for clarity the time intervals multiply the number of seconds by 1,000. You could put 10000 in for 10 seconds but I think 10*1000 helps me determine that it is 10 seconds quite a bit faster. Do what is comfortable for you.

Also, a flag is set to determine if the warning dialog box has been opened and the countdown has begun.

//Your timing variables in number of seconds
//total length of session in seconds
var sessionLength = 30; 
//time warning shown (10 = warning box shown 10 seconds before session starts)
var warning = 10;  
//time redirect forced (10 = redirect forced 10 seconds after session ends)     
var forceRedirect = 10; 

$(document).ready(function() {
	//event to check session time left (times 1000 to convert seconds to milliseconds)
    checkSessionTimeEvent = setInterval("checkSessionTime(requestTime)",10*1000);
});

//event to check session time variable declaration
var checkSessionTimeEvent = "";

//time session started
var requestTime = new Date();

//initial set of number of seconds to count down from for countdown ticker (10,9,8,7...you get the idea)
var countdownTime = warning;
//create event to start/stop countdownTicker
var countdownTickerEvent = ""; 

//initially set to false. if true - warning dialog open; countdown underway
var warningStarted = false;

function checkSessionTime(reqTime)
{
	//get time now
	var timeNow = new Date(); 
	
	//clear any countdownTickerEvents that may be running	
	clearInterval(countdownTickerEvent);
	
	//difference between time now and time session started variable declartion
	var timeDifference = 0;
	
	//session timeout length
	var timeoutLength = sessionLength*1000;
	
	//set time for first warning, ten seconds before session expires
	var warningTime = timeoutLength - (warning*1000);
	
	//force redirect to log in page length (session timeout plus 10 seconds)
	var forceRedirectLength = timeoutLength + (forceRedirect*1000);
	
	timeDifference = timeNow - reqTime;

     if (timeDifference > warningTime && warningStarted === false)
        {                   
            //reset number of seconds to count down from for countdown ticker
			countdownTime = warning;
	
			//call now for initial dialog box text (time left until session timeout)
            countdownTicker(); 
            
            //set as interval event to countdown seconds to session timeout
            countdownTickerEvent = setInterval("countdownTicker()", 1000);
            
            $('#dialogWarning').dialog('open');
			warningStarted = true;
        }
    else if (timeDifference > timeoutLength)
    	{
    		//close warning dialog box if open
            if ($('#dialogWarning').dialog('isOpen')) $('#dialogWarning').dialog('close');
            
            $('#dialogExpired').dialog('open');

        }
        
     if (timeDifference > forceRedirectLength)
     	{    
        	//clear (stop) checksession event
            clearInterval(checkSessionTimeEvent);
            
            //force relocation
            window.location="login.cfm?expired=true";
        }
}

The countdownTicker function provides a countdown inside the warning dialog box to prompt the user to act now. It uses a timer that fires every second for a 5,4,3,2,1 effect inside the dialog box.

function countdownTicker()
{
	//put countdown time left in dialog box
	$("span#dialogText-warning").html(countdownTime);
	
	//decrement countdownTime
	countdownTime--;
}

And, the dialog boxes either allow the user to restart the session or, if they did nothing when the warning popped up, it logs them out by redirecting to the log in page with the expired variable in the query string. Also, it redirects to the log in if they hit the close button on the dialog box rather than the Login button on the dialogExpired dialog box.

$(function(){              
        // jQuery UI Dialog    
        $('#dialogWarning').dialog({
            autoOpen: false,
            width: 400,
            modal: true,
            resizable: false,
            buttons: {
                "Restart Session": function() {
		   //reset session on server
                  $.post("restart_session.cfm");
					
		   //reset the variables
		   requestTime = new Date();
		   warningStarted = false;
		   countdownTime = warning;
					
		   //clear current checkSessionTimeEvent and start a new one
		   clearInterval(checkSessionTimeEvent);
		   checkSessionTimeEvent = "";
		   checkSessionTimeEvent = setInterval("checkSessionTime(requestTime)",10*1000);
					
		    $('#dialogWarning').dialog('close');
                }
            }
        });
        
        $('#dialogExpired').dialog({
            autoOpen: false,
            width: 400,
            modal: true,
            resizable: false,
            close: function() {
                   window.location="login.cfm?expired=true";
            },
            buttons: {
                "Login": function() {
                    window.location="login.cfm?expired=true";
                }
            }
        });
});

The “Restart Session” button sends a post request to a ColdFusion page that sets a session variable. That act alone refreshes the session.

<!---Setting the give_me_more_time session variable refreshes the session.--->
<cfset session.give_me_more_time = true />
<!--- Below is optional. There just so you can see a response from the server in firebug. --->
<cfoutput>session.RequestStartTime: #session.RequestStartTime# session.loggedin: #session.loggedin#</cfoutput>

The dialog box contents are at the bottom of the page but they could be just about anywhere in the body.

<!--Dialog box contents-->
<div id="dialogExpired" title="Session (Page) Expired!"><p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 0 0;"></span> Your session has expired!<p id="dialogText-expired"></p></div>

<div id="dialogWarning" title="Session (Page) Expiring!"><p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 0 0;"></span> Your session will expire in <span id="dialogText-warning"></span> seconds!</div>

Just for Fun

You can view the response from the restrart_session.cfm in Firebug and compare it to the time on the page from the dumped session vars.
session time compare

Usual recommended jQuery and CF reading:

Download zip of all files

Posted in ColdFusion, jquery, Web development. Tags: , . Permalink. Both comments and trackbacks are closed.

14 Comments

  1. Brian
    February 1, 2012 at 8:32 pm | Permalink

    Yeah I did try that. it’s weird though. I made the changes as per your post but it just breaks the code somehow.

    If I put the variable ‘warning’ in there, the timer script never even executes. I tried replacing all the text with just 60000 as I want to have a 60 second countdown, and again the code just breaks still.

    If I replace the 10 with a 60, basically manually specifying what the warning var should be set to, the script executes but it still only counts down 10 seconds, ending at 50 seconds, but the funny thing is it doesn’t pause for 10 seconds after the countdown stops at 50. It just immediately redirects to the login page. So the script is malfunctioning differently in that scenario.

    Frustrating.

    -Brian

  2. Michael Grant
    February 1, 2012 at 4:13 pm | Permalink

    @Brian

    Well it’s been a long time since I looked into it, but the change I listed is what I needed to do to make it work. I can verify that was the correct fix. Perhaps you need to clear your cache?

  3. Brian
    February 1, 2012 at 4:02 pm | Permalink

    @Michael Grant

    I made that same change you suggested in the script but it doesn’t seem to rectify the issue. Any suggestions?

    -Brian

  4. December 12, 2011 at 12:28 pm | Permalink

    Thanks for the walkthrough. So easy to implement and it was a huge upgrade to add this for our users. Great work!

One Trackback

  1. By Session Timeout Warning on August 20, 2010 at 8:58 am

    [...] ColdFusion Session Timeout with Warning and jQuery Session Refresh – jensbits.com __________________ jmurrayhead If you agree with me… click the icon! If my post solved your problem, click the button in the lower right-hand corner of the post. If you like it here…throw us a few bones to help support us. Join our Folding team: DeveloperBarn Folding [...]