Link to home
Start Free TrialLog in
Avatar of futr_vision
futr_vision

asked on

How to redirect a user trying to use the back button once they logged out?

I have an application that is behind authentication. Currently, when a user logs out they can hit the back button and reenter the application. This should not happen. Once they log out their session shoudl expire correct? I used DreamWeaver to implement the logout but I am not sure what else I need to do to expire the session.

NOTE: Once they log out they are dumped on the login page.
Avatar of erikTsomik
erikTsomik
Flag of United States of America image

once they log out you must clear all session variables

<cfset StructDelete(session)>

and you action page should also check if the session is exist the redirect , that should be the first thing you do

Something like this
<cfif not isDefined("session.SessionNAme")>
  <cflocation url="Take back to the main page" addtoken="No">
</cfif>
Avatar of 8riaN
8riaN

One customary solution is to put all the pages that need login protection into a subdirectory - then have the application.cfc (or cfm) check the session right at the beginning and either redirect them back to a general page, or put up a login form. In the latter case, I like to save the URL and FORM variables so that if they succeed in logging in, they don't lose any work (like if they timeout filling out a large form).
Avatar of futr_vision

ASKER

Below is the code that is generated by the built in DreamWeaver server behavior. It is suffiecient for preventing navigation directly to the page but I guess it fails when a user uses the back button because the session is not expired on the logout page( which is my login page).

I'll need to place <cfset StructDelete(session)> on the login page to prevent the user from using the back button correct?

<cfif IsDefined("URL.MM_logout") AND URL.MM_logout EQ "1">
  <cflock scope="Session" type="Exclusive" timeout="30" throwontimeout="no">
    <cfset Session.MM_Username="">
    <cfset Session.MM_UserAuthorization="">
  </cflock>
  <cfset MM_logoutRedirectPage="index.cfm">
  <cfif MM_logoutRedirectPage EQ "">
    <cfset MM_logoutRedirectPage=CGI.SCRIPT_NAME>
  </cfif>
  <cfset MM_logoutQuery=ListDeleteAt(CGI.QUERY_STRING,ListContainsNoCase(CGI.QUERY_STRING,"MM_logout=","&"),"&")>
  <cfif MM_logoutQuery NEQ "">
    <cfif Find("?",MM_logoutRedirectPage) EQ 0>
      <cfset MM_logoutRedirectPage=MM_logoutRedirectPage & "?" & MM_logoutQuery>
      <cfelse>
      <cfset MM_logoutRedirectPage=MM_logoutRedirectPage & "&" & MM_logoutQuery>
    </cfif>
  </cfif>
  <cflocation url="#MM_logoutRedirectPage#" addtoken="no">
</cfif>
<cflock scope="Session" type="ReadOnly" timeout="30" throwontimeout="no">
  <cfset MM_Username=Iif(IsDefined("Session.MM_Username"),"Session.MM_Username",DE(""))>
  <cfset MM_UserAuthorization=Iif(IsDefined("Session.MM_UserAuthorization"),"Session.MM_UserAuthorization",DE(""))>
</cflock>

Open in new window

correct . so once the logout is pressed the delete all the session variables.
Below is the code that currently resides on the login page.  

Also, in the code above the logout page should be login.cfm and not index.cfm
<cfif IsDefined("FORM.username")>
  <cfset MM_redirectLoginSuccess="home.cfm">
  <cfset MM_redirectLoginFailed="login.cfm">
  <cfquery  name="MM_rsUser" datasource="QRA">
    SELECT username,password,access_level FROM dbo.Users WHERE username=<cfqueryparam value="#FORM.username#" cfsqltype="cf_sql_clob" maxlength="10"> AND password=<cfqueryparam value="#FORM.password#" cfsqltype="cf_sql_clob" maxlength="50">
  </cfquery>
  <cfif MM_rsUser.RecordCount NEQ 0>
    <cftry>
      <cflock scope="Session" timeout="30" type="Exclusive">
        <cfset Session.MM_Username=FORM.username>
        <cfset Session.MM_UserAuthorization=MM_rsUser.access_level[1]>
      </cflock>
      <cfif IsDefined("URL.accessdenied") AND false>
        <cfset MM_redirectLoginSuccess=URL.accessdenied>
      </cfif>
      <cflocation url="#MM_redirectLoginSuccess#" addtoken="no">
      <cfcatch type="Lock">
        <!--- code for handling timeout of cflock --->
      </cfcatch>
    </cftry>
  </cfif>
  <cflocation url="#MM_redirectLoginFailed#" addtoken="no">
  <cfelse>
  <cfset MM_LoginAction=CGI.SCRIPT_NAME>
  <cfif CGI.QUERY_STRING NEQ "">
    <cfset MM_LoginAction=MM_LoginAction & "?" & XMLFormat(CGI.QUERY_STRING)>
  </cfif>
