How Laravel's password confirmation works.

How Laravel's password confirmation works.

Written by Kim Hallberg on Aug 27th, 2020 Views Report Post

Introduction into Laravel.

The password confirmation middleware was introduced in Laravel v6.2.0, and it's built into Illuminate/Auth, and can be found under Illuminate/Auth/Middleware/RequirePassword.php

How it works.

It works by looking at the password_confirmed_at session on an incoming request and based on the result it builds the response using the ResponseFactory to either respond with JSON or redirect to the password.confirm route, on success it redirects the user to the original route or the set $redirectTo variable on fails.

The token will be set to 3 hours by default on success but can be customized using the auth.password_timeout configuration option.

Usage of password confirmation.

Let's make some assumptions, first, let us assume you have users and those users can change some security settings, for instance, they might be able to change their password or disable 2-way authentication.

That's something you might wanna protect, especially if you let your users use the remember me feature, say a Jane has set to be remembered, she closes her browser and walks away, later comes John.

John also has an account on your site, so he enters your site only to be logged in as Jane, now John wants to change some security settings as a joke, but when he hits the endpoint /settings/security/ he'll be promoted to enter Jane's password, which he doesn't know, now Janes security settings have been saved from John joke.

I know this is a naive way of looking at it, but the point still stands, sometimes you will want your users to give you a confirmation that they're the ones accessing their account.

So why don't we create this little naive example? Let's spin up a new local Laravel application with some auth.

laravel new password-confirmation --auth

Note: If you wanna follow along I'm using Laravel Valet and PHP 7.3.

Migrate our secret.

To keep this as simple as we can, let's add a column to our users table.

php artisan make:migration create_user_settings --table users
public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('secret')->nullable();
    });
}

public function down()
{
    Schema::table('users', function (Blueprint $table) {
        $table->dropColumn('secret');
    });
}

And of course, don't forget to migrate that and add secret to the fillable array on User.

New routes & controller.

Now let's add some routes, to keep this simple we will use group inheritance, and a new SecretController.

Route::group(['middleware' => ['auth']], function () {
    Route::post('/secret', 'SecretController@store')->name('secret.store');

    Route::group(['middleware' => ['password.confirm']], function () {
        Route::get('/secret/edit', 'SecretController@edit')->name('secret.edit');
    });
});

Our controller will be simple as well, with some basic validation.

class SecretController extends Controller
{
    public function edit()
    {
        return view('secret.edit');
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'secret' => 'required|string'
        ]);

        auth()->user()->update($request->all());

        return redirect()->route('home')->with('status', 'Secret added!');
    }
}

Adding our views.

All that's left to do is create our view, so add a folder name secret under resources/views and create a edit.blade.php file.

This file will contain our form for updating our secret.

<form action="{{ route('secret.store') }}"  method="POST">
    @csrf

    <div class="form-group is-invalid">
        <label for="secret">Your secret</label>
        <input id="secret" type="text" name="secret" class="form-control @error('secret') is-invalid @enderror" value="{{ old('secret') }}" aria-describedby="secret-help" required>
        <div class="d-flex justify-content-between w-100">
            <small id="secret-help" class="form-text text-muted @error('secret') is-invalid @enderror">We'll never share your secret with anyone else.</small>
            @error('secret')<div class="invalid-feedback w-auto">{{ $message }}</div>@enderror
        </div>
    </div>

    <button type="submit" class="btn btn-primary">Save</button>
</form>

We will also edit our home.blade.php file to display our secret, so below your dashboard card add the following.

<div class="card mb-5">
    <div class="card-body text-center">
        @if (auth()->user()->secret)
            {{ auth()->user()->secret }}
        @else
            You don't have any secret yet, <a href="{{ route('secret.edit') }}" title="Edit secret">add one.</a>
        @endif
    </div>
</div

Now if you travel to secret/edit you'll be prompted to confirm your password before continuing on to edit your secret.

confirm password view

edit secret view


In conclusion.

It's that simple to add password confirmation in Laravel, though there are some limitations, password confirmation cannot be used in post requests, so out-of-the-box you cannot add it to secret.store itself.

I haven't looked, but there's almost certainly a package that gives you that functionality, and if I find one I'll add it here later for your benefit.

Now am I saying the demo I made it production-ready? No, as always when updating fields or models, you should check more than just simple validation requirements, but hopefully, this will give you an idea of where you can use this functionality in your project.

As always, thank you for reading and you can find the full source for this demo on my GitHub (thinkverse/password-confirmation).

Comments (0)