| By Sheldon Sargent | Article Rating: |
|
| March 2, 2004 12:00 AM EST | Reads: |
18,641 |
Three years ago, I wrote an article in ColdFusion Developers Journal discussing how to create customized roles-based Coldfusion {CF} authentication {CFDJ Vol. 2 Issue 3: "Customize ColdFusion Authentication 1"} The article focused on showing how to implement page-level security within CF without the pains of setting up advanced security in ColdFusion 4.5.1.
Do you remember <CFAUTHENTICATE>, isAuthorized(), and isProtected(), and how hard it was to properly configure a userDirectory? It didn't even work with certificates! Well, since Macromedia completely removed Netegrity's SiteMinder and replaced it with the JAAS (Java Authorization and Authentication Service) in ColdFusion MX (CFMX), I figured it's time to do an update.
If you followed the example in the first article, or if you just happened to already roll your own security paradigm in CF, you were in good shape for the security changes in CFMX. This is because you already wrote your own code to perform user authentication against some back-end user database (LDAP, Active Directory, RDBMS, etc.) and assigned user roles based on group memberships within the user database. (Code examples for this article can be downloaded from www.sys-con.com/mx/sourcec/cfm).
Additionally, you should have already coded a mechanism for authorizing logged-in user access to features and functionality within your applications. If you were diligent, you even included logic to ensure your authentication code only ran once per user session. CFMX provides this framework for you with the CFLOGIN scope and ColdFusion Components (CFCs).
Keeping in line with the first article, the scope of this article also focuses on user authentication and access at the page level within a given CFMX application. Naturally, this begs questions about other security concerns such as protecting CF templates with OS-level permissions and integrating CFMX with Web server access controls (e.g., digest security). These are all beyond the scope of this article but if there's interest, I will cover these and other topics in future articles. Again, this article will focus on the basics of using the CFLOGIN scope to authenticate and authorize users via CFCs.
User Security Basics
Let's talk basics. Authentication is the process of identifying users - that they are who they say they are. This is typically performed with a username/password challenge, but can be as seamless as using X.509 certificates, or as conclusive as biometric devices. When answering the challenge, a query validates the user's input against some form of user store: RDBMS, LDAP, Active Directory, etc. Authorization is the process of identifying the rights and permissions of the authenticated user. The user store is also the deposit for these rights or group memberships.
CFMX provides new tags and functions to control user security. <CFLOGIN> provides a container for performing user authentication and authorization and instantiates the CFLOGIN structure. Code inside the CFLOGIN tags executes only for unauthenticated users. The CFLOGIN structure contains two variables: CFLOGIN.name and CFLOGIN.password. Populate these variables with one of the following:
- j_username and j_password form fields
- username and password values (or hashes) passed in the Authorization header in Web Server Authentication (Basic, NTLM, Digest, etc.)
- setCredentials method of a Flash Remoting Call
In order to implement page-level security in CFMX, we need to authenticate the user, figure out the user's permissions, and then check those permissions on protected pages/sections of the application. We will use a simple login form to present the authorization challenge (see Code I). We will code a CFC to handle the authentication and authorization, instantiate the CFC inside the CFLOGIN section of the Application.cfm, and store the authenticated user's group memberships (or access levels) within a session-level structure.
The Auth CFC
The heart of our security paradigm is the CFC, so let's start there. As I said, authentication and authorization happen against some sort of user directory. You pick your poison: LDAP, Active Directory, RDBMS, NT SAM, etc. To continue in the spirit of the first article I will use an LDAP as my user directory. However, this time I will be using a username/password combination instead of X.509 certificates.
The Auth CFC will have four methods: init, authenticate, authorize, and setAuthUser. The init method is a "default" constructor that returns an instance of the CFC. It sets some default LDAP connection values for use by other methods in the CFC. You will also notice the <cfset init()> in the pseudo constructor area - the area after the opening <CFCOMPONENT> and before the first <CFFUNCTION>. Since the code in this area automatically runs upon CFC instantiation, the Init function will fire even without invocation (see Code II).
For more information on using the init method as a constructor, see Rob Brooks-Bilson's "Top Ten Tips for Developing ColdFusion Components" at www.oreillynet.com/pub/a/javascript/2003/09/24/coldfusion_tips.html.
Next is the authenticate method. This method requires two arguments: username and password. Pass these arguments to the Username and Password <CFLDAP> attributes, providing authentication to the LDAP. Upon successful authentication (i.e., a valid username/password pair in the LDAP), the method returns true; otherwise it returns false. The Boolean values allow for efficient CFIF evaluations, such as <CFIF auth.authenticate(#Form.j_username#, #FORM.j_password#)>. (See Code III.)
The authorize method also requires the username and password arguments. Because the username and password are passed directly to the LDAP, the authorize method also provides a means of authentication. However, this method returns a comma-separated list of groups to which the user belongs (see Code IV). Within your code - or even at the Web server level - you can create access control lists (ACLs) to assign permissions to your LDAP groups. The application/system rights and permissions assigned to the groups extend to its members. These group memberships are roles within the CFMX security paradigm.
We now have simple methods for authentication and authorization within our application. However, it is not very efficient to authenticate the user without retrieving any of his or her attributes from the LDAP; nor is it efficient to make two separate CFC calls to build a user object. The setAuthUser method provides this functionality. The LDAP query in setAuthUser provides the same authentication as the authenticate method, but it also retrieves useful user attributes and stores them in a local structure. Next setAuthUser calls the authorize method to retrieve the user's group memberships, and then adds the returned list to the local user structure. Finally, setAuthUser returns the user structure (see Code V).
Notice that I wrap the body of the methods in Try-Catch blocks. This standard error-trapping technique provides valuable debugging information. <CFLDAP> now retrieves the actual error returned by the LDAP server instead of throwing a generic error message. Log the errors to a customized log file with <CFLOG>. The <CFTHROW> enables all <CFCATCH> in calling templates to display the LDAP error caught in the CFC method.
The Login Structure
Let's now turn our attention to the Application.cfm (see Code VI). First, create the CFMX Application framework with a <CFAPPLICATION> tag. Enable SESSIONMANAGEMENT and a SESSIONTIMEOUT (or use the default timeout specified in the CFMX Administrator). CFMX 6.1 adds the LOGINSTORAGE attribute to <CFAPPLICATION>, which specifies whether to store the authentication information in a non-persistent cookie (default) or the Session scope. Use the default LOGINSTORAGE=Cookie for this example. Next, code some conditional logic to control user logout. You can do this nicely by wrapping <CFLOGOUT> in a <CFIF> block that checks for a URL or FORM variable. You can strengthen the logout by explicitly clearing the SESSION scope.
The <CFLOGIN>...</CFLOGIN> provides the security container for processing the authentication code for every unauthenticated user request. Performing this authentication only once during the user's session is optimal. The conditional logic forces the user to the login form if the CFLOGIN scope is not instantiated. The j_username and j_password submitted from the loginform.cfm instantiate the CFLOGIN scope.
Begin the authentication process by instantiating the auth.cfc into a shared scope. The Application scope makes the most sense because you want the CFC to persist across user sessions. Use <CFOBJECT> or createObject() to instantiate the CFC instead of <CFINVOKE>. <CFINVOKE> transiently invokes the CFC but will not persist the instance. Call the setAuthUser method to authenticate the user, retrieve the user's group memberships, and build an object containing the user's memberships and other LDAP attributes. Return this object into a Session-level container for the current user.
If setAuthUser() is successful, the Session.User variable will contain the authenticated object structure, providing a simple user profile structure that can be used throughout the site. Now we can log the user into CFMX with <CFLOG INUSER>. Use the CFLOGIN.name and CFLOGIN.password variables for the username and password attribute values. Pass the Session.User.Group value to the CFLOGINUSER to authorize the user for those roles. Then set a Session-level variable that indicates the login status.
The final construct in the CFLOGIN container is a CFCATCH block that handles any errors in the login process. It is important to wrap all of the security code in the Try-CATCH syntax for error control. The code in this block sets the login status indicator to false, creates a login failure message, and returns the user to the login form. The CFCATCH.Message will return any error messages thrown from the CFC methods.
User Authorization
Now that CFMX has authenticated the user, and his/her profile information and group memberships are stored in a Session-level object, use that information to provide authorization throughout the application. Use getAuthUser() to retrieve the username of the logged-in user. Implement access control within your templates by wrapping objects, code, or even the entire template itself in an IF-Else block that uses isUserInRole() to authorize the logged-in user for the protected functionality. The index.cfm provides an example of retrieving the logged in username, displaying the user profile, and using roles-based authorization to display links and/or text (see Code VII).
Conclusion
Leveraging ColdFusion Components and the security tags and functions in ColdFusion MX provides a robust system for roles-based authentication. Persistent CFCs provide a layer of abstraction and code reuse. The CFLOGIN container runs security code for any unauthenticated user request, ensuring that the challenge and authentication of the user happens only once. The built-in functions allow granular page-level access control. These features can augment any existing custom security logic for your CFMX applications.
Notes
- CFLOGIN LoginStorage attribute: At the time of this writing, the CFLOGIN LoginStorage attribute contains a bug (53320) when specifying SESSION. This bug does not completely clear the internal security scope when logging out of CFMX via <CFLOGOUT>. Although the session data is tied to the logged-in user, the bug results in a cached user login exposable to the next user login. This bug does not exist when using cookies to store the authentication information.
CFMX uses a base64-encoded string containing the application name, username, and password as the in-memory cookie value (CFAUTHORIZATION_applicationname) sent with every page request. You may want to consider using SSL and domain-level cookies to protect this information, or utilizing the Session scope to persist this authentication information. You can read more about the LOGINSTORAGE attribute in the CFMX LiveDocs at http://livedocs.macromedia.com/coldfusion/6.1/htmldocs/appsec10.htm.
- CFC Instantiation versus invocation: The code accompanying this article uses createObject() to instantiate the Auth CFC (auth.cfc). It is common practice for developers to use multiple CFINVOKE tags to invoke CFC methods. Accessing CFCs in this manner does not create a persistent instance of the CFC, and therefore does not run the instance data in the CFC pseudo-constructor. Although this constructor code is not required to execute the CFC, it is a best practice to utilize this area. This is why I advocate the use of <CFINVOKE> for method invocation of persistent CFCs instantiated with createObject() and <CFOBJECT>. Consult the CFMX LiveDocs for more details of CFC instantiation and invocation at http://livedocs.macromedia.com/coldfusion/6.1/htmldocs/buildi10.htm.
- Access control: Most Web servers provide site and directory-level access control, requiring user authentication to access folders or directories containing the Web site files. I am not privy to native methods of extending this functionality beyond the template and/or page level to the elements within the code (e.g., links, text, variables, etc). This custom design does extend the Web server's own access control to the element level via CF, utilizing the same user database for authentication.
Published March 2, 2004 Reads 18,641
Copyright © 2004 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Sheldon Sargent
Sarge is Sr. Technical Support Engineer, Adobe Systems, based in Gilbert, Arizona. He has been coding advanced applications in CF since version 2.0.
![]() |
Bobby 11/07/07 08:46:25 AM EST | |||
I can't find the code examples, the link given in the article gives a 404 error. Can anyone point me to the examples? |
||||
![]() |
Nathan Mische 03/19/04 09:54:13 PM EST | |||
Where can I find more info on the CFLOGIN LoginStorage attribute bug (53320)? |
||||
![]() |
Bill Lewington 03/17/04 03:00:33 AM EST | |||
I feel LDAP is a better way to go, so I disagree with Jack. With Microsoft''s Active Directory using LDAP being a little mor standard that SAM, it has made my life easier with authentication on a big Intranet site. |
||||
![]() |
Jack Ince 03/13/04 12:28:22 PM EST | |||
Sarge, I liked your article and it was benificial. However I do not know why you would add the added complexity of LDAP when most CF people use SQL or Access. To try and use your code I have to create a LDAP directory and that will have to be another adventure as I have never looked into all the espects of LDAP for a private site. Yhanls for your insight into this important subject. |
||||
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Cloud Computing Journal: Adobe to Deliver ColdFusion in the Cloud
- Adobe Unveils LiveCycle Enterprise Suite 2 for Deployment in the Cloud
- Adobe Flex Developer Earns $100K in New York City
- Adobe May Cooperate with Apple to Transplant Flash Player to iPhone
- Ph.D. in Twitter Anyone?
- Eolas Sues the Internet
- Adobe LiveCycle Enterprise Suite 2 for Cloud Computing
- Adobe Betas Target RIAs and Cloud Computing
- Special Report on the Emerging Cloud Computing Trend
- Adobe Cans Another 9% of its Workforce
- My Thoughts on Ulitzer
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Ulitzer Live! New Media Conference & Expo
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Eval JavaScript in a Global Context
- Fig Leaf Software to Exhibit at Government IT Conference & Expo
- Cloud Executives Feature on Cloud Computing Expo Power Panel
- Software Flexibility in the Cloud - Part 4 of 5
- Cloud Computing Journal: Adobe to Deliver ColdFusion in the Cloud
- Is Microsoft as Free as Open Source?
- Adobe Reader Sued
- Adobe Unveils LiveCycle Enterprise Suite 2 for Deployment in the Cloud
- Where Are RIA Technologies Headed in 2008?
- Cover Story: How to Increase the Frame Rates of Your Flash Movies
- AJAX World RIA Conference & Expo Kicks Off in New York City
- Your First Adobe Flex Application with a ColdFusion Backend
- Adobe Flex 2: Advanced DataGrid
- i-Technology Blog: Death-Knell For "Rich Media? Hardly!
- Adobe/Macromedia - Microsoft, Look Out!
- How To Create a Photo Slide Show ...
- Adobe Flex Interface Customization - Themes, Styles, Skins
- Personal Branding Checklist
- Has the Technology Bounceback Begun?
- "Real-World Flex" by Adobe's Christophe Coenraets




































