Article

The PHP Anthology Volume 2, Chapter 1 - Access Control

Page: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Next

How do I deal with members who forget their passwords?

In the last solution, I was happy to explain just how great human beings are in comparison to computers. Unfortunately, though, we have a tendency to "age out" important information such as the password we need to log into a site. A feature that allows users to retrieve forgotten passwords is an essential time saver. Overlook this, and you can expect to waste a lot of time changing passwords for people who have forgotten them.

If you encrypt the passwords in your database, you'll need a mechanism that generates a new password that, preferably, is easy to remember. If you're storing passwords as-is, without encryption, it's probably acceptable simply to send the password to the user's registered email address. Using an email address that you've already confirmed as valid is more reliable than the "Secret Question" approach. This common tactic asks users simple questions to refresh their memories, such as, "Where were you born?" and "What's your date of birth?" Just ask yourself how many organizations, both on and offline, you've given that information to. Some online applications, such as forums, even make your birthday available for all to see, should you provide it. Details like this may well be common knowledge.

To solve the problem, we'll build a general AccountMaintenance class, which will do some of the maintenance work for us, then supply it with the information it needs to either fetch an unencrypted password, or generate a new (memorable) password. The typical approach used to generate memorable passwords is inspired by the Secure Memorable Password Generator found at Codewalkers.com.

Password Reminder

Starting with the simple password fetching code, the AccountMaintenance class begins with the usual constants, which allow it to be applied to a different table structure if need be. Note, in particular, the USER_LOGIN_VAR constant, which must contain the same value as that defined by the Auth class.

Example 1.33. AccessControl/AccountMaintenance.php (in SPLIB) (excerpt)          
         
<?php          
/**          
* Constants which define table and column names          
*/          
# Modify this constant to reflect session variable name          
// Name to use for login variable used in Auth class          
@define('USER_LOGIN_VAR', 'login');          
# Modify these constants to match your user login table          
// Name of users table          
@define('USER_TABLE', 'user');          
// Name of user_id column in table          
@define('USER_TABLE_ID', 'user_id');          
// Name of login column in table          
@define('USER_TABLE_LOGIN', 'login');          
// Name of password column in table          
@define('USER_TABLE_PASSW', 'password');          
// Name of email column in table          
@define('USER_TABLE_EMAIL', 'email');          
// Name of firstname column in table          
@define('USER_TABLE_FIRST', 'firstName');          
// Name of lastname column in table          
@define('USER_TABLE_LAST', 'lastName');          
/**          
* AccountMaintenance Class<br />          
* Provides functionality for users to manage their own accounts          
* @access public          
* @package SPLIB          
*/          
class AccountMaintenance {          
 /**          
  * Database connection          
  * @access private          
  * @var object          
  */          
 var $db;          
         
 /**          
  * A list of words to use in generating passwords          
  * @access private          
  * @var array          
  */          
 var $words;          
         
 /**          
  * AccountMaintenance constructor          
  * @param object instance of database connection          
  * @access public          
  */          
 function AccountMaintenance(&$db)          
 {          
   $this->db = &$db;          
 }          
         
 /**          
  * Given an email address, returns the user details          
  * that account. Useful is password is not encrpyted          
  * @param string email address          
  * @return array user details          
  * @access public          
  */          
 function fetchLogin($email)          
 {          
   $email = mysql_escape_string($email);          
   $sql = "SELECT          
             " . USER_TABLE_LOGIN . ", " . USER_TABLE_PASSW . ",          
             " . USER_TABLE_FIRST . ", " . USER_TABLE_LAST . "          
           FROM          
             " . USER_TABLE . "          
           WHERE          
             " . USER_TABLE_EMAIL . "='$email'";          
   $result = $this->db->query($sql);          
   if ($result->size() == 1) {          
     return $result->fetch();          
   } else {          
     return FALSE;          
   }          
 }          

The fetchLogin method looks for a single row that matches the user's email address (note this assumes you have declared a UNIQUE index on the email column so that entries can only appear once. If you use a different table structure, you'll need to modify the query in this method).

Next, we put the simple forgotten password mechanism into action with QuickForm and phpmailer:

Example 1.34. 9.php (excerpt)          
         
<?php          
// Include MySQL class          
require_once 'Database/MySQL.php';          
         
// Include AccountMaintenance class          
require_once 'AccessControl/AccountMaintenance.php';          
         
// Include QuickForm class          
require_once 'HTML/QuickForm.php';          
         
// Include phpmailer class          
require_once 'ThirdParty/phpmailer/class.phpmailer.php';          
         
$host   = 'localhost'; // Hostname of MySQL server          
$dbUser = 'harryf';    // Username for MySQL          
$dbPass = 'secret';    // Password for user          
$dbName = 'sitepoint'; // Database name          
         
// phpmailer settings          
$yourName  = 'Your Name';          
$yourEmail = 'you@yourdomain.com';          
$subject   = 'Your password';          
$msg       = 'Here are your login details. Please change your ' .          
            'password.';

Here, we've set up the environment as usual, including the necessary classes. We've also defined variables for the phpmailer class; these will be the same irrespective of who has forgotten a password.

And now, let's set up QuickForm:

Example 1.35. 9.php (excerpt)          
         
// Instantiate the QuickForm class          
$form = new HTML_QuickForm('passwordForm', 'POST');          
         
// Add a header to the form          
$form->addHeader('Forgotten Your Password?');          
         
// Add a field for the email address          
$form->addElement('text', 'email', 'Enter your email address');          
$form->addRule('email', 'Enter your email', 'required', FALSE,          
              'client');          
$form->addRule('email', 'Enter a valid email address', 'email',          
              FALSE, 'client');          
         
// Add a submit button called submit with label "Send"          
$form->addElement('submit', 'submit', 'Get Password');

If the form has been submitted, we instantiate the MySQL and AccountMaintenance classes and use the fetchLogin method to determine whether there's a matching email address in the user table. If there is, we use phpmailer to send the user an email containing the login and password.

Example 1.36. 9.php (excerpt)          
         
// If the form is submitted...          
if ($form->validate()) {          
 // Instantiate MySQL connection          
 $db = &new MySQL($host, $dbUser, $dbPass, $dbName);          
           
 // Instantiate Account Maintenance class          
 $aMaint = new AccountMaintenance($db);          
           
 if (!$details =          
     $aMaint->fetchLogin($form->getSubmitValue('email'))) {          
   echo 'We have no record of your account';          
 } else {          
   $mail = new phpmailer();          
   // Define who the message is from          
   $mail->From = $yourEmail;          
   $mail->FromName = $yourName;          
             
   // Set the subject of the message          
   $mail->Subject = $subject;          
             
   // Build the message          
   $mail->Body = $msg . "\n\nLogin: " . $details['login'] .          
                 "\nPassword: " . $details['password'];          
             
   // Add the recipient          
   $name = $details['firstName'] . ' ' . $details['lastName'];          
   $mail->AddAddress($form->getSubmitValue('email'), $name);          
             
   // Send the message          
   if(!$mail->Send()) {          
     echo 'An email has been sent to ' .          
          $form->getSubmitValue('email');          
   } else {          
     echo 'Problem sending your details. Please contact the ' .          
          'site administrators';          
   }          
 }          
} else {          
 // If not submitted, display the form          
 $form->display();          
}          
?>

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links