Sunday, September 14, 2008

password recovery in coldfusion

Users and Forgotten Passwords

You know the situation. You require your visitors to create a username and password in order to access your Web site (or at least certain sections). And of course, 90% of those users will forget their password within the first week.

It?s an inconvenience for them if they have to wait for you to send them their password. The Web is about instant gratification, and there?s just too much competition out there to risk a situation like that.

This tutorial will deal with two possible ways that you can resolve this situation.

E-mailing a Password

This method will allow a user to enter their user name and/or e-mail address, and an e-mail will automatically be sent to them with their password.

In order for this to work, let?s assume a database setup as follows:
tbl_user
column name datatype

userID autonumber/identity
username char
password char
email char
This is merely a suggestion. userID is a primary key; a unique autonumber. However, it?s not really necessary. In theory, usernames should be unique, as well as e-mail addresses. Either of those could just as well serve as primary key, negating the need for the userID column.

It is important to make sure that email addresses are unique. When new users sign up, check the database to make sure the email the new user is signing up with is not already in the database. If it is, prompt the user to enter a different e-mail address (or, if they have an account but forgot their password, offer to send them their password via the method outlined below).

In our database, we might have records such as:

userID Username password email
1 CJ 123abc charlie@griefer.com
2 BillyBob 987xxx bb@aol.com
What happens if CJ forgets his password? Well, we already know his e-mail address (charlie@griefer.com)?so we can easily send him his password.

This process requires two pages?one to allow the user to enter their e-mail address, and another to validate the user, and send the user their password.

password_request.cfm


<html>
<head>
<title>Password Request</title>
</head>

<body>

<form action=?password_send.cfm? method=?post?>
Please enter your e-mail address below:
<input type=?text? name=?email? />
<br />
<input type=?submit? value=?proceed? />
</form>

</body>
</html>


password_send.cfm


<!--- see if this email address exists in the database --->
<cfquery name=?checkEmail? datasource=?myDSN?>
SELECT username, password
FROM tbl_user
WHERE email = <cfqueryparam value=?#trim(form.email)#? cfsqltype=?cf_sql_char?>
</cfquery>

<html>
<head>
<title>Password Request</title>
</head>

<body>

<!--- was a record returned from the query? --->
<cfif checkEmail.recordCount>

<cfmail to=?#form.email#?
from=?webmaster@yoursite.com?
subject=?your password?>
Hello #checkEmail.username#.

You recently requested your password from [NAME OF YOUR WEB SITE HERE].

Your password is: #checkEmail.password#.

Please write this down for future reference.

Thank You,
Webmaster
</cfmail>

Thank you, #checkEmail.username#. Your password has been sent and should
arrive shortly.

<!--- no record found, display message to the user --->
<cfelse>

We?re sorry. We were unable to locate that email address in our database. Please <a href=?password_request.cfm?>try to enter your email address again</a>.

</cfif>



That?s it! Your visitors can now get results immediately.


Using a Secret Question

There?s another method that some sites employ that don?t? even require your visitors to have to wait for an email.

This would entail asking the visitor a ?secret question?. Something only he or she would know, in order to prove their identity.

This will require two additional columns in the table that we used above:
tbl_user
column name datatype

userID autonumber/identity
username char
password char
email char
secretQuestion char
secretAnswer char
As you can see, we?re storing a secret question (and answer) for each user. This information would be gathered during their initial sign up process. You can possibly provide them with a list of secret questions, of which they get to pick one?

for example:
• mother?s maiden name
• favorite sport?s team
• favorite pet name
• street you grew up on
Store both the question, and the answer the user enters into the database.

This method will require 3 templates:
1. a template where the visitor can enter their user name
2. a template that retrieves/displays the secret question, and allows the user to input the answer
3. a template that checks the given answer against the database, and displays the results to the user.
password_request.cfm
<!--- this is the template that will allow the user to enter his/her username --->

<html>
<head>
<title>Request Password</title>
</head>

<body>

<form action=?display_question.cfm? method=?post?>
Please enter your user name below, and click ?proceed?.
<br /><br />
<input type=?text? name=?username? />
<input type=?submit? value=?proceed? />
</form>

</body>
</html>



display_question.cfm
<!---
this template checks the username entered on the previous page. if no username was found, the form is displayed again, and the user is asked to re-enter their username.

if a username is found, we output the secret question, with a form to collect the answer. it?s important to note that we are passing the username to the next template in a hidden form field!
--->


<cfquery name=?getQuestion? datasource=?myDSN?>
SELECT secretQuestion
FROM tbl_user
WHERE username = <cfqueryparam value=?#trim(form.username)#? cfsqltype=?cf_sql_char?>
</cfquery>

<html>

<head>
<title>Request Password </title>
</head>

<body>

<cfif getQuestion.recordCount EQ 0>

We?re sorry?we could not find that user name in our database. Please try again.
<br /><br />
<form action=?display_question.cfm? method=?post?>
<input type=?text? name=?username? />
<input type=?submit? value=?proceed? />
</form>

<cfelse>

Please answer the following question:

<form action=?show_password.cfm? method=?post?>
<cfoutput query=?getQuestion?>
<input type=?hidden? name=?username? value=?#form.username#? />
#secretQuestion#
</cfoutput>
<br /><br />
<input type=?text? name=?secretAnswer? />
<input type=?submit? value=?proceed? />
</form>

</cfif>

</body>
</html>


show_password.cfm
<!---
finally, we query the database for the username (taken from the hidden form field on the previous page), and the secret answer (from the previous page?s form).

if the user has entered the correct answer, we can go ahead and display their password to them.
--->


<cfquery name=?checkpassword? datasource=?myDSN?>
SELECT password
FROM tbl_user
WHERE
username = <cfqueryparam value=?#form.username#? cfsqltype=?cf_sql_char?>
AND
secretAnswer = <cfqueryparam value=?#form.secretAnswer#? cfsqltype=?cf_sql_char?>
</cfquery>

<html>

<head>
<title>Request Password</title>
</head>

<body>

<!--- if a record was returned from our query? --->
<cfif checkpassword.recordCount>
<cfoutput>
Hello, #form.username#. Your password is #checkpassword.password#. Please write this down for future use.
</cfoutput>
<!--- no record was returned --->
<cfelse>
Sorry, but we were unable to verify your identity based on the information that you provided. Please <a href=?password_request.cfm?>try and enter your information again</a>.

If you feel an error has been made?please <a href=?mailto:webmaster@mysite.com?>send an e-mail to webmaster@mysite.com</a>.
</cfif>

</body>
</html>



That?s it! Depending on the level of security you want, it?s also possible to combine the two methods above, and ask a user for their e-mail address *and* the answer to a secret question before e-mailing the password.

Either way, both methods allow you to deliver instant customer service to your visitors. Something that they?re sure to appreciate, and something that will keep them coming back.

1 comment:

DFW said...

Thanks, you just saved me a ton of time.
I am sending you a link below talking about best practices in dealing with password resets -- I learned much from it, and offer it not as criticism of your approach, but just to inform you.
Thanks for sharing so generously.
David