Introduction
Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this by hand for every application you create, Laravel provides convenient services for sending password reset links and secure resetting passwords.
Note:
Want to get started fast? Install a Laravel application starter kit in a fresh Laravel application. Laravel's starter kits will take care of scaffolding your entire authentication system, including resetting forgotten passwords.
Model Preparation
Before using the password reset features of Laravel, your
application's App\Models\User
model must
use the Illuminate\Notifications\Notifiable
trait. Typically, this trait is already included on the
default App\Models\User
model that is
created with new Laravel applications.
Next, verify that your App\Models\User
model
implements the
Illuminate\Contracts\Auth\CanResetPassword
contract. The App\Models\User
model
included with the framework already implements this
interface, and uses the
Illuminate\Auth\Passwords\CanResetPassword
trait to include the methods needed to implement the
interface.
Database Preparation
A table must be created to store your application's password reset tokens. The migration for this table is included in the default Laravel application, so you only need to migrate your database to create this table:
php artisan migrate
Configuring Trusted Hosts
By default, Laravel will respond to all requests it
receives regardless of the content of the HTTP request's
Host
header. In addition, the
Host
header's value will be used when
generating absolute URLs to your application during a
web request.
Typically, you should configure your web server, such as
Nginx or Apache, to only send requests to your
application that match a given host name. However, if
you do not have the ability to customize your web server
directly and need to instruct Laravel to only respond to
certain host names, you may do so by enabling the
App\Http\Middleware\TrustHosts
middleware
for your application. This is particularly important
when your application offers password reset
functionality.
To learn more about this middleware, please consult the
TrustHosts
middleware documentation.
Routing
To properly implement support for allowing users to reset their passwords, we will need to define several routes. First, we will need a pair of routes to handle allowing the user to request a password reset link via their email address. Second, we will need a pair of routes to handle actually resetting the password once the user visits the password reset link that is emailed to them and completes the password reset form.
Requesting the Password Reset Link
The Password Reset Link Request Form
First, we will define the routes that are needed to request password reset links. To get started, we will define a route that returns a view with the password reset link request form:
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
})->middleware('guest')->name('password.request');
The view that is returned by this route should have a
form containing an email
field, which will
allow the user to request a password reset link for a
given email address.
Handling the Form Submission
Next, we will define a route that handles the form submission request from the "forgot password" view. This route will be responsible for validating the email address and sending the password reset request to the corresponding user:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
Route::post('/forgot-password', function (Request $request) {
$request->validate(['email' => 'required|email']);
$status = Password::sendResetLink(
$request->only('email')
);
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
})->middleware('guest')->name('password.email');
Before moving on, let's examine this route in more
detail. First, the request's email
attribute is validated. Next, we will use Laravel's
built-in "password broker" (via the
Password
facade) to send a password reset
link to the user. The password broker will take care of
retrieving the user by the given field (in this case,
the email address) and sending the user a password reset
link via Laravel's built-in notification
system.
The sendResetLink
method returns a
"status" slug. This status may be translated
using Laravel's localization helpers in
order to display a user-friendly message to the user
regarding the status of their request. The translation
of the password reset status is determined by your
application's lang/{lang}/passwords.php
language file. An entry for each possible value of the
status slug is located within the passwords
language file.
Note:
By default, the Laravel application skeleton does not include thelang
directory. If you would like to customize Laravel's language files, you may publish them via thelang:publish
Artisan command.
You may be wondering how Laravel knows how to retrieve
the user record from your application's database when
calling the Password
facade's
sendResetLink
method. The Laravel password
broker utilizes your authentication system's "user
providers" to retrieve database records. The user
provider used by the password broker is configured
within the passwords
configuration array of
your config/auth.php
configuration file. To
learn more about writing custom user providers, consult
the authentication
documentation.
Note:
When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out the Laravel application starter kits.
Resetting the Password
The Password Reset Form
Next, we will define the routes necessary to actually
reset the password once the user clicks on the password
reset link that has been emailed to them and provides a
new password. First, let's define the route that will
display the reset password form that is displayed when
the user clicks the reset password link. This route will
receive a token
parameter that we will use
later to verify the password reset request:
Route::get('/reset-password/{token}', function (string $token) {
return view('auth.reset-password', ['token' => $token]);
})->middleware('guest')->name('password.reset');
The view that is returned by this route should display a
form containing an email
field, a
password
field, a
password_confirmation
field, and a hidden
token
field, which should contain the value
of the secret $token
received by our
route.
Handling the Form Submission
Of course, we need to define a route to actually handle the password reset form submission. This route will be responsible for validating the incoming request and updating the user's password in the database:
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
Route::post('/reset-password', function (Request $request) {
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|min:8|confirmed',
]);
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function (User $user, string $password) {
$user->forceFill([
'password' => Hash::make($password)
])->setRememberToken(Str::random(60));
$user->save();
event(new PasswordReset($user));
}
);
return $status === Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withErrors(['email' => [__($status)]]);
})->middleware('guest')->name('password.update');
Before moving on, let's examine this route in more
detail. First, the request's token
,
email
, and password
attributes
are validated. Next, we will use Laravel's built-in
"password broker" (via the
Password
facade) to validate the password
reset request credentials.
If the token, email address, and password given to the
password broker are valid, the closure passed to the
reset
method will be invoked. Within this
closure, which receives the user instance and the
plain-text password provided to the password reset form,
we may update the user's password in the database.
The reset
method returns a
"status" slug. This status may be translated
using Laravel's localization helpers in
order to display a user-friendly message to the user
regarding the status of their request. The translation
of the password reset status is determined by your
application's lang/{lang}/passwords.php
language file. An entry for each possible value of the
status slug is located within the passwords
language file. If your application does not contain a
lang
directory, you may create it using the
lang:publish
Artisan command.
Before moving on, you may be wondering how Laravel knows
how to retrieve the user record from your application's
database when calling the Password
facade's
reset
method. The Laravel password broker
utilizes your authentication system's "user
providers" to retrieve database records. The user
provider used by the password broker is configured
within the passwords
configuration array of
your config/auth.php
configuration file. To
learn more about writing custom user providers, consult
the authentication
documentation.
Deleting Expired Tokens
Password reset tokens that have expired will still be
present within your database. However, you may easily
delete these records using the
auth:clear-resets
Artisan command:
php artisan auth:clear-resets
If you would like to automate this process, consider adding the command to your application's scheduler:
$schedule->command('auth:clear-resets')->everyFifteenMinutes();
Customization
Reset Link Customization
You may customize the password reset link URL using the
createUrlUsing
method provided by the
ResetPassword
notification class. This
method accepts a closure which receives the user
instance that is receiving the notification as well as
the password reset link token. Typically, you should
call this method from your
App\Providers\AuthServiceProvider
service
provider's boot
method:
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
ResetPassword::createUrlUsing(function (User $user, string $token) {
return 'https://example.com/reset-password?token='.$token;
});
}
Reset Email Customization
You may easily modify the notification class used to send
the password reset link to the user. To get started,
override the sendPasswordResetNotification
method on your App\Models\User
model.
Within this method, you may send the notification using
any notification class
of your own creation. The password reset
$token
is the first argument received by
the method. You may use this $token
to
build the password reset URL of your choice and send
your notification to the user:
use App\Notifications\ResetPasswordNotification;
/**
* Send a password reset notification to the user.
*
* @param string $token
*/
public function sendPasswordResetNotification($token): void
{
$url = 'https://example.com/reset-password?token='.$token;
$this->notify(new ResetPasswordNotification($url));
}