FarCry 5 maintain state using browser cookies
So, I've got a new site rolling out that we're using FarCry 5.0 (soon to be 5.1, when it's no longer beta). This particular site has a members area that's styled like facebook/myspace. In keeping with the vein of keeping the users happy, it's become apparent that most people don't want log into their social sites every time they visit them. Hmm... that's not currently built into FarCry, so it's time to dive in and play!
First up, let's extend the FarCryUD to include our new method for cookie detection. You need to make a copy of /farcry/core/packages/security/FarcryUD.cfc and copy it to /farcry/projects/{myproject}/packages/security/FarcryUD.cfc. You'll want to change the top line of the file to the following (the extends being the most important):
<cfcomponent displayname="My User Directory" hint="Provides the interface for the FarCry user directory" extends="farcry.core.packages.security.FarcryUD" output="false" key="CLIENTUD" bEncrypted="false">
Next, let's add the new cookie code detection. This adds a cfelseif into the mix. This is in the authenticate function.
<cfif structkeyexists(form,"userlogin") and structkeyexists(form,"password")>
<!--- If password encryption is enabled, hash the password --->
<cfif this.bEncrypted>
<cfset form.password = hash(form.password) />
</cfif>
<!--- Find the user --->
<cfquery datasource="#application.dsn#" name="qUser">
select *
from #application.dbowner#farUser
where userid=<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.userlogin#" />
and password=<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.password#" />
</cfquery>
<cfset stResult.userid = form.userlogin />
<cfelseif isdefined("cookie.my_memberid") AND isdefined("cookie.my_password")>
<!--- Find the user --->
<cfquery datasource="#application.dsn#" name="qUser">
select *
from #application.dbowner#farUser
where userid=<cfqueryparam cfsqltype="cf_sql_varchar" value="#cookie.my_memberid#" />
and password=<cfqueryparam cfsqltype="cf_sql_varchar" value="#decrypt(cookie.my_password,'saltmebaby')#" />
</cfquery>
<cfset stResult.userid = cookie.burton_memberid />
<cfelse>
<ft:processform>
<ft:processformObjects typename="#getLoginForm()#">
<!--- If password encryption is enabled, hash the password --->
<cfif this.bEncrypted>
<cfset stProperties.password = hash(stLogin.password) />
</cfif>
<!--- Find the user --->
<cfquery datasource="#application.dsn#" name="qUser">
select *
from #application.dbowner#farUser
where userid=<cfqueryparam cfsqltype="cf_sql_varchar" value="#stProperties.username#" />
and password=<cfqueryparam cfsqltype="cf_sql_varchar" value="#stProperties.password#" />
</cfquery>
<cfset stResult.userid = stProperties.username />
</ft:processformObjects>
</ft:processform>
</cfif>
Lastly, we need to modify our login process to create a cookie when the user logs in... only if there isn't already a cookie set. You would want to create a custom login for this, so copy /farcry/core/webtop/login.cfm to /farcry/projects/{myproject}/customadmin/login/login.cfm. You'll want to change the block of code that's in the final cfelse that relocates the user based on a successful login.
<cfelse>
<!--- relocate to original location --->
<cfif not isdefined("cookie.my_memberid")>
<cfset stUser = createObject("component", application.stcoapi["farUser"].packagePath).getByUserID(listfirst(application.security.getCurrentUserID(),"_")) />
<cfcookie name="my_memberid" value="#stUser.userid#" expires="30" >
<cfcookie name="my_password" value="#encrypt(stUser.password,'saltmebaby')#" expires="30">
</cfif>
<cflocation url="#stResult.returnUrl#" addtoken="No">
<cfabort>
</cfif>