Laravel Password Reset via API using JWT for Authentication

Out of the box, Laravel provides a simple, tried and tested password reset method and some view templates to get you going. All you have to do is add styles.

It works great, but what if you’re using Laravel to build an API which uses JSON Web Tokens for authentication? And what if your frontend Javascript application resides on a different domain? How can we adapt the default reset methods to work on an API basis? Read on to find out…

N.B. This example is based on Laravel 5.6. We’re also assuming you’ve already successfully implemented auth via JWT.

Password reset requests

The good news is that the core of the reset flow will still work for us, so, to begin, add the following traits to your authentication controller:

 

Illuminate\Foundation\Auth\SendsPasswordResetEmails;

Illuminate\Foundation\Auth\ResetsPasswords;

 

Next, create a route for the forgotten password request in your api routes, e.g.

 

Make sure it can be accessed by unauthenticated users. Your controller method for the route should look like this:

So far, so simple. However, that sendResetLinkEmail method isn’t set up to provide an API response. It provides its responses using the sendResetLinkResponse and sendResetLinkFailedResponse methods – which we’ll need to override, as they currently return views instead of API data.

Here’s an example of how they could look (bear in mind we’re using the Dingo API library to provide the responses here – your responses are likely to look quite different).

For now, this should be enough to get you a password email reset link sent, assuming you’ve got a mailserver configured. That brings us onto part two of the operation – verifying the user and resetting the password.

 

Resetting the password

Now we need a route that actually resets the password – again, making sure it can be accessed by unauthenticated users. This is an example route:

Our reset controller method is pretty simple – it just calls the reset method from the ResetsPasswords trait as follows. The request object must include the token, the user’s email and a confirmed new password.

The reset method in turn verifies the request as legitimate and, if it is, calls a resetPassword method. By default, this sets the new password, saves the user, sets a different token for the system to remember the user by, and logs them in. However, we don’t need to remember token or the login, because we’re using JWT – so let’s override the resetPassword method as follows:

Finally, we need to override the response methods again to provide an API-friendly response (again, your implementation may vary):

We can now reset our password. There’s one last thing to do – modify the email notification to account for the fact that our frontend is running on a different domain from our API.

Modifying the email notification

To begin, run php artisan make:notification MailResetPasswordNotification. This will create a new notification under app/Notifications.

Next, modify the created notification so that it extends Illuminate\Auth\Notifications\ResetPassword. We need to override that notification’s “toMail” method, which currently looks like this:

The key line is the action method, which constructs the password reset URL. You’ll need to change the second parameter to the URL you want to use for your frontend new password form, which might be something like “https://app.api-consumer.com/password-reset?token=<token>”. Needless to say, make sure the token variable is included in the URL somehow!

There’s one last thing to do. By default, the User model extends the Authenticable class, which includes a sendPasswordResetNotification method. We need to override this method to use our new notification, which we do by adding the following to our User:

And that’s it. All you have to do now is built the front end, which we’ll leave entirely up to you. Also, make sure you set the auth.passwords.users.expires config variable to a sensible value. We’d recommend 60 minutes to ensure a reasonable degree of security. Thanks for reading!

Ryan is a backend developer working primarily on Laravel and Magento projects. He likes more or less the same things that everyone else does and speaks about himself in the third person in real life as well as in blog biographies.