Using Session Timeout And CreateTimeSpan() - Subtle Caveat
CreateTimeSpan() is a really easy way to create timeouts for both your Application and your user Sessions. It takes days, hours, minutes, and seconds and returns the numeric representation of that time span. I am sure most of you, if not all of you, are using this in your CFApplication tags or Application.cfc ColdFusion components.
CreateTimeSpan() is really cool, but it is a method call. A tiny one nonetheless, but a method call that has with it, processing overhead. Is this overhead needed? Not really. Unless you are in the habit of changing your timeouts, most likely all your CreateTimeSpan() calls return a constant value. Well, if they are returning a constant value, why bother using the method call. Readability? Sure. Maintainability? Of course.
But what if you were NEVER going to change the time span value. Could you just put in the value that CreateTimeSpan() would have returned. The answer is quite tricky in its subtlety. You can AND you can't. To demonstrate, I have set up a very simple HTML page:
<html>
<head>
<title>Session Timeout Test</title>
</head>
<body>
<cfoutput>
<p>
ID: #SESSION.CFID#<br />
Token: #SESSION.CFTOKEN#
</p>
</cfoutput>
</body>
</html>
All this does is output the SESSION information. Then, I set up an Application.cfc that defines the Application and Session management in several different ways. Depending on which variable I pass in the URL (a,b,c,d), the SESSION timeout is set using a different methodology:
<cfcomponent>
<cfset THIS.Name = "SessionTimeoutTest" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 1, 0, 0 ) />
<cfset THIS.SessionManagement = true />
<!---
Check to see which session timeout we are
going to utilize.
--->
<cfif StructKeyExists( URL, "a" )>
<!---
Here, we are going to try to use the result of
the CreateTimeSpan() without calling the method.
CreateTimeSpan() for 5 minutes results in the
number: 0.00347222222222. Use this instead of
the method call.
--->
<cfset THIS.SessionTimeout = 0.00347222222222 />
<cfelseif StructKeyExists( URL, "b" )>
<!---
Try to set an intermediary variable that stores
the result of the CreateTimeSpan() method, and
then applies that to the application setup.
--->
<cfset dtTimeSpan = CreateTimeSpan(
0, <!--- Days. --->
0, <!--- Hours. --->
5, <!--- Minutes. --->
0 <!--- Seconds. --->
) />
<!--- Set the sessiontimeout. --->
<cfset THIS.SessionTimeout = dtTimeSpan />
<cfelseif StructKeyExists( URL, "c" )>
<!---
Try to set an intermediary variable that stores
the value we built in "a" and then applies that
to the application setup.
--->
<cfset dtTimeSpan = 0.00347222222222 />
<!--- Set the sessiontimeout. --->
<cfset THIS.SessionTimeout = dtTimeSpan />
<cfelseif StructKeyExists( URL, "d" )>
<!---
Try to set an intermediary variable that stores
the value we built in "a". However, this time,
store the value as a Java Double. and then applies
that to the application setup.
--->
<cfset dtTimeSpan = JavaCast(
"double",
0.00347222222222
)/>
<!--- Set the sessiontimeout. --->
<cfset THIS.SessionTimeout = dtTimeSpan />
<cfelse>
<!---
For our default, we are just going to use
the CreateTimeSpan() directly.
--->
<cfset THIS.SessionTimeout = CreateTimeSpan(
0, <!--- Days. --->
0, <!--- Hours. --->
5, <!--- Minutes. --->
0 <!--- Seconds. --->
) />
</cfif>
<!--- Set page settings. --->
<cfsetting
showdebugoutput="false"
/>
</cfcomponent>
If I call "index.cfm" with no URL variable, we are setting the SESSION timeout using a direct CreateTimeSpan() call. This works just fine and outputs the SESSION information.
If I call "index.cfm?a", we are setting the SESSION timeout using the known result of our 5-minute CreateTimeSpan() call. CreateTimeSpan( 0, 0, 5, 0 ) returns the value 0.00347222222222. To test this for yourself see what (5 / 60 / 24) comes out to. When I try this methodology, it returns the ColdFusion error:
Element CFID is undefined in SESSION.
Clearly, the session is not working. If I call "index.cfm?b", we are setting an intermediary variable to a CreateTimeSpan() call and then setting the SESSION timeout equal to that variable. My theory here was that maybe indirect settings just would not work. This worked just fine, so it had nothing to do with where the CreateTimeSpan() was applied.
If I call "index.cfm?c", I set an intermediary variable to our known time span value, just to see if maybe directly setting the known value was breaking. This returns the same ColdFusion error as above.
I realized at this point, that it had nothing to do with directly or intermediary setting. It must have been something to do with the value returned by CreateTimeSpan() that was some how getting lost in translation to the known value. I decided that it was time to see what class of data we were even dealing with:
#CreateTimeSpan( 0, 0, 5, 0 ).GetClass().GetName()#
... returns:
java.lang.Double
Ahhhhh! We are NOT dealing with just any old number. We are dealing with a Java Double. So, now, when I call "index.cfm?d", I set the intermediary value to the JavaCast-Double value of the known time span value. This works like a charm. This could be done without an intermediary variable as well.
That is very subtle. Very subtle indeed. I think the ColdFusion error is not helpful in this case. It's some sort of casting issue. When I put the known value of the time span directly in the code, ColdFusion sees this as String value. Then, it has to cast that to a numeric value for use with the SessionTimeout value. When casting from string, it must NOT cast to a double by default and then the value is not coming through.
This seems like a bug to me. But, then again, ColdFusion is typeless... how can I hold it accountable for not knowing which type of number to cast to.
Want to use code from this post? Check out the license.
Reader Comments
Nice work Ben.
Please post the final code you use instead of createTimeSpan now.
@Topper,
I actually still use the CreateTimeSpan(). It just seemed easier than trying to come up with a work-around. And, if I was gonna go with JavaCast(), I might as well just go with CreateTimeSpan().
Makes sense. Thanks Ben.
Found this little gem of information, i was tearing my hair out converting my application.cfm to application.cfc for the first time, getting session errors left and right.
In all my old application.cfm files, specifically the cfapplication tag, using sessiontimeout="0.02084" (30 minutes) works like a charm.
Thanks
Interesting,
Well I have been trying to prevent my browser functionality from being functional. What I mean by that is when I cflogout, sessiontimeout="#CreateTimeSpan(0,0,0,0)#" in my logout file everything works just fine except I want my browser controls to stop being functional. How can I prevent the browser buttons from being functional is what I have been trying to achieve.
After selecting the logout link, you are sent to the default page for the site. After attempting to select a page that requires a login the login form appears just as it should. Remind you I am still using a Application.cfm file for my application file.
It seems that the site session ends, but the actual browse sesion does not end.
Can someone help me with this issue?
Kindest Regards,
Ty Whalin
@Ty,
I am not sure what you mean? You want to disable the back/forward controls on the browser?
Yes, that is correct/what I mean. How can it be truely logged out when the browser buttons still allow me to return to the page that I logged out from? Sure, I completely understand if I close the browser the session expires. First, I'm using a Access DB at the moment for my Clientmanagement, there are three different Application.cfm files, one for the entry of the site, the next for the secure area of the site, and third one for the logout page.
Next, between the time I wrote the original message to you, I had to restructure my navigaional links on my main menu because I noticed the spry assests were causing several problems concerning login and logout. My only means of entering the site now are via the Account link on my main menu. It goes directly to the LoginForm.cfm. After a successful login all pages are using the getAuthUser() code. When selecting the logout link provided on the main menu, the logout.cfm sends me back to my default page when you first enter the site.
But of course the problem still persist when using the browser buttons. The actual getAuthUser() displays the UserLogin name, which is defined in the cfloginuser tag. Shouldn't the getAuthUser() no longer be valid if the cflogout tag is called in the logout.cfm? It seems to me that CreateTimeSpan(0,0,0,0) should end the session and the getAuthUser() should no longer be valid.
I also placed a cflogout tag on all the pages that do not need to be securely logged in. Finally, the original question; I believe that if the sessiontimeout is set to CreateTimeSpan(0,0,0,0) and the cflogout tag is included, then the browser buttons should be disabled. The only other reason I would say, is my session is not actually loging out of the session, which is probably the case.
All help is greatly appreciated,
Neo Symmetry
P.O. BOX 453
Ocklawaha, FL. 32183
(352) 505-2968
Ty Whalin
@Ty,
It sounds like you just have a caching issue in the browser. I am sure that if the user actually tried to click on any of the links, the server wouldn't let them. Browser caching is a tough thing to deal with. I don't have a great suggestion for this.
I know this is a year old, but I am on this subject and ran across this one. Ty, you have to check each and everytime a request is made to see if the person is logged in. If they are not, then access to that page is denied. Since the application.cfm file is called each time a request is made, you do it in there. Go with an Application.cfc file and do it in the onRequest method.
BUT my question is this. Is there anyway of telling what the timeout for a session, application is set to?
I have an application where the sessions keep timing out really fast. My logic flow is this:
person comes to the site. Session timeout is set to 2 seconds.
They login and are validated the session timeout is set to 2 hours. BUT it seems that I'm actually getting something in between after login. More like 5 minutes. I'd like to be able to troubleshoot by seeing what the timeout actually is set to.
@Don,
Here is a quick overview of how to do that, and the bomb of goodness that Terry Ryan let us know about:
www.bennadel.com/blog/1686-Accessing-ColdFusion-Application-Settings.htm
:) I had just found the trick that you have to use onRequest to see the THIS variables.
But this is a good article too. I'm learning soooo much my head just asploded.
Messy.
Interesting enough, my timeout is actually going to 2 hours. So now I have to figure out why it is dropping the session.loggedin after much less time.
@Don,
It's possible that there is a session cap in your ColdFusion administrator. I don't think that can be overridden (in a greater-than way) even from the Application.cfc file.
Durned administrators!
But wait! I'M the administrator. lol
Yeh. I checked. I have the max set to 2 hours in admin too.
Now on another site I have this problem and I'm not the admin. But I figured out that the session was dying when I left the site, which is done when going through the payment process. Even tho it is set to cookies.
So I got sneaky and manually set a cookie that just tells CF that the person is logged in. THOSE admins tell me it isn't their fault. uh huh.
@Don,
So wait, a bit confused, did you fix the problem?
@Ben Nadel,
Not yet. Of course that is a smaller problem than others. Just bugs me in development because I have to login everytime I want to test it. I think I'm misssing something about sessions. Maybe an error is killing the session.
What I am doing is getting rid of the change to 2 seconds and see if that makes a difference.
Of course headed into the weekend ......
Hi Ben,
I came across this while looking for a way to set session timeouts using application.cfc based on a session variable. I have two sets of users, one I want to have a short timeout, and the other (admins) I want to have a longer timeout. I see that you were able to override the timeout settings based upon the existence of a URL struct key. Is it possible to do this by testing for the value of a session variable?
I tried moving the code to onRequestStart and it appears to update the application session timeout when I dump the application settings, but the page still times out at the default time.
@Brian,
You can't technically base the session timeout based on a session variable because the session variables are not available until *after* the request has been associated with a given application/session. As such, you have to define the session timeout before you even know anything about the given user's session.
That said, you *can* set session values based on cookies, which are available throughout the entire request. If you have two different types of users, you can set this cookie when they log in and then alter the session timeout on subsequent requests.
Take a look at my "Mastering the ColdFusion Application Framework":
www.bennadel.com/blog/1933-Mastering-The-ColdFusion-Application-Framework.htm
I have several demos on how to change the session settings throughout the life cycle of an application / user interaction.
@Ben,
Awesome, cookies will do. Thanks for the fast response. You rock!
@Brian,
No worries my man - good luck with your timeouts.
I have website with a mailinglist admin login
/maillist/admin/
and another login for same site in /secure/
If i put an application.cfc in /maillist/admin/ folder with the same application name and a longer timeout,
would that work to allow it to share session variables with the /secure/ programs,
but if the user went back to those other programs the shorter timeout would apply?
I want to give users more time in /maillist/admin/ section, share session variables but not increase the main session timeout.
i have a application.cfc in root.
Give me a suggestion !!!