Confirm e-mail addresses in Laravel 5.6

When using Laravel ≥ 5.7 you should use the new built-in MustVerifyEmail contract. Check out this free Laracasts video for details about the usage!

When it comes to user registration and account creation in your web app, you usually don’t only want to verify the pure correctness of an e-mail address according to a regular expression. You also may want to verify the existence of a mailbox behind the address your user provided. To do so it’s a common practice to send a small confirmation e-mail to the provided address and let the user click a link that verifies that the e-mail account exists.

I recently saw a small tutorial that explains how you do so using a pre-built Laravel package. That’s fine but while I was learning Laravel, I saw that usually it’s way more easier to quickly integrate a specific function yourself instead of relying on pre-built dependencies. So for sure it’s easy to do just some composer require stuff in your project, but why not taking a few more minutes writing it yourself? The advantages are pretty obvious: You don’t need to rely on the code quality of an external code maintainer. Your code is immune to sudden changes in the code that are breaking its backward compatibility and it’s perfectly fitted to your needs. And on top you’ll learn something new.

So. Why don’t we just have a look at how to integrate the e-mail address confirmation feature on our own. Let’s consider the requirements first. So when a user registers and provides his e-mail address we want to send him a e-mail that includes a link to a specific route. When he hits that route we want to flag his e-mail as confirmed in the database. Until the user has not confirmed his e-mail address he shall not be able to do whatever the main functionality of your web app is.

The implementation

So let’s start with the implementation of the feature itself. I’m assuming that you are already kind of familiar with the basic functionality of Laravel including the php artisan make:auth authentication scaffolding. If you are not, I recommend to head to Laravel’s documentation as it’s pretty well documented there.

If you are ready to continue we can start with the database-part of the implementation.

Modify the “users” table

First of all we need to add some additional fields in our users table. We (obviously) need a field for a secret confirmation token. But you should also consider to log the date and time of the confirmation. In a few cases it could be also required to log the IP address that was used during the confirmation (e.g. to proof that a specific user agreed to your terms of usage at a specific time). If you are doing so, make sure that your process complies to the data privacy laws that are valid in your country ( → e.g. watch out for the GDPR).

Set up a new migration (or modify the existing create_users_table migration as we are doing here in this tutorial).

Code example 1: The (modified) database migration for the “users” table.

As you can see I added two more fields to the existing default users table migration. The first field (confirmation_token) will be responsible for storing the confirmation token that is bound to the specific user. Its length is limited to 32 characters as this is the exact length of a string that is generated using the md5() hashing function which we will user later on to generate some kind of random token value. I have to clearify that MD5 needs to be considered as unsecure when using it as a real cryptographic hashing function. But in our use case we will just hash the current time in microseconds to generate a random hexadecimal string with a fixed length. So please don’t use it to hash your user’s passwords. Instead feel free to use Laravel’s built-in Hash::make() function which is using bcrypt per default which is considered safe.

The second field holds the timestamp of a successful confirmation. As you may noticed all fields are nullable and set to NULL per default. There is no token set as default value for the confirmation_token field, because we want to set this dynamically upon user registration and we can’t provide an inline function as default value. Laravel’s Query Builder can’t translate it into a deterministic SQL query.

Modify the “User” model

Next part of our code that needs to be modified in order to implement our e-mail address confirmation functionality is the User model. Think a second about functions you may want to use in your future code. First of all you want to check whether the user confirmed his e-mail address or not (e.g. to disable some of your application’s functionality). So it sounds like we need to implement a hasConfirmed() method. Additionally we need to provide functions to apply and also remove the confirmed status at our user’s database representation. And finally we should provide a (static) function for generating a random confirmation token.

So here we go — these are the new functions we need to implement in our User model:

Code example 2: New functions in the “User” model.

The function microtime() provides the current time in microseconds. Passing true as argument instructs the function to return the current microtime as float. It’s made static as it does not rely on a specific instance of the class.

hasConfirmed() just checks if the confirmation_token field in the database is NULL for the specific user. If it is, the user has already confirmed his e-mail address.

Next on we are providing a confirm() function to attempt a confirmation based on the user-provided token. It basically checks if the provided token equals the database-stored token. If yes the confirmation_token field is nulled and the current timestamp is applied on the confirmed_at field.

Last but not least the unconfirm() function that reverses the confirm() method by setting a freshly generated confirmation token and nulling the confirmed_at field at the database.

Before we continue with the routes and controllers we need to modify a few more things in our User model. But this time functions are not object to out changes. Eloquent (Laravel’s ORM — the magic behind the models and (for example) the reason why you can access relations by using a property) provides many protected variables that can be easily overwritten by each model you create. There are a many useful ones and one of them is the $dates variable. It’s an array that defines which attribute in the database should Eloquent consider as date(time) variable. Eloquent casts those fields to a Carbon instance for you which enables you to use the full function stack of this awesome open source library. If you are not familiar with Carbon check out it’s documentation.

Code example 3: Modified variables in the “User” model.

Another variable we modify is the $fillable array that defines which attributes of the model are mass-assignable. If we are not adding the confirmation_token to this array we won’t be able to quickly add it to the register method at the RegisterController we’ll look at in the next step. To learn more about mass assignment and Eloquent’s magic variables head to the specific chapter in Laravel’s documentation.

