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:
reset
in the Admin
Controller.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')); }
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');?>
Admin
Controller.isset
and pass in the submit button name:if (isset($_POST['submit'])) {
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'; } }
get_user_email($email)
: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)){
if (count($errors) == 0) {
public function reset() { ……. $this->view->render('admin/auth/reset', compact('title', 'errors')); }
At this point, among other things, an email needs to be sent.
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.
composer.json
and phpmailer
in the require list:{ "autoload": { "psr-4": { "App\" : "app/", "System\" : "system/" } }, "require": { "filp/whoops": "^2.1", "phpmailer/phpmailer": "~6.0" } }
composer update
in Terminal. This will pull in phpmailer,
making it available to our application.Admin
Controller, import phpmailer
:use PHPMailerPHPMailerPHPMailer; use PHPMailerPHPMailerException;
reset
method inside the following if
statement. This is where we'll resume:if (count($errors) == 0) { }
md5,
uniqid,
and rand
to make a random token.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);
phpmailer
, and then setting who the email will come from. Change this as desired.$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);
admin/change/password_token
when using localhost:
$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";
$mail->send();
Session::set('success', "Email sent to ".htmlentities($email)); Url::redirect('/admin/reset');
The completed method looks like this:
public function reset() { if (Session::get('logged_in')) { Url::redirect('/admin'); } ……. $title = 'Reset Account'; $this->view->render('admin/auth/reset', compact('title', 'errors')); }
change_password
that accepts a parameter called $token
:$user = $this->user->get_user_reset_token($token); if ($user == null) { $errors[] = 'user not found.'; }
The method looks like this:
$title = 'Change Password'; $this->view->render('admin/auth/change_password', compact('title', 'token', 'errors')); }
change_password.php
in app/views/admin/auth:
<?php include(APPDIR.'views/layouts/header.php');?> …… </div> <?php include(APPDIR.'views/layouts/footer.php');?>
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.
$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:if (isset($_POST['submit'])) { $token = htmlspecialchars($_POST['token']); …….. } }
The full method looks like this:
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.