How Laravel's password confirmation works.
Tags: laravel, php, tutorial, webdev
Published: 2020-08-27 14:26:41

## 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.

```bash
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.

```bash
php artisan make:migration create_user_settings --table users
```

```php
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 &amp; controller.

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

```php
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.

```php
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.

```php
<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.

```php
<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](https://cdn.devdojo.com/images/august2020/confirm-password.png)

![edit secret view](https://cdn.devdojo.com/images/august2020/edit-secret.png)

---

## 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]).

[Laravel v6.2.0]: https://blog.laravel.com/password-confirmation
[Illuminate/Auth/Middleware/RequirePassword.php]: https://github.com/illuminate/auth/blob/HEAD/Middleware/RequirePassword.php
[thinkverse/password-confirmation]: https://github.com/thinkverse/password-confirmation