Building a Password Reset Mechanism for Our Application

To complete the authentication system, we need the ability to reset the password should we forget what it is. Here are the steps to do so:

  1. Create a new method called reset in the Admin Controller.
  2. Again, check if the user is logged in and if they are, redirect them back to the admin.
  3. Set up an errors array and set the page title before loading a view called reset:
    public function reset()
    {
        if (Session::get('logged_in')) {
            Url::redirect('/admin');
        }
    
        $errors = [];
    
        $title = 'Reset Account';
    
        $this->view->render('admin/auth/reset', compact('title', 'errors'));
    }
  4. Now create a view called reset.php in app/views/admin/auth and enter:
    <?php include(APPDIR.'views/layouts/header.php');?>
    
    <div class="wrapper well">
    
        <?php include(APPDIR.'views/layouts/errors.php');?>
    
        <h1>Reset Account</h1>
    
        <form method="post">
    …
    …
        </div>
    
    <?php include(APPDIR.'views/layouts/footer.php');?>

    Note

    The form will post to the same url /admin/reset. The only data we're collecting is the email address. The email address will be used to verify that the user exists before proceeding.

  5. Now, go back to the reset method on the Admin Controller.
  6. First, check if the form has been submitted with an isset and pass in the submit button name:
    if (isset($_POST['submit'])) {
  7. Next, make sure that the email address is isset, otherwise default to null. Check that the email address is in the correct format:
    $email = (isset($_POST['email']) ? $_POST['email'] : null);
    
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    
        $errors[] = 'Please enter a valid email address';
    } else {
        if ($email != $this->user->get_user_email($email)){
            $errors[] = 'Email address not found';
        }
    }
  8. Lastly, check if the email address belongs to an existing user. To do this, create a new method in the user Model called get_user_email($email):

    Note

    This will return the email address if it exists, otherwise null will be returned.

    public function get_user_email($email)
    {
        $data = $this->db->select('email from users where email = :email', [':email' => $email]);
        return (isset($data[0]->email) ? $data[0]->email : null);
    }

    In the preceding Controller, we have:

    if ($email != $this->user->get_user_email($email)){

    Note

    This checks that the email address provided in the form does not match with the database, in which case a new error is created.

  9. After the validation check, there are no errors:
    if (count($errors) == 0) {
  10. Save the file; the method so far looks like this:

    Note

    For full code snippet, refer to Lesson 7.php file in the code files folder.

    public function reset()
    {
    …….
    
        $this->view->render('admin/auth/reset', compact('title', 'errors'));
    }

    At this point, among other things, an email needs to be sent.

    Note

    A best practice is to not use PHP's built-in mail( ) function and to use a library such as phpmailer ( https://github.com/PHPMailer/ instead.

  11. Open composer.json and phpmailer in the require list:
    {
        "autoload": {
            "psr-4": {
                "App\" : "app/",
                "System\" : "system/"
            }
        },
        "require": {
            "filp/whoops": "^2.1",
            "phpmailer/phpmailer": "~6.0"
        }
    }
  12. Save the file and type composer update in Terminal. This will pull in phpmailer, making it available to our application.
  13. At the top of the Admin Controller, import phpmailer:
    use PHPMailerPHPMailerPHPMailer;
    use PHPMailerPHPMailerException;
  14. Next, go to the reset method inside the following if statement. This is where we'll resume:
    if (count($errors) == 0) {
    
    }
  15. Now, we need to make a random token. For this, use md5, uniqid, and rand to make a random token.
  16. Then, set up a data and where array. The $data will specify the reset_token to have a value of $token, and the $where will be the email address. Pass them to the update() method of the user Model to update the user.

    This will store the $token against the users record in the database:

    $token = md5(uniqid(rand(),true));
    $data  = ['reset_token' => $token];
    $where = ['email' => $email];
    $this->user->update($data, $where);
  17. Now, we set up the email to be sent by creating a new instance of phpmailer, and then setting who the email will come from. Change this as desired.
  18. Pass the $email address that this is going to be sent to and set the mode to HTML by passing true to isHTML():
    $mail = new PHPMailer(true);
    $mail->setFrom('[email protected]');
    $mail->addAddress($email);
    $mail->isHTML(true);
  19. Set the subject and the email body. We provide two bodies: a HTML one and a plaintext one. The plain text one is used in case the user's email client cannot render HTML.
  20. Create a link that points to admin/change/password_token when using localhost:

    Note

    It's important to remember the URL http://localhost:8000 will only work for your machine.

    $mail->Subject = 'Reset you account';
    $mail->Body    = "<p>To change your password please click <a 
    href='http://localhost:8000/admin/change_password/$token'>this link</a></p>";
    $mail->AltBody = "To change your password please go to this address: http://localhost:8000/admin/change_password/$token";
  21. Now, everything is set up. Send the email:
    $mail->send();
  22. Create a session to inform the user and redirect the admin/reset:
    Session::set('success', "Email sent to ".htmlentities($email));
    Url::redirect('/admin/reset');

    The completed method looks like this:

    Note

    For full code snippet, refer to Lesson 7.php file in the code files folder.

    public function reset()
    {
        if (Session::get('logged_in')) {
            Url::redirect('/admin');
        }
    …….
        $title = 'Reset Account';
    
        $this->view->render('admin/auth/reset', compact('title', 'errors'));
    }
  23. When the user clicks on the link in the email, we need to handle the request. To do this, create another method called change_password that accepts a parameter called $token:

    Note

    This method takes the $token, passes it to a method in the users Model called get_user_reset_token($token), and returns the user object. If the token does not match the database, then null is returned.

    For full code snippet, refer to Lesson 7.php file in the code files folder.

    $user = $this->user->get_user_reset_token($token);
    if ($user == null) {
           $errors[] = 'user not found.';
    }

    The method looks like this:

    Note

    For full code snippet, refer to Lesson 7.php file in the code files folder.

    $title = 'Change Password';
    
        $this->view->render('admin/auth/change_password', compact('title', 'token', 'errors'));
    }

    Note

    The render method passed the $title, $token, and $errors to the view.

  24. Another view is needed. Create a view called change_password.php in app/views/admin/auth:

    Note

    For full code snippet, refer to Lesson 7.php file in the code files folder.

    <?php include(APPDIR.'views/layouts/header.php');?>
    
    ……
        </div>
    
    <?php include(APPDIR.'views/layouts/footer.php');?>

    Note

    The form has a hidden input called $token. Its value is the $token passed from the Controller, and this will be used to verify the request.

    There's also two inputs: a password and confirm password. These are used to collect the desired password.

    When the form is submitted and the form data is collected, a method called to get_user_reset_token($token) is made again to verify that the provided token is valid.

    Also, the passwords must match and be more than three characters in length.

  25. If there is no error, then update the user's record in the database by passing to $this->user->update an array to clear out the reset_token. Hash the password using password_hash(), where the ID matches the user object and the token matches the provided token:

    Note

    For full code snippet, refer to Lesson 7.php file in the code files folder.

    if (isset($_POST['submit'])) {
    
        $token = htmlspecialchars($_POST['token']);
    ……..
        }
    
    }
  26. After the update, log the user in and redirect them to the admin dashboard.

    The full method looks like this:

    Note

    For full code snippet, refer to Lesson 7.php file in the code files folder.

    public function change_password($token)
    {
    …….
    
        $title = 'Change Password';
    
        $this->view->render('admin/auth/change_password', compact('title', 'token', 'errors'));
    }

This concludes the authentication sections. We can now log in, log out, and reset the password, should we forget it.

We have now come to the end of this section. Here, we learned how to build a password reset system and also gained further experience in using third-party tools.

In the next section, we will see how to add CRUD functionality for user management.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset