Login Action Page Issues.

Hey everyone,

I'm creating the action page for a login system for an application. The user enters their email address and password and clicks login and this page does the checking. It first querys the datasource for the user's email address and password. Then it checks to see if their account is active or not...the it checks to see if they are an administrator or not. If they are an administrator, they are passed to the user page with the sessionID=123454321 and if they are a regular user, the session variables are set and they are passed to the regular user's page with the sessionID=54321. I'm having issues with the logic of the CFIF statements.

Could somebody please proof read my code for me. I would very much appreciate it.

Thanks,
Chris

***********************************************************************************************
BEGIN CODE
***********************************************************************************************
<CFPARAM NAME="FORM.email" type="string">
<CFPARAM NAME="FORM.password" type="string">

<!------------ Check DB for User and PW ---------->
<CFQUERY Name="getuser" datasource="#dns#">
Select *
FROM tbl_Users
WHERE EMAIL = '#FORM.email#'
AND PASSWORD = '#FORM.password#'
</CFQUERY>

<CFSET SESSION.Auth.ActiveID = GetUser.Active>
<CFSET SESSION.Auth.Admin = GetUser.Admin>

<!------------Check to see if active or not------>
<CFIF GetUser.Active is 1>
<CFLOCATION URL="login.cfm?Error=1">
</CFIF>

<!------------Check to see if Administrator or not------>
<CFIF GetUser.Admin is 1>
<CFLOCATION URL="users.cfm?sessionID=123454321">

<!-----------If User Checks Out Okay------------->
<CFELSEIF GetUser.RecordCount EQ 1>
<CFSET SESSION.Auth = StructNew()>
<CFSET SESSION.Auth.IsLoggedIn = "YES">
<CFSET SESSION.Auth.ID = GetUser.ID>
<CFSET SESSION.Auth.FName = GetUser.FName>
<CFSET SESSION.Auth.LName = GetUser.LName>
<CFSET SESSION.Auth.User = GetUser.User>
<CFSET SESSION.Auth.LastIP = GetUser.LastIP>
<CFSET SESSION.Auth.LastBrowser = GetUser.LastBrowser>
<CFSET SESSION.Auth.LastLogin = GetUser.LastLogin>
<CFSET SESSION.Auth.TotLogins = GetUser.TotLogins>

<!------Send Now Logged In User To the Next Page----->
<CFLOCATION URL="users.cfm?sessionID=54321">
<CFELSE>
<CFLOCATION URL="login.cfm?Error=1">
</CFIF>
LVL 2
inverted_2000Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

mrichmonCommented:
Well a few things.

1) You should be usingcfqueryparam in the query like this:

<CFQUERY Name="getuser" datasource="#dns#">
Select *
FROM tbl_Users
WHERE EMAIL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.email#">
AND PASSWORD = <cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.password#">
</CFQUERY>
 
2) Password is a reserved word - you may want to consider re-naming your form field, but people usually don't have problems with this reserved word so I guess you could leave it.

3) You never check if you get any results.  The first step after the query should be

<cfif GetUser.RecordCount EQ 0>
   User not even logged in at all
<cfelse>
    The rest of your processing here
</cfif>

4) All lines that write to session should be in <cflock>

<cflock scope="session" timeout="10" type="exclusive">
   <CFSET SESSION.Auth.ActiveID = GetUser.Active>
   <CFSET SESSION.Auth.Admin = GetUser.Admin>
</cflock>

5) <CFIF GetUser.Active is 1>
<CFLOCATION URL="login.cfm?Error=1">
</CFIF>

This seems to be checking that they are active, and if so giving an error....  Unless 1 means not active

6) If the user is an admin you never set the rest of the variables.


I would adjust the code as follows:

<CFPARAM NAME="FORM.email" type="string">
<CFPARAM NAME="FORM.password" type="string">

<!------------ Check DB for User and PW ---------->
<CFQUERY Name="getuser" datasource="#dns#">
Select *
FROM tbl_Users
WHERE EMAIL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.email#">
AND PASSWORD = <cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.password#">
</CFQUERY>