</cfif>

Open in new window

Avatar of gdemaria
You need to look at two things... how is your code checking to see if a session is lgged in and how is the code logging out the user.   These two things have to be in sync.

For example, if you test for session.user_id to see if a user is logged in, then when they log out, you need to clear the value of that variable.

oops, i left my browser open too long and missed all the responses.. Ignore my last comment as it was addressed already.
Changes in session variables often do not "stick" when using cflocation after the change

try removing this line and test if it's working

<cflocation url="#MM_logoutRedirectPage#" addtoken="no">


Hmmm. It though an error at first cause it was looking for parameters. I altered it to look like this:

<cfset StructDelete(session,"MM_Username",true)>

But that doesn't work either.

I apologzie for my ignorance but I am very new to programming and CFML. Seems to me that Adobe's(Macromedia's) script should account for this behavior in some way. I also have issues with a user not being redirected to the login page when their session expires and they try and perform an action on a authenticated page. Instead  of the redirect the page throws and error stating that MM_User is undefined.
are you using application.cfm or application.cfc ?
Have you check that you aren't either
a) logging in again using the stored form submission on the login page? (e.g. hitting back to the login page and hitting refresh)
or
b) Looking at a page which the browser cached and deciding you aren't logged out?

The test is to go back, not hit refresh, but hit some other link or button.

If the situation is a) - there's really no problem to fix. if it's b) you could add meta tags to control caching.

Otherwise, the problem is in the logic that decides whether you can see the restricted page because the dreamweaver code should work fine if SESSION.MM_UserAuthorization is checked in the restricted pages.

Just refreshed the page and it seem even more like the problem isn't logout or login, but the way the restricted pages are enforced.

What about application.cfc/cfm - and how do you specify restricted pages?
Hmmm. I have an application.cfc and an application.cfm in my local folder but only application.cfm on the server. Below is the code I have in the application.cfm  and application.cfc files.
<!--- application.cfm --->
<cferror type="exception" template="error.cfm">
<cferror type="request" template="error.cfm">
<cfapplication name="authenticate"
 sessionmanagement="yes"
 sessiontimeout="#CreateTimeSpan(0,0,20,0)#">
 
 
<!--- application.cfc --->
<cfcomponent>
	<cffunction name="onRequestStart">
		<cfargument name="thisRequest" required="true"/>
		<cfinclude template="2application_include.cfm">
		<cfif GetAuthUser() NEQ "">
			<cfoutput>
				<cfform action="2authenticate.cfc?method=logout&loginType=#args.authLogin#" method="Post">
					<cfinput type="submit" Name="Logout" value="Logout">
				</cfform>
			</cfoutput>
		</cfif>
	</cffunction>
 
</cfcomponent>

Open in new window

