Skip to main content
Nordiam
Known Participant
May 27, 2006
Answered

cflogin and cfauthorization hell...

  • May 27, 2006
  • 6 replies
  • 9434 views
This is all in ColdFusion MX 7.03, Firefox, and loginstorage="session".

I've been wrestling with this for a few days now and I can't seem to figure out what I'm missing.

I have cflogin working perfectly except for when the browser is closed and then re-opened without logging out.

I have every possible debug variable being displayed once I reopen the browser, and I've verified there is NO Session.Auth (a structure that contains user info, populated within <cflogin> once authenticated), NO cfauthorization, NO GetAuthUser, NO user roles, etc.

Then, entering a Login ID and password, I am transported to the secure page, but without any of my user session variables that should have been set by cflogin.

So cflogin is obviously being skipped. Does ColdFusion think that I'm already authenticated, so it doesn't run cflogin? I just reopened the browser!! I even have a new CFID and a new token.

cfauthorization seems to be created after I've logged in on the reopened browser, but because cflogin isn't processed, my Session.Auth doesn't exist. I can therefor catch the problem with this...

<cfif isDefined("CFAUTHORIZATION_MyApp") and #CFAUTHORIZATION_MyApp# NEQ "" and NOT isDefined("Session.Auth")>
<cflock scope="Session" timeout="15" type="exclusive">
<cfscript>
StructDelete(Session,"cfauthorization");
</cfscript>
</cflock>
<cflogout>
</cfif>

The problem is that you have to login first, only to be logged out, and tossed back to the login screen.

My login processing script is an include in the application.cfm.

I would really like to do this with session client storage, and while I know there were problems with this in earlier versions of ColdFusion, I thought it was fixed in MX 7.