<cfif GetUser.RecordCount EQ 1>
    <!------------Check to see if active or not------>
    <CFIF GetUser.Active NEQ 1>
         <CFLOCATION URL="login.cfm?Error=1">
    </CFIF>

    <!-----------If User Checks Out Okay------------->
     <cflock scope="session" timeout="10" type="exclusive">
         <CFSET SESSION.Auth = StructNew()>
         <CFSET SESSION.Auth.IsLoggedIn = "YES">
         <CFSET SESSION.Auth.ID = GetUser.ID>
         <CFSET SESSION.Auth.FName = GetUser.FName>
         <CFSET SESSION.Auth.LName = GetUser.LName>
         <CFSET SESSION.Auth.User = GetUser.User>
         <CFSET SESSION.Auth.LastIP = GetUser.LastIP>
         <CFSET SESSION.Auth.LastBrowser = GetUser.LastBrowser>
         <CFSET SESSION.Auth.LastLogin = GetUser.LastLogin>
         <CFSET SESSION.Auth.TotLogins = GetUser.TotLogins>
        <CFSET SESSION.Auth.ActiveID = GetUser.Active>
        <CFSET SESSION.Auth.Admin = GetUser.Admin>
    </cflock>

         <!--- now check if admin --->
         <CFIF GetUser.Admin is 1>
               <CFLOCATION URL="users.cfm?sessionID=123454321">
        <cfelse>
               <CFLOCATION URL="users.cfm?sessionID=54321">
        </cfif>
<CFELSE>
    <CFLOCATION URL="login.cfm?Error=1">
</CFIF>

 
marcin_komCommented:
I would start by checking the RecordCount first ... if it is 0 means no user found - raise an error.  Once you know the record is there you can access its fields.

I think your check for active should probably be
   <CFIF GetUser.Active is 0>
I am assuming you want to give error to users that are not active.

Here is the code given these assumptions:

-----------------------------------------------------------------------------------
<CFPARAM NAME="FORM.email" type="string">
<CFPARAM NAME="FORM.password" type="string">

<!------------ Check DB for User and PW ---------->
<CFQUERY Name="getuser" datasource="#dns#">
Select *
FROM tbl_Users
WHERE EMAIL = '#FORM.email#'
AND PASSWORD = '#FORM.password#'
</CFQUERY>

<!--- check if found record before assuming we can access data in the record --->
<CFIF GetUser.RecordCount EQ 0>
      <CFLOCATION URL="login.cfm?Error=1">
</CFIF>

<CFSET SESSION.Auth.ActiveID = GetUser.Active>
<CFSET SESSION.Auth.Admin = GetUser.Admin>

<!------------Check to see if active or not------>
<CFIF GetUser.Active is 0>
      <CFLOCATION URL="login.cfm?Error=1">
</CFIF>

<!------------Check to see if Administrator or not------>
<CFIF GetUser.Admin is 1>
      <CFLOCATION URL="users.cfm?sessionID=123454321">
      
<!-----------If User Checks Out Okay------------->
<CFELSEIF GetUser.RecordCount EQ 1>
      <CFSET SESSION.Auth = StructNew()>
      <CFSET SESSION.Auth.IsLoggedIn = "YES">
      <CFSET SESSION.Auth.ID = GetUser.ID>
      <CFSET SESSION.Auth.FName = GetUser.FName>
      <CFSET SESSION.Auth.LName = GetUser.LName>
      <CFSET SESSION.Auth.User = GetUser.User>
      <CFSET SESSION.Auth.LastIP = GetUser.LastIP>
      <CFSET SESSION.Auth.LastBrowser = GetUser.LastBrowser>
      <CFSET SESSION.Auth.LastLogin = GetUser.LastLogin>
      <CFSET SESSION.Auth.TotLogins = GetUser.TotLogins>
      
      <!------Send Now Logged In User To the Next Page----->
      <CFLOCATION URL="users.cfm?sessionID=54321">
      
<!--- This CFELSE is only reached if there are multiple records found
      This should raise some sort of an internal error --->