Going to need 2application_include.cfm too
And where do I get that? I'm not even sure where I got the code for the application.cfc file. That applicaiton.cfc page might be left over from the ColdFusion Login Wizard built into DreamWeaver. I'm using the User Authentication server behaviors instead.
hmm, look for it in the same dir as application.cfc
Can you give an example of a page that should require authentication? What coldfusion code does it have in the beginning, what dir does it live in, and if it's not the webroot, is there another application.cfc file in that dir?
THe code for the page that requires authentication is in a prevous message. All files are in the root directory with the exception of the cfc's which are in their own directory. The application.cfc is not on the server that hosts this application - only application.cfm. Do I need to move the applciation.cfc mentioned above to the server? I thought I had read that you need to use one or the other.
The 2application_include.cfm file is not there. The application.cfc is probably a left over file from when I tried to use the ColdFusion Login Wizard in DreamWeaver. If I need to to have an applicaiton.cfc file on the server I probably need to engineer a new one or adjust the existing one.
Hmm, must be that first page, but the logic you posted doesn't prevent anything, just checks for logout and sets variables scope versions of the session vars - is there logic down lower that tests MM_UserAuthorization  and redirects?

You use either Application.cfm or Application.cfc - the latter wins out if there's both. Maybe try making a new site with one page and adding the login server behavior you are using to find out what files are getting created, and what's left over from before.

Did you test whether you really even still logged in after hitting back?  I'd log in, browse someplace else, log out, go back to the "someplace else" page, and try a restricted link from there - or a refresh - or both, and see if we're dealing with server session problems or just the browser cache.
You can have one or the other (.cfm or .cfc)    If the .cfc is there, however, it takes preference and the other will not be used by Coldfusion.

I would remove the application.cfc from the folder


You can put this code in your application.cfm file

Set up global variables for you to use in every file...

 request.MM_Username
 request.MM_UserAuthorization

It will login the user if they submit the login form

It will open login.cfm file if the page requires authentication

In the first line, put a list of the pages that require login

<cfset securePages = "index.cfm,cart.cfm"> <!--- list of files that should be secure ---->
 
<!---- process a login, if there is one ---->
<cfif IsDefined("FORM.username")>
  <cfquery  name="MM_rsUser" datasource="QRA">
    SELECT username,password,access_level 
	 FROM dbo.Users 
	WHERE username=<cfqueryparam value="#FORM.username#" cfsqltype="cf_sql_clob" maxlength="10"> 
	AND password=<cfqueryparam value="#FORM.password#" cfsqltype="cf_sql_clob" maxlength="50">
  </cfquery>
  <cfif MM_rsUser.RecordCount NEQ 0>
    <cflock scope="Session" timeout="30" type="Exclusive">
	  <!--- set your global variables, to be used through out the application ---->
      <cfset request.MM_Username = FORM.username>
      <cfset request.MM_UserAuthorization = MM_rsUser.access_level[1]>
      <!--- set your session variable, this will remember your session ----->
      <cfset Session.MM_Username = request.MM_Username>
      <cfset Session.MM_UserAuthorization = request.MM_UserAuthorization>
    </cflock>
  <cfelse>
    <cfset loginError = "Invalid username/password combination">
  </cfif>
<cfelseif isDefined("session.MM_userName")> <!---- see if session is already defined, if so, use those values ---->
  <cflock scope="Session" type="ReadOnly" timeout="30" throwontimeout="no">
     <cfset request.MM_Username = Session.MM_Username>
     <cfset request.MM_UserAuthorization = Session.MM_UserAuthorization>
  </cflock>
<cfelse> <!---- no login, no session, just start with empty values ---->
  <cfset request.MM_Username="">
  <cfset request.MM_UserAuthorization="">
</cfif>
 
