cult3

Password reminders and reset in Laravel 4

Sep 23, 2013

Table of contents:

  1. The remindable interface
  2. How the process works
  3. What are we going to need to create?
  4. Creating the table migration
  5. Creating the reminder form
  6. Sending the request email
  7. Creating the reset form
  8. Updating the password
  9. Conclusion

A couple of weeks ago I looked at how to create registration and authentication functionality in Laravel 4. Registering and authenticating are two of the fundamental features of a social application.

The third crucial component of this user interaction cycle is allowing users to be able to request an email to reset their password. This is one of those features that is so easy to overlook when developing an application, but your users will soon let you know about it if you forget it.

Fortunately Laravel 4 makes setting up this functionality incredibly easy and so this will be our focus for this week’s tutorial.

The remindable interface

If you remember back to Setting up your first Laravel 4 Model, I briefly touched upon the remindable interface. It is this interface that enables Laravel’s reminder functionality to work out of the box.

So before we begin, ensure that your model implements the interface like this:

class User extends Magniloquent implements UserInterface, RemindableInterface
{
}

How the process works

I’m sure you have reset your password on an online service in the past so you’ll be well aware of how this kind of thing works, but just for clarity, I will talk you through how it works in Laravel.

  1. First a user requests a reminder to be sent to their registered email address.
  2. This action generates a secret token which is stored in the database alongside the timestamp of when it was created.
  3. An email is sent to the user with a link to a form for them to reset their password. The secret token which we generated in the previous step is set as a parameter on the URL.
  4. When the user clicks the link and is taken to the password reset form the token is validated against the record in the database.
  5. If the token is valid, the user is allowed to reset their password

What are we going to need to create?

As you can see from the process above, it is relatively simple to create a system to allow users to reset their password, especially seeing as though Laravel does all of the heavy lifting for us.

However, we are still going to have to create a few things in order for the process to work:

Table migration We need a way of being able to store the reminder tokens in the database. Polluting the user table wouldn’t be a good idea, so Laravel can automatically generate the migration for the table for you.

Routes We will need a couple of routes to be able to GET the forms and POST the results to.

Password Controller We are going to need to use a couple of Controller methods to display forms and accept POST requests so it makes sense to keep them all together in the same Controller.

Reset Views And finally, we need two views. The first one is to display the form to request the reminder email, and the second one is to display the form that accepts the new password.

Creating the table migration

As I mentioned above, Laravel ships with much of this functionality all ready for you to implement. One big time saver is that you can automatically create the migration using the artisan command line interface. This is fantastic because it means you don’t have to think about the schema that you’re going to need.

To create the migration, simply run the following in Terminal:

php artisan auth:reminders

This should create the following migration:

use Illuminate\Database\Migrations\Migration;

class CreatePasswordRemindersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create("password_reminders", function ($t) {
            $t->string("email");
            $t->string("token");
            $t->timestamp("created_at");
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop("password_reminders");
    }
}

Finally, run the migration:

php artisan migrate

Creating the reminder form

So the first thing we need to do is to create a form that will allow a user to enter her email address to request a reset link.

First, create a new route in your routes.php file:

Route::get("password/reset", [
    "uses" => "PasswordController@remind",
    "as" => "password.remind",
]);

Here I’m simply setting a GET route that is associated with a method on the Controller.

Next, create a new Controller file called PasswordController.php and copy the following code:

class PasswordController extends BaseController
{
    public function remind()
    {
        return View::make("password.remind");
    }
}

As you can see from the method above, all we have to do is to return a View file.

So under your views directory, create a new folder called password and a new view filed called remind.blade.php and copy the following code:

@if (Session::has('error'))
{{ trans(Session::get('reason')) }}
@elseif (Session::has('success'))
An email with the password reset has been sent.
@endif

{{ Form::open(array('route' => 'password.request')) }}

<p>{{ Form::label('email', 'Email') }}
{{ Form::text('email') }}</p>

<p>{{ Form::submit('Submit') }}</p>