Add a new method to the RegisterController

We could either put our confirmation method in its own controller or we could just put it right into the scaffolded RegisterController. In this tutorial I choose the last option but as always you are free to decide to choose another way. Thanks to our model functions we added in the last step there is not much logic in this confirmation method.

Code example 4: The confirmation logic in the registration controller.

As you can see, we basically just call the confirm() function and redirect the user back to the login page with the one or other message. To enable the user to hit the confirmation endpoint in our application we also need to add it to our routes file.

Adding a new route

Laravel’s Route Model Binding will take care of the assignment of the $user variable in our controller method. If there is no user with the provided id, Laravel will throw an automated HTTP 404 (Not found) response.

Code example 5: The confirmation endpoint in the routes file.

Modify the registration logic

Now we need to head back to the RegisterController. Laravel’s auth scaffolding is completely modifiable and to implement the e-mail address confirmation functionality we’ll need to edit a function of if. Take a closer look at the create() method which is responsible for bringing the user data into the database. To add a value to the confirmation_token field we just need to modify one single line.

Code example 6: Modifying the create() function at the RegisterController.

It’s that easy. We just use the static User::generateToken() function we created in the User in the second step.

Sending the confirmation e-mail

We nearly implmeneted everything we need for our self-implemented e-mail address confirmation functionality. But we still need to get the confirmation link into the user’s e-mail inbox. To do so, Laravel offers several possibilities we could utilize. For example you could write the function call that sends the mail directly into the controller. I’m a huge fan of thinking big when it comes to web applications. That means I would not recommend you to do so unless you already know that you won’t add much more logic later on.

Putting too much logic into the controller can lower the maintainability of your code. If you come back to your code later on you will need to search for your confirmation e-mail logic. Or if things are running well you may want to add many more features upon registration. For example processing statistics, log the registration or send other e-mails to the user. You won’t want to add all of this functionality right in the controller.

To avoid those problems I’ll take advantage of a concept that is often forgotten by beginners: EventListeners. You can easily build up many of these and years later you can come back and you will find all your logic what happens when a user registers at a single place.

But first of all let’s have a look at the underlying RegistersUsers trait to understand what happens “under the auth scaffolding hood”.

This is what happens when the POST request hits the register endpoint when using Laravel’s auth scaffolding.

In the beginning the request gets validated. So far so easy. But then the event() helper method is called. It’s responsible for raising a new Registered event in the background that we can utilize. The event accepts an instance of the User model as argument and passes it to every event listener that wants to get notified when a new user registers. We just need to add a new EventListener and wait for the Registered event to be raised. Run the following command to scaffold a new event listener:

php artisan make:listener UserRegistered

You’ll find your newly created event listener in the app/Listeners directory. In here we want to grab the passed instance of the User model and send an e-mail with the confirmation link to the user’s e-mail address. Each Listener comes with a constructor and a handle() method out of the box. In here we can do what ever we want when the event is raised and its payload is passed to our newly created listener.

Code example 7: The event listener that handles the confirmation e-mail.

The mailing functionality in Laravel is pretty much straightforward. You need to create a so called Mailable which is basically a type of class that represents your generic message. And then you can just fire it off to your user. We’ll take care of the Mailable in a second. For the moment let’s stick to the newly built event listener.

You really need just one single line of code in your listener to get the “hardest” part of your manual implementation up running. We use the Mail facade and pass the user’s e-mail address he provided along with the corresponding confirmation token.

Luckily the $event variable equals to an instance of your User model. Remember the method in the RegistersUser trait: The event itself accepts the instantiated representation of the newly registered user. That’s why you can access the user using the $event variable right now. Pretty awesome, isn’t it?

The Mailable

There’s just one more thing until we’re finished with our implementation. In the code example above I passed a new instance of the ConfirmUserMail class to the Mail facade. This so-called Mailable doesn’t exist, yet. So let’s create it.

php artisan make:mail ConfirmUserMail

You’ll find the newly created Mailable in your app/Mail directory.

Code example 8: The mailable that creates represents the e-mail message with the confirmation token

Again we just need to add some pretty basic code. The constructor should take the User object as we passed it to our mailable in code example 7 in line 18.

When passing a new instance of a mailable to the Mail facade, the build() method is triggered right after the constructor was called.

Here you are able to return a View that should be compiled with given variables to generate the final e-mail that will be sent to your user.

In our case we just can access the user’s ID and his confirmation token inside the view to build the confirmation URL out of this according to our confirmation route we defined before. In this example the mailable would load the view that is located in resources/views/emails/confirmation.blade.php. Just put the link to the confirmation route in here along with some nice warm words and your user will hit the confirmation endpoint and confirms his e-mail address.


Well that’s it. That’s all you need to implement a basic kind of e-mail confirmation in your Laravel 5.6 application. For sure it may be some more work than just including any kind of package someone else built for you. But in the end you built a feature that is perfectly fitted to your application and can be easily modified in the feature. Furthermore you should have learned about the following Laravel features:

  • Some insights behind Laravel’s auth scaffolding system
  • $fillable and $dates variables in Eloquent models
  • EventListeners and Mailables

Hope you are now motivated to try to implement some features yourself!

DevOps Engineer — I’m building and breaking stuff that is related to web development.