Forgot Password Recovery in Login System with PHP and MySQL

Forgot password recovery feature is mandatory for the login system. It helps the user to update account password which they have forgotten. Using forgot password link user can easily reset their account password. In this tutorial, we’ll show you the forgot password recovery process and create a script to implement forgot password email functionality in PHP Login System.

Our previous PHP Login System tutorial had provided a guide to implementing user registration and login system in PHP. Now we’ll extend that PHP login script with forgot password functionality with PHP and MySQL. Also, we’ll show you how can send reset password link via email to the user. Before you begin with this forgot password recovery tutorial, we suggest go through the previous PHP login system tutorial first.

Here the files list that is used in forgot password recovery process.

  • user.php – handle database related works
  • userAccount.php – handle forgot password, reset password, and email sending.
  • index.php – display login form with forgot password link.
  • forgotPassword.php – display forgot password form.
  • resetPassword.php – display reset password form.
  • style.css – styling login, forgot, and reset form.

Database Table Creation

To store the user details you already have a users table in the MySQL database. Now add a column named forgot_pass_identity to user table.

ALTER TABLE `users` ADD `forgot_pass_identity` VARCHAR(32) NOT NULL AFTER `phone`;

Entire users table SQL will like the following.

CREATE TABLE `users` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `first_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
 `last_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
 `email` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
 `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `phone` varchar(15) COLLATE utf8_unicode_ci NOT NULL,
 `forgot_pass_identity` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
 `created` datetime NOT NULL,
 `modified` datetime NOT NULL,
 `status` enum('1','0') COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

User Class (user.php)

User Class handles all the database related works, like get, insert, update user data. This class has extended with one method (update()) for update forgot password identity code in the users table.

<?php
/*
 * User Class
 * This class is used for database related (connect fetch, and insert) operations
 * @author    CodexWorld.com
 * @url       http://www.codexworld.com
 * @license   http://www.codexworld.com/license
 */
class User{
    private 
$dbHost     "localhost";
    private 
$dbUsername "root";
    private 
$dbPassword "";
    private 
$dbName     "codexworld";
    private 
$userTbl    "users";
    
    public function 
__construct(){
        if(!isset(
$this->db)){
            
// Connect to the database
            
$conn = new mysqli($this->dbHost$this->dbUsername$this->dbPassword$this->dbName);
            if(
$conn->connect_error){
                die(
"Failed to connect with MySQL: " $conn->connect_error);
            }else{
                
$this->db $conn;
            }
        }
    }
    
    
/*
     * Returns rows from the database based on the conditions
     * @param string name of the table
     * @param array select, where, order_by, limit and return_type conditions
     */
    
public function getRows($conditions = array()){
        
$sql 'SELECT ';
        
$sql .= array_key_exists("select",$conditions)?$conditions['select']:'*';
        
$sql .= ' FROM '.$this->userTbl;
        if(
array_key_exists("where",$conditions)){
            
$sql .= ' WHERE ';
            
$i 0;
            foreach(
$conditions['where'] as $key => $value){
                
$pre = ($i 0)?' AND ':'';
                
$sql .= $pre.$key." = '".$value."'";
                
$i++;
            }
        }
        
        if(
array_key_exists("order_by",$conditions)){
            
$sql .= ' ORDER BY '.$conditions['order_by']; 
        }
        
        if(
array_key_exists("start",$conditions) && array_key_exists("limit",$conditions)){
            
$sql .= ' LIMIT '.$conditions['start'].','.$conditions['limit']; 
        }elseif(!
array_key_exists("start",$conditions) && array_key_exists("limit",$conditions)){
            
$sql .= ' LIMIT '.$conditions['limit']; 
        }
        
        
$result $this->db->query($sql);
        
        if(
array_key_exists("return_type",$conditions) && $conditions['return_type'] != 'all'){
            switch(
$conditions['return_type']){
                case 
'count':
                    
$data $result->num_rows;
                    break;
                case 
'single':
                    
$data $result->fetch_assoc();
                    break;
                default:
                    
$data '';
            }
        }else{
            if(
$result->num_rows 0){
                while(
$row $result->fetch_assoc()){
                    
$data[] = $row;
                }
            }
        }
        return !empty(
$data)?$data:false;
    }
    
    
/*
     * Insert data into the database
     * @param string name of the table
     * @param array the data for inserting into the table
     */
    
public function insert($data){
        if(!empty(
$data) && is_array($data)){
            
$columns '';
            
$values  '';
            
$i 0;
            if(!
array_key_exists('created',$data)){
                
$data['created'] = date("Y-m-d H:i:s");
            }
            if(!
array_key_exists('modified',$data)){
                
$data['modified'] = date("Y-m-d H:i:s");
            }
            foreach(
$data as $key=>$val){
                
$pre = ($i 0)?', ':'';
                
$columns .= $pre.$key;
                
$values  .= $pre."'".$val."'";
                
$i++;
            }
            
$query "INSERT INTO ".$this->userTbl." (".$columns.") VALUES (".$values.")";
            
$insert $this->db->query($query);
            return 
$insert?$this->db->insert_id:false;
        }else{
            return 
false;
        }
    }
    
    
/*
     * Update data into the database
     * @param string name of the table
     * @param array the data for inserting into the table
     */
    
public function update($data$conditions){
        if(!empty(
$data) && is_array($data) && !empty($conditions)){
            
//prepare columns and values sql
            
$cols_vals '';
            
$i 0;
            if(!
array_key_exists('modified',$data)){
                
$data['modified'] = date("Y-m-d H:i:s");
            }
            foreach(
$data as $key=>$val){
                
$pre = ($i 0)?', ':'';
                
$cols_vals .= $pre.$key." = '".$val."'";
                
$i++;
            }
            
            
//prepare where conditions
            
$whereSql '';
            
$ci 0;
            foreach(
$conditions as $key => $value){
                
$pre = ($ci 0)?' AND ':'';
                
$whereSql .= $pre.$key." = '".$value."'";
                
$ci++;
            }
            
            
//prepare sql query
            
$query "UPDATE ".$this->userTbl." SET ".$cols_vals." WHERE ".$whereSql;

            
//update data
            
$update $this->db->query($query);
            return 
$update?true:false;
        }else{
            return 
false;
        }
    }

}

userAccount.php

This file controls the registration, login, logout, forgot password, and reset password request. This script has extended with two requests forgotSubmit and resetSubmit. Also, the email sending functionality is added in forgotSubmit request to send the email with reset password link to the user.

<?php
//start session
session_start();

//load and initialize user class
include 'user.php';
$user = new User();

if(isset($_POST['forgotSubmit'])){
    
//check whether email is empty
    
if(!empty($_POST['email'])){
        
//check whether user exists in the database
        
$prevCon['where'] = array('email'=>$_POST['email']);
        
$prevCon['return_type'] = 'count';
        
$prevUser $user->getRows($prevCon);
        if(
$prevUser 0){
            
//generat unique string
            
$uniqidStr md5(uniqid(mt_rand()));;
            
            
//update data with forgot pass code
            
$conditions = array(
                
'email' => $_POST['email']
            );
            
$data = array(
                
'forgot_pass_identity' => $uniqidStr
            
);
            
$update $user->update($data$conditions);
            
            if(
$update){
                
$resetPassLink 'http://codexworld.com/resetPassword.php?fp_code='.$uniqidStr;
                
                
//get user details
                
$con['where'] = array('email'=>$_POST['email']);
                
$con['return_type'] = 'single';
                
$userDetails $user->getRows($con);
                
                
//send reset password email
                
$to $userDetails['email'];
                
$subject "Password Update Request";
                
$mailContent 'Dear '.$userDetails['first_name'].', 
                <br/>Recently a request was submitted to reset a password for your account. If this was a mistake, just ignore this email and nothing will happen.
                <br/>To reset your password, visit the following link: <a href="'
.$resetPassLink.'">'.$resetPassLink.'</a>
                <br/><br/>Regards,
                <br/>CodexWorld'
;
                
//set content-type header for sending HTML email
                
$headers "MIME-Version: 1.0" "\r\n";
                
$headers .= "Content-type:text/html;charset=UTF-8" "\r\n";
                
//additional headers
                
$headers .= 'From: CodexWorld<sender@example.com>' "\r\n";
                
//send email
                
mail($to,$subject,$mailContent,$headers);
                
                
$sessData['status']['type'] = 'success';
                
$sessData['status']['msg'] = 'Please check your e-mail, we have sent a password reset link to your registered email.';
            }else{
                
$sessData['status']['type'] = 'error';
                
$sessData['status']['msg'] = 'Some problem occurred, please try again.';
            }
        }else{
            
$sessData['status']['type'] = 'error';
            
$sessData['status']['msg'] = 'Given email is not associated with any account.'
        }
        
    }else{
        
$sessData['status']['type'] = 'error';
        
$sessData['status']['msg'] = 'Enter email to create a new password for your account.'
    }
    
//store reset password status into the session
    
$_SESSION['sessData'] = $sessData;
    
//redirect to the forgot pasword page
    
header("Location:forgotPassword.php");
}elseif(isset(
$_POST['resetSubmit'])){
    
$fp_code '';
    if(!empty(
$_POST['password']) && !empty($_POST['confirm_password']) && !empty($_POST['fp_code'])){
        
$fp_code $_POST['fp_code'];
        
//password and confirm password comparison
        
if($_POST['password'] !== $_POST['confirm_password']){
            
$sessData['status']['type'] = 'error';
            
$sessData['status']['msg'] = 'Confirm password must match with the password.'
        }else{
            
//check whether identity code exists in the database
            
$prevCon['where'] = array('forgot_pass_identity' => $fp_code);
            
$prevCon['return_type'] = 'single';
            
$prevUser $user->getRows($prevCon);
            if(!empty(
$prevUser)){
                
//update data with new password
                
$conditions = array(
                    
'forgot_pass_identity' => $fp_code
                
);
                
$data = array(
                    
'password' => md5($_POST['password'])
                );
                
$update $user->update($data$conditions);
                if(
$update){
                    
$sessData['status']['type'] = 'success';
                    
$sessData['status']['msg'] = 'Your account password has been reset successfully. Please login with your new password.';
                }else{
                    
$sessData['status']['type'] = 'error';
                    
$sessData['status']['msg'] = 'Some problem occurred, please try again.';
                }
            }else{
                
$sessData['status']['type'] = 'error';
                
$sessData['status']['msg'] = 'You does not authorized to reset new password of this account.';
            }
        }
    }else{
        
$sessData['status']['type'] = 'error';
        
$sessData['status']['msg'] = 'All fields are mandatory, please fill all the fields.'
    }
    
//store reset password status into the session
    
$_SESSION['sessData'] = $sessData;
    
$redirectURL = ($sessData['status']['type'] == 'success')?'index.php':'resetPassword.php?fp_code='.$fp_code;
    
//redirect to the login/reset pasword page
    
header("Location:".$redirectURL);
}

Note that:

Don’t forget to change the email content, from address, and website base URL in $resetPassLink as per your project requirement.

Login Form with Forgot Password Link (index.php)

A forgot password link is added in the existing login form for navigating user to forgot password form.

<div class="container">
    <form action="userAccount.php" method="post">
        <input type="email" name="email" placeholder="EMAIL" required="">
        <input type="password" name="password" placeholder="PASSWORD" required="">
        <div class="send-button">
            <input type="submit" name="loginSubmit" value="LOGIN">
        </div>
        <a href="forgotPassword.php">Forgot password?</a>
    </form>
</div>

Forgot Password Form (forgotPassword.php)

A form displays to collect the account email for identifying user account. The form is submitted to the userAccount.php file with a forgotSubmit request.

<?php
session_start
();
$sessData = !empty($_SESSION['sessData'])?$_SESSION['sessData']:'';
if(!empty(
$sessData['status']['msg'])){
    
$statusMsg $sessData['status']['msg'];
    
$statusMsgType $sessData['status']['type'];
    unset(
$_SESSION['sessData']['status']);
}
?> <h2>Enter the Email of Your Account to Reset New Password</h2> <?php echo !empty($statusMsg)?'<p class="'.$statusMsgType.'">'.$statusMsg.'</p>':''?> <div class="container"> <div class="regisFrm"> <form action="userAccount.php" method="post"> <input type="email" name="email" placeholder="EMAIL" required=""> <div class="send-button"> <input type="submit" name="forgotSubmit" value="CONTINUE"> </div> </form> </div> </div>

Reset Password Form (resetPassword.php)

This script is loaded when the user clicks on the reset password link in the mail. A form will appear to update account password and the form is submitted to the userAccount.php file with a resetSubmit request.

<?php
session_start
();
$sessData = !empty($_SESSION['sessData'])?$_SESSION['sessData']:'';
if(!empty(
$sessData['status']['msg'])){
    
$statusMsg $sessData['status']['msg'];
    
$statusMsgType $sessData['status']['type'];
    unset(
$_SESSION['sessData']['status']);
}
?> <h2>Reset Your Account Password</h2> <?php echo !empty($statusMsg)?'<p class="'.$statusMsgType.'">'.$statusMsg.'</p>':''?> <div class="container"> <div class="regisFrm"> <form action="userAccount.php" method="post"> <input type="password" name="password" placeholder="PASSWORD" required=""> <input type="password" name="confirm_password" placeholder="CONFIRM PASSWORD" required=""> <div class="send-button"> <input type="hidden" name="fp_code" value="<?php echo $_REQUEST['fp_code']; ?>"/> <input type="submit" name="resetSubmit" value="RESET PASSWORD"> </div> </form> </div> </div>

CSS Code (style.css)

No new CSS code are added to implement forgot password functionality. The same CSS file is included from our login system.

.container {
    width: 40%;
    margin: 0 auto;
    background-color: #f7f7f7;
    color: #757575;
    font-family: 'Raleway', sans-serif;
    text-align: left;
    padding: 30px;
}
h2 {
    font-size: 30px;
    font-weight: 600;
    margin-bottom: 10px;
}
.container p {
    font-size: 18px;
    font-weight: 500;
    margin-bottom: 20px;
}
.regisFrm input[type="text"], .regisFrm input[type="email"], .regisFrm input[type="password"] {
    width: 94.5%;
    padding: 10px;
    margin: 10px 0;
    outline: none;
    color: #000;
    font-weight: 500;
    font-family: 'Roboto', sans-serif;
}
.send-button {
    text-align: center;
    margin-top: 20px;
}
.send-button input[type="submit"] {
    padding: 10px 0;
    width: 60%;
    font-family: 'Roboto', sans-serif;
    font-size: 18px;
    font-weight: 500;
    border: none;
    outline: none;
    color: #FFF;
    background-color: #2196F3;
    cursor: pointer;
}
.send-button input[type="submit"]:hover {
    background-color: #055d54;
}
a.logout{float: right;}
p.success{color:#34A853;}
p.error{color:#EA4335;}

Conclusion

Hope this tutorial will help you to implement forgot password recovery system with reset email sending feature. Here we’ve tried to extend our previous login system script with simple forgot password functionality. To get the login system and forgot password functionality script together, download the source code.

Do you want to get implementation help, or enhance the functionality of this script? Click here to Submit Service Request

17 Comments

  1. Jeff Said...
  2. Ananth Said...
  3. Fracisco Said...
  4. Joel Malach Said...
    • CodexWorld Said...
  5. Andrea Said...
  6. Marcus Said...
    • CodexWorld Said...
  7. Nida Akram Said...
  8. Rashmi Said...
  9. Felipe Said...
  10. Anders Yuran Said...
  11. Harshil Patel Said...
    • CodexWorld Said...
  12. Josh Said...
    • CodexWorld Said...

Leave a reply

keyboard_double_arrow_up