{{ Form::close() }}

If you remember back to my post on Laravel 4 forms the code above should look familiar. Basically the code above will generate a form with an email input field and a submit button. When the form is submitted, it with be POSTed to the password.request route.

The if...else statement above the form will display any errors or a confirmation if the form was been submitted correctly.

Sending the request email

Next we need to set up another route to POST the form to and a method on the Controller to send the email.

Back in your routes.php file, copy the following new route:

Route::post("password/reset", [
    "uses" => "PasswordController@request",
    "as" => "password.request",
]);

Now, in your PasswordController.php file, add the following new method:

public function request()
{
    $credentials = array('email' => Input::get('email'), 'password' => Input::get('password'));

    return Password::remind($credentials);
}

This super simple method uses Laravel’s input reminder functionality to add the email to the reminder table we created earlier and send the email. You might get an exception saying that you haven’t set up your sender address yet. To fix this, go to app/config/mail.php and fill in the relevant details.

Of course to actually send the email you will need to set up an SMTP server. Personally I like SendGrid. In a future tutorial I will do a much deeper dive on setting up email related stuff in your Laravel application.

If you want to see the template for the default email, it is stored under app/views/auth/reminder.blade.php. You are of course free to change this, just ensure you leave the URL tag intact.

Creating the reset form

Next we will create the form that the user will land on after clicking the reset link in the email.

First create a new route:

Route::get("password/reset/{token}", [
    "uses" => "PasswordController@reset",
    "as" => "password.reset",
]);

This route requires that the secret token is set in the URL. This will be set automatically in the reminder email.

Next add the following method to your Password Controller file to request the correct View:

public function reset($token)
{
    return View::make('password.reset')->with('token', $token);
}

Notice how this method will accept the token from the URL and assign it the View.

And finally, create another View under app/views/password called reset.blade.php and copy the following code:

@if (Session::has('error'))
{{ trans(Session::get('reason')) }}
@endif

{{ Form::open(array('route' => array('password.update', $token))) }}

<p>{{ Form::label('email', 'Email') }}
{{ Form::text('email') }}</p>

<p>{{ Form::label('password', 'Password') }}
{{ Form::text('password') }}</p>

<p>{{ Form::label('password_confirmation', 'Password confirm') }}
{{ Form::text('password_confirmation') }}</p>

{{ Form::hidden('token', $token) }}

<p>{{ Form::submit('Submit') }}</p>

{{ Form::close() }}

Again this is a relatively simple form. First we display any errors at the top. Then we create fields so the user can enter their email address and choose a new password.

Notice how I’ve also set the token as a hidden field and I’ve also set it as a parameter in the action route. The hidden token is used to validate against the database and the URL parameter is used as a requirement of the route.

Updating the password

And finally we can create the route and method to actually update the User’s password.

Firstly create the POST route:

Route::post("password/reset/{token}", [
    "uses" => "PasswordController@update",
    "as" => "password.update",
]);

Next create the method on the Password Controller:

public function update()
{
    $credentials = array('email' => Input::get('email'));

    return Password::reset($credentials, function($user, $password)
    {
        $user->password = Hash::make($password);

        $user->save();

        return Redirect::to('login')->with('flash', 'Your password has been reset');
    });
}

Again this uses some Laravel trickery to do the heavy lifting. Laravel will attempt to reset the password. If it is successful, a User instance and the password are sent to the closure so you can run the update.

Next I’m simply redirecting to the login page with a flash message.

The reset method on the Password class will automatically deal with the heavily lifting of validating the request. It will check to ensure that a valid token has been sent and it will check the credentials and that the passwords match for you.

If there is an error with what the user entered, Laravel will automatically redirect back to the form and set an error message with a reason in the Session.

Conclusion

And there you have it, very simply password reminder and reset functionality almost straight out of the box from Laravel. One of the beautiful things about Laravel 4 is it automatically deals with common bits of functionality like this for you, so you don’t have to reinvent the wheel on every project.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.