- 5 minutes read

Password confirmation for sensitive actions on Laravel

I recently wrote an article about how Laravel easily allows controlling browser sessions. That article was motivated by a personal bad experience of having my cellphone stolen. To prevent other people in possession of the device from having access and being able to perform actions.

But it took some time for me to access my accounts from another device and disconnect the sessions. Thinking about how to prevent others from performing sensitive actions, I remembered that many services I use requests password from time to time for specific actions, such as:

Not surprisingly, Laravel has a feature to handle that in a pretty easy way:

Protecting Routes

First, you need to know how to protect the routes leading the user to perform sensitive actions.

Add the built-in middleware \Illuminate\Auth\Middleware\RequirePassword::class, which is defined in the app/Http/Kernel.php file, having password.confirm as the alias.

Let's say you want to prevent the user from disconnecting other devices previously logged in.

app/routes/web.php

Route::get('/disconnect-other-devices', function () {
    // ...
})->middleware(['auth', 'password.confirm']);

As you can see, the password.confirm middleware is set. It checks if the session item auth.password_confirmed_at has expired based on the auth.password_timeout config value. By default, the value represents three hours.

Laravel session lifetime represents two hours by default. Considering you keep those values, the password.confirm middleware will only act upon user sessions that were set to be remembered. Of course, you can increase the session lifetime, and the password confirmation would work for all session cases.

By default, the middleware redirects the user to the named route password.confirm. As I explain in the following section, you can change the route name.

If your request expects a JSON response, it shows an error message instead of redirecting. This is how the middleware handles it:

\Illuminate\Auth\Middleware\RequirePassword::class

//...
if ($request->expectsJson()) {
     return $this->responseFactory->json([
         'message' => 'Password confirmation required.',
     ], 423);
 }

return $this->responseFactory->redirectGuest(
    $this->urlGenerator->route($redirectToRoute ?: 'password.confirm')
);
//...

The Password Confirmation Form

If the request does not expect a JSON response, you may display a form allowing the password input. The route should be named password.confirm by default.

app/routes/web.php

Route::get('/confirm-password', function () {
    return view('auth.confirm-password');
})->middleware('auth')->name('password.confirm');

You may change it by using passing the route name when setting the middleware:

app/routes/web.php

Route::get('/disconnect-other-devices', function () {
    // ...
})->middleware(['auth', 'password.confirm:my-custom-named-route']);

Route::get('/confirm-password', function () {
    return view('auth.confirm-password');
})->middleware('auth')->name('my-custom-named-route');

Confirming The Password

To confirm it, you need to validate and call the passwordConfirmed method as below:

if (! Hash::check($request->password, $request->user()->password)) {
    return back()->withErrors([
        'password' => ['The provided password does not match our records.']
    ]);
}

$request->session()->passwordConfirmed();

return redirect()->intended();

If you are performing an Ajax request:

if (! Hash::check($request->password, $request->user()->password)) {
    return response()->json([
        'message' => 'The provided password does not match our records',
    ], 422);
}

$request->session()->passwordConfirmed();

return response()->noContent();

In Closing

In this post, you've learned how to protect sensitive actions in your Laravel applications.

If you have any comments, you can share them in the discussion on Twitter.

Do you like my content?

Subscribe to my newsletter to be notified on future articles.