Is there something blatantly obvious that I'm doing wrong or missing?
    This topic has been closed for replies.
    Correct answer BKBK
    Perhaps this is by design?
    Yes. Coldfusion behaves exactly as it should.

    Login and then close and re-open the browser, now login again, and 'Session.Auth' is undefined.
    There is the crux of the matter. You are using a so-called "session-only" cookie, which expires when the browser closes. You can already spot a paradox, namely: "now login again, and 'Session.Auth' is undefined". According to your code, that should not be possible. The cfif-block <cfif (ValidateUser.RecordCount EQ 1)>...</cfif> ensures that, whenever a user logs in, Session.Auth.isLoggedIn is defined and true.

    I would remove the cookie code

    <cfif IsDefined("Cookie.CFID") AND IsDefined("Cookie.CFTOKEN")>
    <cfset Local_CFID = Cookie.CFID>
    <cfset Local_CFTOKEN = Cookie.CFTOKEN>
    <cfcookie name="CFID" value="#Local_CFID#">
    <cfcookie name="CFTOKEN" value="#Local_CFTOKEN#">
    </cfif>


    Its effect counters what you wish to happen when the browser closes and reopens. Replace it with the earlier suggestion,

    <cfif NOT isDefined("Session.Auth")>
    <cfset Session.Auth = StructNew()>
    </cfif>
    <cfparam name="Session.Auth.isLoggedIn" default="False" type="boolean">

    The variable Session.Auth.isLoggedIn should be defined at all times during a session. Your own code logic demands that. When and where its value is true is another matter.

    There might be problems with the blanket deletes,

    <cfloop collection="#Session#" item="sessionVar">
    <cfset StructDelete(Session, sessionVar)>
    </cfloop>


    In the Application.cfc framework, Coldfusion needs to handle the standard session variables itself. Otherwise, it might not fire the events nor react reliably when the events fire. Replace the code with

    <cfset StructDelete(Session, sessionVar1)>
    <cfset StructDelete(Session, sessionVar2)>

    where sessionVar1, sessionVar2, ... are custom session variables like Session.Auth. As far as the session variables CFID, CFToken, sessionID, CFAuthorization,... are concerned, hard-code them if and only when you really have to. With Application.cfc, it is usual practice to let Coldfusion handle them automatically.






    There is a choice to make.


    ------------------------------------------------------------------------------------------------------------------
    (1) Require that the session end when the browser is closed. The ideal way to implement that is by J2EE session management. Since that is not available, you've chosen to use session-only cookies.
    ------------------------------------------------------------------------------------------------------------------
    > When the cookie code is removed, I remain logged in after I close and open the browser.

    Then don't remove the session-only cookie code. You will use it with the understanding that, when the browser closes and reopens, that might only expire the session-cookie at the client end, and not end the session at the server end. Subsequently, Coldfusion will automatically create a new session for the browser's new request. Most likely, the new session will reside in server memory alongside the session whose cookie has just been expired. It will be there till its timeout is flagged. The setting, loginStorage="session", ensures that the user's login details and session variables have the same timeout. Hence, the login details and session.cfauthorization will also be in server memory till session timeout.


    ------------------------------------------------------------------------------------------------------------------
    (2) Not bother what happens when the user closes and reopens the browser; only bother about the current values of Session.Auth.isLoggedIn, Session.Auth.FirstName, getAuthUser() and session.cfauthorization, as those are the values that tell you everything you need to know about the user.
    ------------------------------------------------------------------------------------------------------------------
    > When the cookie code is removed, I remain logged in after I close and open the browser.
    That is expected behaviour. Login details are stored in the session, and the session survives closing and reopening of the browser. The only sure mechanaism to end the session is session-timeout.

    > How do I prevent that without the code?

    You wouldn't want to prevent that. It is consistent with your requirements. You identify users and track their login-status by means of a session. By default, when you maintain state with Coldfusion session management, the session will still be alive when you temporarily close and reopen the browser. That means, the user's login details and login-status, which are stored in his or her session, will still be active. That kind of consistency is what most applications require.

    > If setClientCookies is set to True, but I remove all the cookie code from onSessionStart,
    > I am still logged in after I re-open the browser. Perhaps this is by design?


    Yes. In fact, if you decide on choice (2) you should set setClientCookies to "true" and loginStorage to "session".

    > Also, after logging in, the value for cfauthorization never changes, even after
    > restarting the CF server. Maybe this is normal?


    Coldfusion will store login details in the Session.cfauthorization variable and will use session cookies to identify the user till timeout or till logout. There are two other matters worth taking into account. One, the browser may reopen a cached page rather than make a new request. Two, even after a session has ended, Coldfusion can require up to half a minute to delete its variables.




    6 replies

    Nordiam
    NordiamAuthor
    Known Participant
    June 10, 2006
    I can't tell you how thankful I am for spending the time to clear this up for me. I guess I'll just have to work with what I've got. At least it's good to know that I'm not crazy. I talked to Crystaltech this morning and the tech opened a ticket to try and get J2EE session variables enabled on my shared server. We'll see what happens.

    I can, of course, just use the cflogin information in a query once the user is logged in. I guess in this case I opted for stubborness and understanding over a quick fix.

    Thanks again!
    BKBK
    Community Expert
    Community Expert
    June 10, 2006
    I guess I'll just have to work with what I've got. At least it's good to know that I'm not crazy. I talked to Crystaltech this morning and the tech opened a ticket to try and get J2EE session variables enabled on my shared server.
    Closing and reopening the browser can be a real problem. Users constantly do it. J2EE session management will certainly offer you more control and more reliability than session-only cookies. With J2EE, Coldfusion will end the session and delete all session variables when the user closes and reopens the browser.

    Session.Auth.IsLoggedIn is not reliable if the user has closed and re-opened the browser. In other words, it is always False after logging in after closing and re-opening the browser.
    Your definition and use of Session.Auth.IsLoggedIn is sound. I don't think it's a question of reliability. It's just a speed trick of Coldfusion's. When the browser reopens the session-only cookie is expired, which makes it impossible for the browser to connect to that particular session, even though it is still in server memory. Any subsequent request will then start a new session by default. For the new session, Session.Auth.IsLoggedIn is yet undefined (in one version of the code) or False (in the other version).

    Nordiam
    NordiamAuthor
    Known Participant
    June 9, 2006
    When the cookie code is removed, I remain logged in after I close and open the browser.

    How do I prevent that without the code?

    If I use setClientCookies="False" then I am stuck with having to append the CFID and CFToken to the URL every time?

    Can I not maintain the session in a cookie unless the user has cookies turned off. Only then appending CFID and CFToken to the URL?

    I have also made the additional changes you stated.
    BKBK
    Community Expert
    Community Expert
    May 28, 2006
    I decided to migrate to Application.cfc
    A wise choice in any case. In my opinion, one cannot tap into the full power of MX7 without it.

    My login code is now being called this way:
    <cffunction name="onRequestStart" returnType="boolean" output="false">
    <cfargument name="TargetPage" type="string" required="true">
    <cfinclude template="login.cfm">
    <cfreturn true>
    </cffunction>

    I would recommend something along the lines of the code in OnRequestStart below. You will find more information in security scenario examples in the livedocs.

    I've also tried using OnSessionEnd with the following (I know the Session is already dead, but I'm desperate):

    <CFCOOKIE NAME="CFID" VALUE="#CFID#" EXPIRES="NOW">
    <CFCOOKIE NAME="CFTOKEN" VALUE="#CFTOKEN#" EXPIRES="NOW">
    <CFLOGOUT>
    <cfscript>
    StructDelete(Session, "Auth");
    StructDelete(Session, "cfid");
    StructDelete(Session, "cftoken");
    </cfscript>

    As you've already guessed, all of this is unnecessary, especially as the session no longer exists. OnSessionEnd is the place to tie loose ends, e.g. to log closing data to file or to database(see example below). Also, <cflogout> does not belong there. Logging out should be done in OnRequestStart.

    One way to do logouts is to ensure that all logout links have in their query-string the name-value pair, logUserOut=true. Then, in OnRequestStart, you will have (before the cflogin tag) a test like

    <cfif isDefined("URL.logUserOut") and URL.logUserOut EQ true>
    <cflogout>
    </cfif>

    As far as setting up my Session.Auth, I'm just checking that a single record was returned from the database, and setting an arbitrary value to true. So I assume this can happen before or after cfloginuser. I've tried both and doesn't make any difference.
    <cfscript>
    Session.Auth = structNew();
    Session.Auth.isLoggedIn = "True";
    </script>

    Your problem with Session.Auth is not simple to track down. The session scope is in effect across multiple requests, any one of which can start a new session, thereby altering the value of Session.Auth. The only solution is to code tightly, for example, such that Session.Auth is always defined and is false by default.

    I'm really not settings cookies manually at all. By 'Session Only' cookies, do you mean something like this?
    Yes. If you're also not using J2EE session management, then it is very likely that your application is not maintaining session state from page to page. That could also happen if the client doesn't accept cookies. To plug such loopholes, use URLSessionFormat() or, alternatively, append session.URLToken to all links from your site to your site.

    Nordiam
    NordiamAuthor
    Known Participant
    May 29, 2006
    At least I've updated my app with application.cfc. It's cleaing up the rest of my site quite nicely.

    BKBK, I still can't get ANY session variables set within cflogin once the browser is closed and reopened.

    Before I get into a long winding ramble of all the things I've tried today, I was wonding if I could email you a link to a zip file of the 4 little files handling logins? Perhaps you could outline any blatant problems? I'm going crazy.

    So anyway...

    My login script is actually almost identical to the one in the livedocs, except that I'm calling login.cfm via a cfinclude instead of writing it directly inside application.cfc. I assume this is ok?

    In login.cfm, I also update last login time in the db, use a salt, hash, password for authentication, etc. (The skeleton I'm working with now is stripped of all this stuff).

    I took your advice and put the logout login in OnRequestStart. Simple and clean... I like it. I also removed everything from the OnSessionEnd, except for the log writing.

    I worked with a few dozen variations of tightly managing Session.Auth today, both in application.cfc and login.cfc, but the only thing that changes is that Session.Auth.isLoggedIn is False now instead of Undefined (again... only on a not-logged-out, closed, and reopened browser).

    My Application.cfc ...

    <cffunction name="onSessionStart">
    <cfif NOT isDefined("Session.Auth")>
    <cfset Session.Auth = StructNew()>
    </cfif>
    <cfparam name="Session.Auth.isLoggedIn" default="False" type="boolean">
    </cffunction>

    The line below cfloginuser ...

    <cfif NOT isDefined("Session.Auth")>
    <cfset Session.Auth = StructNew()>
    </cfif>
    <cfset Session.Auth.isLoggedIn = True>

    I had one question about this actually. Am I correct in thinking that I do not need to lock the Session scope when handling this within the Application.cfc? If so, I assume this is extended to includes? Anyway, I've tried it both ways.

    My hosting company doesn't support J2EE session management, so I don't use it on my development server. I have tried turning cookies on and off in Firefox, updating all the URL's to use URLSessionFormat()... none of which seem to do anything that solves my problem.

    The cookies do have the same information as the Session variables and they are set to expire as the end of session.

    You said, "The session scope is in effect across multiple requests, any one of which can start a new session, thereby altering the value of Session.Auth"

    I'm not really sure what this means? If I am the only one with access to the CF server, and I'm the only one logging in, and after I close the browser the session is dead, the cookies are gone, and I'm logged out. Isn't my session unique and special and maintained in memory?

    Anyway, thanks for all the advice so far. It's really helping my development skills.
    BKBK
    Community Expert
    Community Expert
    May 29, 2006
    My login script is actually almost identical to the one in the livedocs, except that I'm calling login.cfm via a cfinclude instead of writing it directly inside application.cfc. I assume this is ok?
    Yes.

    login.cfc
    A typo, I presume

    Session.Auth.isLoggedIn is False now instead of Undefined
    Thus, you define Session.Auth.isLoggedIn when the session starts, so that it remains defined till the end of the session. Its value is False by default. Its value is True only when the client is logged in. I believe that to be a simpler, but more robust code logic.

    Am I correct in thinking that I do not need to lock the Session scope when handling this within the Application.cfc?
    Yes. You don't need to lock any session code in OnSessionStart.

    You said, "The session scope is in effect across multiple requests, any one of which can start a new session, thereby altering the value of Session.Auth". I'm not really sure what this means?
    A session is loosely analogous to a relay. The pages or requests are the runners. The baton is the set of session IDs to be passed from page to page. For consistency, we will assume that the end of the relay, like the end of the session, happens when a time-out is called.

    Just as the relay will stop prematurely for the Blue Team if one of its runners fails to pass the baton so, too, does a session stop prematurely if one of its requests fails to pass its IDs to the next. I intentionally use the word stop, not end. The relay team is still lingering on the tracks, waiting to be timed out; the session is still lingering in memory, waiting to be timed out.

    In the meantime, any further request begins a new session, with a brand new set of IDs and new Session.Auth. By our analogy, another relay team, say the Reds, starts. Now, the Blues and the Reds are simultaneously on the tracks. By analogy, there are now two distinct sets of session IDs simultaneously in memory. This gives us a possible explanation for the next question.

    If I am the only one with access to the CF server, and I'm the only one logging in, and after I close the browser the session is dead, the cookies are gone, and I'm logged out.
    Not necessarily. If you close and reopen the browser, Coldfusion might start a new session, with an entirely new set of session IDs. However, if the previous session had not yet timed out, then it would still be alive in memory. The cookies would very likely still be there. That is, the previous session will reside in memory alongside the current session session.

    To see that, perform this test. Save the following code as sessionVars.cfm.
    <p><cfoutput>
    GetAuthUser(): #GetAuthUser()# <br>
    Session.Auth.isLoggedIn: #Session.Auth.isLoggedIn#<br>
    session.URLToken: #session.URLToken#<br>
    session.cfauthorization: #session.cfauthorization# <br>
    </cfoutput></p>

    Add the link

    <cfoutput><a href="sessionVars.cfm?#session.URLToken#">session vars test</a></cfoutput>

    to your index.cfm page.

    (i) When you successfully log in, and are sent to index.cfm, click on the link.
    (ii) Copy the values of the 4 variables.
    (iii) Close and reopen the browser.
    (iv) Perform a new login as you did before.
    (v) Navigate to index.cfm, click on the link and copy the values of the new set of 4 variables.
    (vi) Compare these variables with those you obtained in (ii).
    (vii) Go back to index.cfm. Append ?#session.URLToken# after index.cfm in the browser's address field, where #session.URLToken# is the value of the previous URLToken obtained in (ii), and press Enter.
    (viii) Click on the session vars link. Compare the values with those in (ii). Conclusion?

    email
    See private message



    Nordiam
    NordiamAuthor
    Known Participant
    May 28, 2006
    I decided to migrate to Application.cfc (from Application.cfm) last night in the hopes that it would illuminate my problem.

    My login code is now being called this way:

    <cffunction name="onRequestStart" returnType="boolean" output="false">
    <cfargument name="TargetPage" type="string" required="true">
    <cfinclude template="login.cfm">
    <cfreturn true>
    </cffunction>

    I've also tried using OnSessionEnd with the following (I know the Session is already dead, but I'm desperate):

    <CFCOOKIE NAME="CFID" VALUE="#CFID#" EXPIRES="NOW">
    <CFCOOKIE NAME="CFTOKEN" VALUE="#CFTOKEN#" EXPIRES="NOW">
    <CFLOGOUT>
    <cfscript>
    StructDelete(Session, "Auth");
    StructDelete(Session, "cfid");
    StructDelete(Session, "cftoken");
    </cfscript>

    I'm really not settings cookies manually at all. By 'Session Only' cookies, do you mean something like this?

    <cfif IsDefined("Cookie.CFID") AND IsDefined("Cookie.CFTOKEN")>
    <cfset localCFID = Cookie.CFID>
    <cfset localCFTOKEN = Cookie.CFTOKEN>
    <cfcookie name="CFID" value="#localCFID#">
    <cfcookie name="CFTOKEN" value="#localCFTOKEN#">
    </cfif>

    As far as setting up my Session.Auth, I'm just checking that a single record was returned from the database, and setting an arbitrary value to true. So I assume this can happen before or after cfloginuser. I've tried both and doesn't make any difference.

    <cfscript>
    Session.Auth = structNew();
    Session.Auth.isLoggedIn = "True";
    </script>

    I'm testing an even simpler version of this now, in which there are simply 4 files.

    Application.cfc <- Calls login.cfm and handles logout
    index.cfm <- Loads if authenticated
    login.cfm <- Almost identical to your example with the exception of setting Session.Auth
    login_form.cfm <- A simple form using j_username and j_password


    1. So I start fresh by using <cflogout>
    2. I login after this and my Session.Auth exists
    3. I close the browser and then open the browser
    4. I login again and my Session.Auth does NOT exist

    It seems that cflogin is working perfectly (as I need to login when I open the browser), with the exception of my Session.Auth not being created when I login after the browser has been closed.

    If I simply hit the logout button and then login again... waalaa my Session.Auth exists again.

    Turn towards wall, bang head.

    BKBK
    Community Expert
    Community Expert
    May 28, 2006
    One thing to rule out, first. I've been assuming you're not using "session only" cookies (cfcookie without expires-attribute) or J2EE session management. Is that so? They are the two customary ways to end a session when the client closes the browser or, to put it in more technically correct language, the two customary ways to begin a new session when the client reopens the browser.

    My Session.Auth is set immediately after cfloginuser within cflogin, so I figured that if the Session.Auth didn't exist, cflogin or cfloginuser wasn't being processed
    Shouldn't it be the other way round? If cflogin and cfloginuser haven't been processed and GetAuthUser() returns a blank, then Session.Auth should not exist or, if it does, its keys should be empty.

    The code logic you've outlined seems okay. However, the connection between cflogin, login form and cfloginuser is not apparent. Here's an example, which I adapted from the Macromedia livedocs on cflogin.


    BKBK
    Community Expert
    Community Expert
    May 27, 2006
    I just reopened the browser!! I even have a new CFID and a new token.
    This is the clue that shows the problem is more likely with your code than with Coldfusion. When you reopened the browser you apparently began a new session. Since login credentials are stored in the session scope( loginstorage="session"), Coldfusion purged the previous login details at the start of the new session.

    So cflogin is obviously being skipped.
    Apparently not. You say you could enter a Login ID and password. It means Coldfusion sent you to the login page when you reopened the browser. To code this part well, you should ensure that Coldfusion sends the user to the login page only if the cflogin tag is active. After all, it is usual to have the login form code, <cfinclude template="loginform.cfm">, within the cflogin tag.

    These kinds of session/login problems are often caused by using the wrong booleans, e.g. CFAUTHORIZATION_MyApp, for the if-block in which you set your session or login variables. Occasionally, not using locks can also be a problem.

    Incidentally, I don't yet know of any motivation in Coldfusion to use StructDelete(Session,"cfauthorization"). Much better to use <cflogout>, which performs the deletion and more besides.

    Nordiam
    NordiamAuthor
    Known Participant
    May 27, 2006
    BKBK... thanks for your response...

    I have an Application.cfm in the sub-folders of the website which require authentication (call it the protected folder). That Application.cfm (in the sub-folder) calls the login script (login.cfm) using a cfinclude (it also calls the root Application.cfm). This way, I can just include login.cfm in any application.cfm in the sub-folders that require authentication. Is this usual?

    When a protected folder is opened in a browser, Application.cfm is loaded and Login.cfm first checks to see if cflogin is defined.

    If not defined, login_form.cfm is included and the rest is aborted.

    After that, if the username and password are blank or passwords don't match, login_form.cfm is included and the rest is aborted.

    If you've gotten this far (assuming account info is correct), the username and password are authenticated, roles are assigned, the user is logged in (cfloginuser), and a Session.Auth structure is set up to hold some key user variables, namely Session.Auth.isLoggedIn="True".

    If all goes well, the cflogin finishes processing the login and the index.cfm file in the protected folder is loaded. Roles then determine what is seen by the user.

    My Session.Auth is set immediately after cfloginuser within cflogin, so I figured that if the Session.Auth didn't exist, cflogin or cfloginuser wasn't being processed, but apparently it is?

    Messing with CFAUTHORIZATION_MyApp was just an after thought... an attempt to catch my problem. I'm not using it anywhere else.

    Also, the action for the login form is the index.cfm file in the protected folder.

    My logout script is in the root application.cfm file, but of course it doesn't get processed unless the user clicks the link to logout.

    Nordiam
    NordiamAuthor
    Known Participant
    May 27, 2006
    I just tested a very basic version of this and here is a summary.

    If I login and logout using <cflogout> all goes well. In other words, my Session.Auth structure with user information exists.

    If I login, then close and re-open the browser, I am presented with the login form. When I log in from here my Session.Auth structure does not exist.

    As stated previously, the Session.Auth is set immediatley after cfloginuser, so I don't userstand why it isn't being processed.

    Here is a basic version...

    <cfloginuser name="#User.login_id#" password="#User.Password#" roles="#User.roles#">

    <cflock scope="Session" timeout="30" type="exclusive">
    <cfset Session.Auth = structNew() >
    <cfset Session.Auth.isLoggedIn = "True" >
    </cflock>