<CFELSE>
      <CFTHROW message="Multiple user records found where one expected">
</CFIF>
-------------------------------------------------------------------------------------------------

Hope this works,
Marcin
marcin_komCommented:
mrichmon,

You are a beast (in a good way) ... haha....  I can not believe how active you are on Experts-Exchange.

I bow my head and admit that your answer is definitely more complete than mine (looks like we submitted at about the same time, which is why I did not see your answer before posting mine).

Question for you - using <cfqueryparam... - is this the ColdFusion way of having parametarized queries thus allowing a much faster execution of repeated queries since the server does not need to compile the SQL statement each time?  How does ColdFusion keep track of which queries it has to maintain with open database connections to prevent the server from discarding the pre-compiled parameterized query.  I am familiar with this mechanism from Perl scripts, but the only advantage there is if you actually call the query more than once from a single script execution, thus the reason for my question.

Regards,
Marcin
Expert Spotlight: Joe Anderson (DatabaseMX)

We’ve posted a new Expert Spotlight!  Joe Anderson (DatabaseMX) has been on Experts Exchange since 2006. Learn more about this database architect, guitar aficionado, and Microsoft MVP.

mrichmonCommented:
Yes I noticed that you would have been typing when I was :o)

>><cfqueryparam... - is this the ColdFusion way of having parametarized queries thus allowing a much faster execution of repeated queries
Sort of.

It does cause Cold Fusion to parameterize the query.  It also properly encodes the parameter based on the database that you are using - which it knows since the datasource is set up in cfadmin.  So it knows the difference between putting # around dates for access, ' for SQL, etc.  It knows which dangerous characters to encode, etc.

Also it stores in server memory a template of the query.  I was told once it looks kind of like (using above as example):

Select *
FROM tbl_Users
WHERE EMAIL = ? (varchar)
AND PASSWORD = ? (varchar)

It knows that you will be sending in varchars there so it has a place holder there.  I don't know how long it lasts, but it is longer than the connection since it is in the server memory or a file I think - not sure here.

But another thing that cfqueryparm does is that it validates the type.

So if you had a query like

SELECT * FROM mytable WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#FORM.fieldname#">

And a malicious person tried to send in

1;DELETE FROM *

Without cfqueryparam this would delete data in all your tables.  (Called a SQL injection attack)
WIth cfqueryparam CF will generate an error saying that the above is not an integer and it will not process the statement.


It also speeds up queries, but I don't know the details of how.
inverted_2000Author Commented:
Thanks for your help so far guys.

This is starting to upset me.  I'm testing the code on my testing server and nothing is happening when I pass the form variables to the actionpage.  I changed the "password" field to "pass" on the forms and in the database.  

Since it's not running corrently on my server I uploaded it to the production server.  It seems that isn't working either.  It is saying that an object is expected, but I don't think that the production server is using the new pages that I've been FTPing to it.  I can tell because on the default page, the form validates the username for an email address and then check for the email address and for the password.  They are both required fields and the production server is allowing the fields to be passed to the authenticateuser.cfm page null.  

Let me play with it a little more and I'll post again!!!

Thanks a ton so far...the code is a lot more effecient then what I had.

Chris
mrichmonCommented:
It is possible that the server is cacheing the old pages.  A strange bug that sometimes happens in CF and very hard to fix.

You have to have access to the server itself to fix if this is the problem....
inverted_2000Author Commented:
Well this might help.  On the production server it states with a Java error that an object is expected at line 18.  That would be where we start to lock the variables:

<!-----------If User Checks Out Okay------------->
     <cflock scope="session" timeout="10" type="exclusive">
         <CFSET SESSION.Auth = StructNew()>
         <CFSET SESSION.Auth.IsLoggedIn = "YES">
         <CFSET SESSION.Auth.ID = GetUser.ID>
         <CFSET SESSION.Auth.FName = GetUser.FName>
         <CFSET SESSION.Auth.LName = GetUser.LName>
         <CFSET SESSION.Auth.User = GetUser.User>
             <CFSET SESSION.Auth.email = GetUser.email>
             <CFSET SESSION.Auth.pass = GetUser.pass>
         <CFSET SESSION.Auth.LastIP = GetUser.LastIP>
         <CFSET SESSION.Auth.LastBrowser = GetUser.LastBrowser>
         <CFSET SESSION.Auth.LastLogin = GetUser.LastLogin>
         <CFSET SESSION.Auth.TotLogins = GetUser.TotLogins>
        <CFSET SESSION.Auth.ActiveID = GetUser.active>
        <CFSET SESSION.Auth.Admin = GetUser.admin>
    </cflock>



