I am not a huge fan of ColdFusion's built-in authentication framework; to be fair, I have never really given it a chance, so I'm not here to say if it's good or not. But, the other day, I was helping someone debug a CFLogin problem, so I figured I'd post it here as well. Below, I've set up a tiny example of what was happening.
Here is the Application.cfc:
<cfcomponent output="false" hint="I define application settings and event handlers."> <cfset THIS.Name = "My Test App" /> <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 1, 0 ) /> </cfcomponent>
And here is the index.cfm page which makes use of ColdFusion's CFLogin tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>CFLogin Test</title> </head> <body> <h1> CFLogin Test </h1> <p> Hello user! <!--- Only display if user is not logged in. ---> <cflogin> <a href="login.cfm">Please Log In</a>. </cflogin> </p> </body> </html>
Nothing much going on here; but, when we run the page, we get the following ColdFusion error:
The string COOKIE.CFAUTHORIZATION_My Test App is not a valid ColdFusion variable name.
If the problem is not immediately obvious, it's that ColdFusion uses the application name (THIS.Name in Application.cfc) to define the authorization cookie name (the default approach as stated by the ColdFusion documentation). The reason that we are getting the error is that our application name, "My Test App," has spaces in it and valid variable names can only contain letters, numbers, underscores, and dollar signs.
To get around this, we can change the name of our application to not have spaces or any other invalid characters. But that seems a little bit extreme, right? That's be like changing the shape of my foot to fit a particular shoe. Rather than bending the application to fit into CFLogin's default constraints, let's use the CFLogin tag in a way that fits into our application.
To do this, we are going to have to tell the CFLogin tag which application key to use such that it doesn't use our application name. That is what the ApplicationToken attribute is for. The ApplicationToken allows us to provide the CFLogin tag with a dynamic value that will override the application name. Make the changes are fairly straightforward:
<cfcomponent output="false" hint="I define application settings and event handlers."> <cfset THIS.Name = "My Test App" /> <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 1, 0 ) /> <cffunction name="OnRequestStart" access="public" returntype="boolean" output="false" hint="I fire before a template is executed."> <!--- Store application key for CFLogin. ---> <cfset APPLICATION.LoginKey = "MyTestAppLogin" /> <!--- Return out. ---> <cfreturn true /> </cffunction> </cfcomponent>
NOTE: I would usually initialize the APPLICATION scope in the OnApplicationStart() event method, but for ease of demo, I did it in the OnRequestStart() event method.
.... and the index.cfm page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>CFLogin Test</title> </head> <body> <h1> CFLogin Test </h1> <p> Hello user! <cflogin applicationtoken="#APPLICATION.LoginKey#"> <a href="login.cfm">Please Log In</a>. </cflogin> </p> </body> </html>
Now, we are defining our CFLogin ApplicationToken in the APPLICATION scope and then using it in our CFLogin tags. This time, when the page runs, we get the rendered output, and when we check our cookies, we can see that ColdFusion has created the following cookie based on our ApplicationToken:
I'm not sure if this is a no brainer to those of you that generally use ColdFusion's CFLogin tag; but, since I had to help someone debug this problem, I figured it would be worth posting.
Want to use code from this post? Check out the license.
Few comments here.
The applicationToken isn't just to override the default value. I believe the real use (or the intended use) was to allow you to share Authentication/Authorization info among multiple applications. So you can login once and work within multiple different apps (on the same server).
I'm going to ding you for using onRequestStart to set an Application variable. That really needs to be onApplicationStart. OOps, just saw your note. I don't see how using onRequestStart makes it easy. I guess I'm just worried people will see the code and NOT the note. I'd change it. ;)
I believe you are correct regarding the cross-application login. When I was looking up the documentation, I think they made mention of this.
Yeah, I know I know :) The only reason I went with OnRequestStart() is because I ran the application once to get error and I didn't want to have to wait the whole minute for the application to timeout before it would even fire the OnApplicationStart() event handler :) I guess that's the epitome of laziness.
Ironically, it probably took me a minute just to write the OnRequestStart() event handler.
That's why my typical onRequestStart will have a URL hook to rerun onApplicationStart. A minute -is- a long time when testing. ;)
Word up! It is a long time when testing. That's a good idea - the direct call to OnApplicationStart(). Ok, I'll keep it cleaner next time.
As long as that URL hook has appropriate security around it -- even if you are just checking the remote IP. Otherwise, it's a gaping security hole just asking for someone to DoS your site. (And to see what they can get away with by restarting your site and immediately performing secure stuff while the site is reloading.)
I am not sure what you're referring to?
Meh, security is overrated. ;) I'd take the simple ModelGlue approach. You can configure both a url key and a url value to allow for reload. Out of the box it is init=true, but you can change that to toosexy=forthisblog. That's probably more than enough.
Ben - posts like these really make my day!
They open my eyes to little things that I never would have thought about on my own!
applicationToken - Fantastic!!! :)
Always glad to shed some light. Of course, I am not well versed in the CFLogin stuff. I believe Ray Camden has a good number of posts on the topic.
I have quite a few posts on cflogin. Unfortunately most are not good ones. CFLOGIN has many warts. It has improved greatly in CF8, but in general, I'd probably not recommend using it.
I would like to recommend everyone to use a valid variable name as application name. Use application.prettyName for the pretty name. Maybe all my "not a valid variable name" errors over the years can be traced to the transition from Application.cfm to .cfc (I often used name="myAppname_#version#" in the cfapplication tag and the dots in version made it illegal), but I am not 100% sure.
Unless one have a very good reason for using spaces or strange characters in the application name, I think it is best avoided.
Thanks very much Ben. I have used yours and many other blogs to figure out problems over the years, but have never really bothered to leave comments. I figure I should get off my butt and start contributing more.
My first attempt at contribution then will be to thank you and everyone else whose comments and hard work have saved me countless hours. It is certainly appreciated.
I'm happy that stuff you find here has been good; and, I'll take it as a compliment that it has even led to commenting :) Awesom!
Hello and thank you Ben,
I know this is an old post but I had a problem with implementing the above method. my cflogout doesn't work when I use applicationtoken in cflogin. I end up using StructClear instead. Any suggestions?