<!---- do you need to be logged in?  If yes, and you're not, then open the login page ---->
<cfif listFindNoCase(securePages,listFirst(cgi.script_name,"/\")) and len(request.MM_username) eq 0>
  <cfinclude template="login.cfm">
  <cfabort>
</cfif>

Open in new window

1. The only file that is on the server is the application.cfm
2. The only code in that file is what I included in a previous post
3. I have logged out, browsed to another page and then tried to access a restricted page and I get redirecte to the login page as I should. If I log out, which dumps me on the login page, and then use the back button I can access the application again. I am even able to make changes as if I never logged out.
There is something you're not showing.   The application.cfm file does not define your session variables and never checks to see if you're user is logged in.   That's pretty much where it is normally done.

Since it's not in there, do you know where it is?

For example, where is your logout code (that you posted 07/02/09 02:12 PM)?
What is calling that if it's not in your application.cfm file where it should be?
Well, there is no code posted here that actually redirects you if you're not logged in, and yet you say that functionality is working.  I see logout code and rescoping of the likely-looking MM_Username and MM_UserAuthorization variables, but the only redirect is when URLMM_Logout or FORM.Username is present.

Can you search all the files for: cflocation
that would most likely turn up the code that's doing the work, I suspect.

On pages with "restrict access to page" server behavior selected, you should find code like that below:
<cfif MM_Username EQ "" OR MM_UserAuthorization EQ "" OR ListFind("1,2", MM_UserAuthorization) EQ 0>
    <cfset MM_referer=CGI.SCRIPT_NAME>
    <cfif CGI.QUERY_STRING NEQ "">
        <cfset MM_referer=MM_referer & "?" & CGI.QUERY_STRING>
    </cfif>
    <cfset MM_failureURL="login_failed.cfm?accessdenied=" & URLEncodedFormat(MM_referer)>
    <cflocation url="#MM_failureURL#" addtoken="no">
</cfif>

Open in new window

Ahh. Looks like I did miss some code when I pasted it in the post at  07/02/09 02:12 PM). Here is the code that is on the application page once a user logs in. Hope that helps.
<cfif IsDefined("URL.MM_logout") AND URL.MM_logout EQ "1">
  <cflock scope="Session" type="Exclusive" timeout="30" throwontimeout="no">
    <cfset Session.MM_Username="">
    <cfset Session.MM_UserAuthorization="">
  </cflock>
  <cfset MM_logoutRedirectPage="login.cfm">
  <cfif MM_logoutRedirectPage EQ "">
    <cfset MM_logoutRedirectPage=CGI.SCRIPT_NAME>
  </cfif>
  <cfset MM_logoutQuery=ListDeleteAt(CGI.QUERY_STRING,ListContainsNoCase(CGI.QUERY_STRING,"MM_logout=","&"),"&")>
  <cfif MM_logoutQuery NEQ "">
    <cfif Find("?",MM_logoutRedirectPage) EQ 0>
      <cfset MM_logoutRedirectPage=MM_logoutRedirectPage & "?" & MM_logoutQuery>
      <cfelse>
      <cfset MM_logoutRedirectPage=MM_logoutRedirectPage & "&" & MM_logoutQuery>
    </cfif>
  </cfif>
<!---  < url="#MM_logoutRedirectPage#" addtoken="no">
---></cfif>
<cflock scope="Session" type="ReadOnly" timeout="30" throwontimeout="no">
  <cfset MM_Username=Iif(IsDefined("Session.MM_Username"),"Session.MM_Username",DE(""))>
  <cfset MM_UserAuthorization=Iif(IsDefined("Session.MM_UserAuthorization"),"Session.MM_UserAuthorization",DE(""))>
</cflock>
<cfif MM_Username EQ "" OR MM_UserAuthorization EQ "" OR ListFind("admin,user",MM_UserAuthorization) EQ 0>
  <cfset MM_referer=CGI.SCRIPT_NAME>
  <cfif CGI.QUERY_STRING NEQ "">
    <cfset MM_referer=MM_referer & "?" & CGI.QUERY_STRING>
  </cfif>
  <cfset MM_failureURL="login.cfm?accessdenied=" & URLEncodedFormat(MM_referer)>
  < url="#MM_failureURL#" addtoken="no">
</cfif>
<cfparam name="SESSION.MM_Username" default="1">
<cfset CurrentPage=GetFileFromPath(GetTemplatePath())>
<cfset CurrentPage=GetFileFromPath(GetBaseTemplatePath())>

Open in new window

I'm going to assume that line 30 really reads
<cflocation url="#MM_failureURL#" addtoken="no">
Did you accidentally replace cflocation with a null? If so, you could look for addtoken - it's only really used in the cflocation tag.

Ok, so now we can clearly see how everything should be working - and it looks fine.

What browser/version are you using, and when you hit the back button does it pop up an alert box that says stuff about resubmitting form fields?

Yes. Line 30 does read as you indicated.

I have tried in both FF 3 and IE 8. I do not get a pop up in either.
Does this page exist on the web so I could see the behavior first hand?  The only reason I can think of that you should be logged in again is if the login form is getting resubmitted - all the code looks like it should work.

Otherwise, add something like this to end of the cfapplication file and see what you're dealing with.

<cflock scope="session" type="readonly" timeout="5">
<cfdump var="#session#">
</cflock>
The application does exist on the web however I can not give you access to it due to the nature of the content. I did put the code above at the end of the page and it returned a table with values for:

cfid
cftoken
mm_userauthorization
mm_username
sessioid
urltoken
When I log out I have the same values at the bottom of the login page minus the mm_userauthorization and mm_username. If hit the back button all of the data is populated as if I never left the page.
I understand.

I'm still not sure we've ruled out the browser cache. When you hit the back button, the HTML generated by the last run through the code is just redisplayed on the browser unless you hit refresh, the code isn't actually run on the server again otherwise. It's what happens when you either refresh or try to navigate to a protected page from there that you learn the new contents of the session.  If the page you land on going back is the processing page for a login attempt, then you'll just log in again when you hit refresh since the forms data is part of the page request.

Can you construct a test that eliminates resubmitting the login form and eliminates browser-cached HTML as causes of the symptom?
Ok. Here is what I have done and I think you are correct.

1. Log in
2. Log out
3. Hit the back button
4. Refreshed pages

Step 4 does push me over to the login page which is the correct behavior. Assuming that everything is working correctly on that end how do we deal with the browser cache? This page is using a HTML cfgrid which is AJAX-based. When you use the back button the grid is rerendered and the call to the cfc is made etc.... In the end I can access and overwrite data even though I logged out.
When you hit the Refresh, you're resubmitting the login credentials.
Can you click any links between login and logout? then the back button will take you to a page which will refresh without resupplying the login credentials.

If you can verify the correct behavior in that scenario, and are still concerned about the login credentials persisting in the browser cache, the next step is a pragma-cachey thing I could look up for you.
I'm not sure I am getting what you are saying. This is what I think you mean.

1. Log into the application
2. While in the application go to a different website
3. Hit the back button and see if I am still able to access the application.

I've done the above and then even hit refresh after the back button and I am still able to view and use the application. Navigating away from the application does not expire the session.

In what I think is a related issue if I let the session timeout and then click on any of the links within the application I get an error in my browser

Element MM_USERNAME is undefined in SESSION.
The error occurred on line 4.
You should take out the StructDelete(Session) which you put in earlier to fix that error.

And I don't mean go to another website.  My point is that if you login then immediately click logout, then go back, then refresh you will produce exactly the same effect as if you logged in again, because the login/password are resubmitted from the history when you hit refresh.

You need to get a history step that does not have the login credentials associated with it
This could be as simple as clicking in the address bar and just hitting enter to browse directly to the page without submitting a login form.  This will verify that logout is indeed working, and that the only problem you have is the login credentials persisting in the browser history (which is what I now suspect has been your only problem all along.)

You could try putting these two lines at the very top of the secured page(s):
  <cfheader name="Expires" value="#Now()#">
  <cfheader name="Pragma" value="no-cache">

That might solve your problem completely
I think I am a bit closer now. Right before I implemented the code you jsut posted I noticed that I could log out in IE and then type the URL of the protected page directly in the browser and be able to navigate back to that page. The code you just provided seems to have fixed that. FF did not have this issue. However, I can still log out and then back button to the previous page. What if I introduced a new page as the "logged out" page with a link to log back in? Right now the get looped back to the log in page when they log out.
ASKER CERTIFIED SOLUTION
Avatar of 8riaN
8riaN

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Am I leaving:

<cfheader name="Expires" value="#Now()#">
  <cfheader name="Pragma" value="no-cache">


and adding above it

<cfheader name="Cache-Control" value="no-cache">

yes, but replace the cache-control no-cache with the cache-control no-store if the first one does help.
Browsers all implement caching a little differently and controlling it can be difficult.
This fixed the problem in IE

<cfheader name="Cache-Control" value="no-cache">

This fixed the problem in both

<cfheader name="Cache-Control" value="no-store">


Do I need to remove ?

<cfheader name="Expires" value="#Now()#">
  <cfheader name="Pragma" value="no-cache">
you could just leave them in for random other browsers unless you know your target audience will only ever use FF or IE.  In any case, they won't hurt anything.
Awesome. Thanks.  Now I need to fix that timeout error. Should I open another question for that one?
nah, all the info we'll need is right here already.  Can you either figure out which line of which template is breaking and post those lines, or post the whole error message?
Hmm. Can't get it to time out. I even cranked the timeout down to "1"
Thanks for the persistence.
Odd. I can't replicate the error. I wonder if it was inadvertently fixed.
Congratulations, hope everything goes well from here on out.
I was wrong. The timeout error is still there. Below is the error I see in Firebug under the Response tab.  It looks like it is checking for the username but since the session expired it can not find it therefor it throws an error when trying to retrieve data from the DB. What I need to be able to do is kick the user back out to the login page if their session times out.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>ColdFusion Error</title>
</head>
 
<body>
<h2>ColdFusion Error</h2>
 
<ul>
<li><b>Your Location:</b> 75.68.61.220
<li><b>Your Browser:</b> Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)
<li><b>Date and Time the Error Occurred:</b> {ts '2009-07-03 08:14:13'}
<li><b>Referrer:</b></li>
<p>http://www.mysite.com/mypage.cfm</p>
<li><b>Template:</b></li>
<p>/CFC/test.cfc</p>
<li><b>Message Content:</b>:
<p>Element MM_USERNAME is undefined in SESSION.  <br>The error occurred on line 54.</p>
<li><b>Query String:</b></li>
<p>method=getClients&returnFormat=json&argumentCollection=%7B%22page%22%3A1%2C%22pageSize%22%3A25%2C%22gridsortcolumn%22%3A%22CLIENT_NAME%22%2C%22gridsortdir%22%3A%22ASC%22%7D&_cf_nodebug=true&_cf_nocache=true&_cf_clientid=F1ABACDE5A3E52B938FBBE7A0614E438&_cf_rc=2</p>
 