Im my database I only have the fields:
ID
User
FName
LName
email
pass
active
admin
LastIP
LastBrowser
LastLogin
TotLogins

That looks like I've got everything, but maybe I don't?

Thanks,
Chris
inverted_2000Author Commented:
Also, the server doesn't seem to be caching the pages.  I've made changes to the form's fields and buttons and the server produced the changes.  
mrichmonCommented:
Then I would say there is an error in how you are testing if the fields are required....

A few questions

1) What exactly is the error - can you show it

2) What version of CF is the server?
inverted_2000Author Commented:
CF version is 6.1.

You can see the error via.  www.nilservices.com
Username: new@user.com
Password: user

inverted_2000Author Commented:
Also, on my test server the page never leaves the default.cfm page.  It says that it is opening authenticateuser.cfm, but I never get any debugging information because it never makes it throught the authenticate page.
mrichmonCommented:
The reason is that you get a Redirection limit exceeded error.  (you can see this in firefox - IE just times out)

What this means is that you have an infinite loop.

Most likely you have the application.cfm send them to the login page if not logged in?  I bet there is a logic error that is forcing them to the login page, then the login page sees they are logged in sends them back, but that page sends them to login, and so on....
inverted_2000Author Commented:
I just saw that error in Netscape....
inverted_2000Author Commented:
Here's the application.cfm page.

I'm sure that I have extra jazz in there, but since I build so few applications, I tend to reuse a lot of code....and this page is one of them:

<CFSET dns = "NilSource">
<CFAPPLICATION Name="PrivatePages" sessionmanagement="yes" sessiontimeout="#CreateTimeSpan(0,0,20,0)#">
<CFIF NOT IsDefined("SESSION.Auth.IsLoggedIn")>
      <CFIF IsDefined("FORM.email")>
            <CFINCLUDE TEMPLATE="authenticateuser.cfm">      
      </CFIF>
<CFLOCATION URL="login.cfm?Error=1">
</CFIF>
mrichmonCommented:
Okay logic should look as follows:

<CFIF NOT IsDefined("SESSION.Auth.IsLoggedIn")>
          <CFINCLUDE TEMPLATE="authenticateuser.cfm">
       <CFABORT>
</CFIF>
inverted_2000Author Commented:
Okay....now it's upset about the  CFQUERY on the authenticateuser page.  I think we've almost got it (o:

Error Occurred While Processing Request  
The required parameter FORM.email was not provided.  
This page uses the CFPARAM tag to declare the parameter FORM.email as required for this template. The parameter is not available.Please verify that you have passed or initialized the parameter correctly. If you wish to set a default value for the parameter you should use the DEFAULT attribute of the CFPARAM tag.  
 
The error occurred in C:\Inetpub\wwwroot\nilservices\trusted\authenticateuser.cfm: line 1
 
1 : <CFPARAM NAME="FORM.email" type="string">
2 : <CFPARAM NAME="FORM.passText" type="string">
3 :

 

--------------------------------------------------------------------------------
 
Please try the following:
Check the ColdFusion documentation to verify that you are using the correct syntax.
Search the Knowledge Base to find a solution to your problem.

 
Browser   Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 1.0.3705)
Remote Address   127.0.0.1
Referrer   http://localhost/nilservices/default.cfm 
Date/Time   06-Jul-05 03:22 PM
 
Stack Trace (click to expand)  
 
mrichmonCommented:
Maybe that should have been

<CFIF NOT IsDefined("SESSION.Auth.IsLoggedIn")>
          <CFINCLUDE TEMPLATE="login.cfm">
       <CFABORT>
