We’ve recently moved to cf11 for a project and have hit on an unusual problem:
When a user lets their session timeout, and they try to login back in it takes two attempts for them to successfully log in.
When a user manually logs out, they have no problems logging in.
This problem didn’t happen in CF8. I’ve examined user scopes and cannot see a difference. I’ve tried adding the logout code before sign-in in the hope I can make the state the same. Neither has worked. Is this a known problem? And do you have a suggestion to what I can try?
EDIT:
I have an Application.cfc and result.cfm in the root of the project. I have a signin/ folder for pages that are not logged in. That contains signin.cfm and onsignin.cfm that handles the authentication. When running the code, wait for the session to time out before logging in again with the same user name again.
signin/signin.cfm
<form action="onsignin.cfm" method="POST" >
User name: <input name="login" type="text" />
<input type="submit" value="Login" name="btnSubmit" >
</form>
signin/onsignin.cfm
<cffunction name="authenticate" access="public" returntype="void">
<cfargument name="login" />
<cflogin idletimeout="3600">
<cfloginuser name="#arguments.login#" password="1234AbCd" roles="admin,developer,login_session,login_signoff" />
</cflogin>
<cfreturn />
</cffunction>
<cfscript>
authenticate(trim(Login));
writeOutput("At /signin/onsignin.cfm<br/>");
writeOutput("IsUserLoggedIn: #IsUserLoggedIn()#<br /><hr/>");
</cfscript>
<cflocation url="../result.cfm" addToken="no" /><!--- before we get to result.cfm onRequestStart() in Application.cfc is triggered. --->
Application.cfc
<cfcomponent>
<cfset THIS.Name = "LoginTest" />
<cfset THIS.SessionManagement = true />
<cfset THIS.ClientManagement = false />
<cfset THIS.LoginStorage = "session" />
<cfset THIS.setClientCookies = false />
<cfset This.sessiontimeout= createTimeSpan(0,0,0,20)/><!--- 20second timeout to show the session problem --->
<cffunction name="outputCurrentLoginState" access="private">
<cfargument name="currentFunction" type="string" required="true"/>
<cfargument name="TargetPage" type="string" required="true"/>
<cfscript>
writeOutput("In function: #arguments.currentFunction# state is <br />");
writeOutput("TargetPage: #arguments.TargetPage# <br />");
writeOutput("GetAuthUser: #GetAuthUser()#<br />");
writeOutput("GetUserRoles: #GetUserRoles()#<br />");
writedump(session);
writedump(cookie);
writeOutput("IsUserLoggedIn: #IsUserLoggedIn()#<br /><hr />");
/*use writeLog() to view to Console */
</cfscript>
</cffunction>
<cffunction name="OnRequestStart" access="public" returntype="boolean">
<cfargument name="TargetPage" type="string" required="true"/>
<cfset outputCurrentLoginState("OnRequestStart",arguments.TargetPage)/>
<cfif not IsDefined("Cookie.CFID")>
<CFLOCK SCOPE="SESSION" TYPE="exclusive" TIMEOUT="5">
<cfcookie name="CFID" value="#SESSION.CFID#" secure="false" httpOnly="true" />
<cfcookie name="CFTOKEN" value="#SESSION.CFTOKEN#" secure="false" httpOnly="true" />
</CFLOCK>
</cfif>
<cfreturn true />
</cffunction>
<cffunction name="OnRequest" access="public">
<cfargument name="TargetPage" type="string" required="true"/>
<cfset outputCurrentLoginState("OnRequest",arguments.TargetPage)/>
<cfif findNoCase("signin/", arguments.TargetPage)>
<cflogout/>
</cfif>
<cfinclude template="#ARGUMENTS.TargetPage#" />
</cffunction>
</cfcomponent>
result.cfm
<cfscript>
writeOutput("GetAuthUser: #GetAuthUser()#<br />");
writeOutput("GetUserRoles: #GetUserRoles()#<br />");
writeOutput("--- IsUserLoggedIn: #IsUserLoggedIn()#<br />");
writeOutput("At /result.cfm<hr/>");
</cfscript>
Update: Now I have the test code above that fails for cf11, I tried it on a cf8 server and in cf8 it works as I expect it to. When the session times out, the user does not have any problem creating a new session. It is only in cf11 were it fails.
I have a work around that will be good enough for my use case. Old code:
<cflogin idletimeout="3600">
<cfloginuser name="#yourlogin#" password="#yourpassword#" roles="#yourroles#" />
</cflogin>
New code:
<cflogin idletimeout="3600" allowconcurrent="false">
<cfloginuser name="#yourlogin##createUUID()#" password="#yourpassword#" roles="#yourroles#" />
</cflogin>
Adding a random value to the cfloginuser name
attribute should make everyone unique, even if they use the same credentials when you validate them against a database.
It’s a bit of a hack, but it gets around the issue.
Thank you for everyone’s help… Especially Miguel for picking up on allowconcurrent
.