<li><b>Generated Content:</b></li>
<p>
 
</p>
</ul>
 
</body>
</html>

Open in new window

what does test.cfc do? esp. on and around line 54?

If you wrote it yourself, why is it checking the session? If it's generated, why doesn't it have code like this:
<cflock scope="Session" type="ReadOnly" timeout="30" throwontimeout="no">
  <cfset MM_Username=Iif(IsDefined("Session.MM_Username"),"Session.MM_Username",DE(""))>
  <cfset MM_UserAuthorization=Iif(IsDefined("Session.MM_UserAuthorization"),"Session.MM_UserAuthorization",DE(""))>
</cflock>

If it's called from a restricted page (mypage.cfm), why does it even call the cfc at all instead of just redirecting? What is mypage.cfm doing, calling the cfc as a webservice?
Here is line 54. It's part of the initial query that returns data to the grid.
		    FROM Clients WHERE owner = <cfqueryparam value="#SESSION.MM_Username#" cfsqltype="cf_sql_clob" maxlength="10"> 

Open in new window

This error occurs when any of the links on the page are clicked (including logout) and when any actions are taken on the cfgrid
Couple of problems there:

1) This code shouldn't run at all if they aren't logged in - maybe the cfcomponent is called before the session checking stuff in mypage.cfm?
2) Session variables shouldn't be accessed outside of a <cflock> statement - Use this code in front of the query:
<cflock scope="Session" type="ReadOnly" timeout="30" throwontimeout="no">
  <cfset MM_Username=Iif(IsDefined("Session.MM_Username"),"Session.MM_Username",DE(""))>
  <cfset MM_UserAuthorization=Iif(IsDefined("Session.MM_UserAuthorization"),"Session.MM_UserAuthorization",DE(""))>