</CFIF>

Not sure exactly how you pages are named or the flow...

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
inverted_2000Author Commented:
WONDERFUL (o:

TY soooo very very much for your help today mrichmon!!!

Here's the finial solution.

**************************************
FORM PAGE (default.cfm)
**************************************
 <cfform name="login" method="post" action="trusted/authenticateuser.cfm">
              <tr>
                <td>&nbsp;</td>
                <td><cfinput name="UserLogin" type="text" class="form1" message="You need to enter an email address or you have entered an invalid address."
                        validate="Regular_expression" required="No" pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$">
                </td>
              </tr>
              <tr>
                <td>&nbsp;</td>
                <td class="navtxtWhite">password:</td>
              </tr>
              <tr>
                <td>&nbsp;</td>
                <td><cfinput name="UserPassword" type="password" class="form1" required="yes" message="How about a Password?"></td>
              </tr>
              <tr>
                <td>&nbsp;</td>
                <td class="copyright"><input name="Login" type="submit" value="Login"></td>
              </tr>
            </cfform>

******************************************************************************
APPLICATION PAGE (application.cfm) /
Login.cfm is the page that users from all form are turned to if they have an error.  They can also email
themselves their password from this page or try to login again.  It's the purgatory of the application.
Error=1 is just what I used to tell the user that they have a bad or miss spelled username or password.
Error=2 would state that their account has been suspended when active is found to be unchecked or negative
and so on...
*******************************************************************************
<CFSET dns = "NilSource">
<CFAPPLICATION Name="PrivatePages" sessionmanagement="yes" sessiontimeout="#CreateTimeSpan(0,0,20,0)#">
<CFIF NOT IsDefined("SESSION.Auth.IsLoggedIn")>
          <CFINCLUDE TEMPLATE="../login.cfm&Error=1">
</CFIF>

**************************************************************************
ACTION PAGE PAGE (authenticateuser.cfm) /
**************************************************************************
<CFPARAM NAME="FORM.UserLogin" type="string">
<CFPARAM NAME="FORM.UserPassword" type="string">

<!------------ Check DB for User and PW ---------->
<CFQUERY Name="getUser" datasource="#dns#">
Select *
FROM tbl_Users
WHERE EMAIL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.UserLogin#">
AND PASSWORD = <cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.UserPassword#">
</CFQUERY>

<cfif GetUser.RecordCount EQ 1>
    <!------------Check to see if active or not------>
    <CFIF GetUser.Active NEQ 1>
         <CFLOCATION URL="../login.cfm?Error=2">
    </CFIF>

    <!-----------If User Checks Out Okay------------->
     <cflock scope="session" timeout="10" type="exclusive">
         <CFSET SESSION.Auth = StructNew()>
         <CFSET SESSION.Auth.IsLoggedIn = "YES">
         <CFSET SESSION.Auth.ID = GetUser.ID>
         <CFSET SESSION.Auth.FName = GetUser.FName>
         <CFSET SESSION.Auth.LName = GetUser.LName>
         <CFSET SESSION.Auth.User = GetUser.User>
         <CFSET SESSION.Auth.LastIP = GetUser.LastIP>
         <CFSET SESSION.Auth.LastBrowser = GetUser.LastBrowser>
         <CFSET SESSION.Auth.LastLogin = GetUser.LastLogin>
         <CFSET SESSION.Auth.TotLogins = GetUser.TotLogins>
        <CFSET SESSION.Auth.ActiveID = GetUser.Active>
        <CFSET SESSION.Auth.Admin = GetUser.Admin>
    </cflock>

         <!--- now check if admin --->
         <CFIF GetUser.Admin is 1>
               <CFLOCATION URL="users.cfm?sessionID=123454321">
        <cfelse>
               <CFLOCATION URL="users.cfm?sessionID=54321">
        </cfif>
<CFELSE>
    <CFLOCATION URL="../login.cfm?Error=1">
</CFIF>
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Web Servers

From novice to tech pro — start learning today.