</cflock>
then access MM_Username instead of SESSION.MM_Username on line 54.
3) Don't round-trip to the database if the query has a parameter guaranteed to produce no results - if you can't figure out how to properly prevent this code from getting run if the session is timed out (#1), add a <cfif MM_Username NEQ ""> between the <cflock> and the <cfquery>
You want to ensure your session variables always exist, that's much easier than testing for them to be present and have a value.   I had suggested copying them into request variables for global use to prevent locking every where.   If you don't want to do that, then you could at least put this in your application.cfm file to ensure the variables always exist...

<cfif NOT isDefined("session.MM_userName")> 
  <cflock scope="Session" type="EXCLUSIVE" timeout="30" throwontimeout="no">
     <cfset Session.MM_Username = "">
     <cfset Session.MM_UserAuthorization = "">
  </cflock>
</cfif>
 

Open in new window

gdemaria is right in general, it's a good idea, but I believe the IsDefined test should be inside a lock too -either the EXCLUSIVE one, or it's own READONLY one.
I'm not completely comprehending this discussion or maybe I misstated what is happening.  When a user timesout nothing happens until the click on a link that goes to another secured page or tries to interact with the grid in some way.  Is automatically redirecting the user to the login page when they timeout a simple solution?
No, it's more complicated, you'd need to set a second timer on the client's browser to auto-refresh the page - but you shouldn't get far enough when you click a link to interact with the grid at all, and the dreamweaver produced authentication code does not fail when the session is empty.  The query you wrote that causes the error should not be running if the session has timed out, authentication code should be redirecting you to the login page before you get there.  Is there only one authenticated page?
Three pages. Two of them you navigate to via links. The third runs in a cfwindow when the user performs a search.
Do all of them die in that test.cfc line 54? You could simulate a timeout by making a page, or adding to a page code that does a <cfset StructDelete("Session");
Well, they don't all die on line simply because they access different queries in my CFC. I do believe they all die when  they come across SESSION.MM_Username
All the pages that access the cfc should have the "restrict access" or whatever it's called server behavior from dreamweaver, that puts in code like the below:

That code should be moved to the top of all of those pages - that should take care of the problem by redirecting before the cfc is ever accessed.
<cflock scope="Session" type="ReadOnly" timeout="30" throwontimeout="no">
  <cfset MM_Username=Iif(IsDefined("Session.MM_Username"),"Session.MM_Username",DE(""))>
  <cfset MM_UserAuthorization=Iif(IsDefined("Session.MM_UserAuthorization"),"Session.MM_UserAuthorization",DE(""))>
</cflock>
<cfif MM_Username EQ "" OR MM_UserAuthorization EQ "" OR ListFind("admin,user",MM_UserAuthorization) EQ 0>
  <cfset MM_referer=CGI.SCRIPT_NAME>
  <cfif CGI.QUERY_STRING NEQ "">
    <cfset MM_referer=MM_referer & "?" & CGI.QUERY_STRING>
  </cfif>
  <cfset MM_failureURL="login.cfm?accessdenied=" & URLEncodedFormat(MM_referer)>
  < url="#MM_failureURL#" addtoken="no">
</cfif>

Open in new window

Ok. One page was missing that. Come to think of it that is the last page where I saw the error. I'll test it out now.
Hmm. Well, I had to restart my browser and when it went to reload the tab where the authenticated page was I got the same error except it wasn't generated by the cfc but by the page itself at this line of code

WHERE dbo.Users.username = <cfqueryparam value="#SESSION.MM_Username#" cfsqltype="cf_sql_clob" maxlength="10">


Looks like it doing a good job if identifying that the MM_Username is not present. Now if I can only get it to send the user to the login page when this occurs.  I wonder if I tried using a new version of DW that this behavior might have been fixed.
that code I posted above should just be present before the line above is encountered, then it will do exactly that.
Not sure how quick my next response will be - most of my city is blacked out and my laptop/smartphone batteries won't last forever :D
Thanks. I think it's all fixed now. DreamWeaver did stick one of my queries above the restriction code. I've moved it and it appears to be working now. I'll keep testing to make sure that is the case. Thanks for the help,.