> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Wachtwoordherstel

> Hoe je wachtwoordherstel implementeert in Laravel

## Inleiding

Wachtwoordherstel is een onmisbare authenticatieflow in webapplicaties. Als je een starter kit gebruikt, wordt deze functionaliteit automatisch opgezet, maar bij API-only projecten of projecten met een eigen UI is handmatige implementatie nodig.

**Gevallen waarin handmatige implementatie nodig is:**

* Een API-only backend (de frontend is een SPA of mobiele app)
* Wanneer je de authenticatie-UI zelf bouwt zonder starter kit
* Wanneer je de e-mailnotificatie of het ontwerp van de herstel-URL volledig wilt aanpassen

<Info>
  Heb je je project aangemaakt met een starter kit (`laravel new`), dan is de wachtwoordherstelfunctionaliteit al geïmplementeerd. Deze pagina legt uit hoe je het implementeert zonder starter kit.
</Info>

De volledige flow van wachtwoordherstel ziet er zo uit:

```mermaid theme={null}
flowchart TD
    A["Gebruiker"] --> B["E-mailadres invoeren<br>GET /forgot-password"]
    B --> C["Herstellink versturen<br>POST /forgot-password"]
    C --> D["E-mail ontvangen<br>URL met token"]
    D --> E["Herstelformulier tonen<br>GET /reset-password/{token}"]
    E --> F["Wachtwoord wijzigen<br>POST /reset-password"]
    F --> G["Redirect naar<br>inlogscherm"]
```

## Configuratie

De configuratie van wachtwoordherstel beheer je via de sleutel `passwords` in `config/auth.php`.

```php theme={null}
// config/auth.php

'passwords' => [
    'users' => [
        'driver' => 'database',
        'provider' => 'users',
        'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
        'expire' => 60,
        'throttle' => 60,
    ],
],
```

* `driver` — opslagmethode (`database` of `cache`)
* `expire` — geldigheidsduur van het token (minuten). Standaard 60 minuten
* `throttle` — wachttijd (seconden) voordat opnieuw verzenden is toegestaan

## Drivers

### De database-driver

De standaarddriver. Wachtwoordhersteltokens worden opgeslagen in de databasetabel `password_reset_tokens`. Deze tabel is opgenomen in de standaardmigratie van Laravel (`0001_01_01_000000_create_users_table.php`).

```php theme={null}
'passwords' => [
    'users' => [
        'driver' => 'database',
        'provider' => 'users',
        'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
        'expire' => 60,
        'throttle' => 60,
    ],
],
```

### De cache-driver

<Tip>
  Een nieuwe optie die beschikbaar is vanaf Laravel 11. Omdat er geen databasetabel nodig is, kun je wachtwoordherstel met een eenvoudigere opzet implementeren.
</Tip>

De `cache`-driver slaat tokens op in de cachestore. De migratie voor de tabel `password_reset_tokens` is dan niet meer nodig. Tokens worden opgeslagen met het e-mailadres van de gebruiker als sleutel, dus let erop dat je elders in je app geen e-mailadressen als cachesleutel gebruikt.

```php theme={null}
'passwords' => [
    'users' => [
        'driver' => 'cache',
        'provider' => 'users',
        'store' => 'passwords', // Optioneel: een aparte cachestore
        'expire' => 60,
        'throttle' => 60,
    ],
],
```

Als je bij de sleutel `store` een aparte cachestore opgeeft, voorkom je dat de hersteldata wordt gewist bij `php artisan cache:clear`. De opgegeven waarde moet overeenkomen met een storenaam die in `config/cache.php` is geconfigureerd.

## Het model voorbereiden

Om wachtwoordherstel te gebruiken heeft het model `App\Models\User` twee traits nodig.

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable, CanResetPassword;

    // ...
}
```

* `Notifiable` — nodig om e-mailnotificaties te versturen
* `CanResetPassword` — biedt de methodes die nodig zijn om wachtwoordhersteltokens te genereren en te valideren

<Info>
  Het standaard `User`-model van Laravel bevat deze traits al. Bij een nieuwe installatie hoef je niets toe te voegen.
</Info>

## De routes implementeren

Voor wachtwoordherstel zijn vier routes nodig.

### 1. Formulier om een herstellink aan te vragen

Toont een formulier waarin het e-mailadres wordt ingevoerd.

```php theme={null}
// routes/web.php

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

De bijbehorende Blade-view:

```blade theme={null}
{{-- resources/views/auth/forgot-password.blade.php --}}

<form method="POST" action="/forgot-password">
    @csrf

    <div>
        <label for="email">E-mailadres</label>
        <input id="email" type="email" name="email" value="{{ old('email') }}" required autofocus>
        @error('email')
            <span>{{ $message }}</span>
        @enderror
    </div>

    @if (session('status'))
        <div>{{ session('status') }}</div>
    @endif

    <button type="submit">Herstellink versturen</button>
</form>
```

### 2. De herstellink versturen

Verwerkt de formulierinzending en verstuurt de herstelmail met `Password::sendResetLink()`.

```php theme={null}
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::ResetLinkSent
        ? back()->with(['status' => __($status)])
        : back()->withErrors(['email' => __($status)]);
})->middleware('guest')->name('password.email');
```

`Password::sendResetLink()` geeft een statusstring terug.

| Statusconstante             | Beschrijving                                          |
| --------------------------- | ----------------------------------------------------- |
| `Password::ResetLinkSent`   | De herstellink is verzonden                           |
| `Password::INVALID_USER`    | Geen gebruiker gevonden met het opgegeven e-mailadres |
| `Password::RESET_THROTTLED` | Het verzenden is beperkt (throttling)                 |

### 3. Het wachtwoordherstelformulier

Toont een formulier waarin de gebruiker die op de link in de e-mail heeft geklikt een nieuw wachtwoord invoert.

```php theme={null}
Route::get('/reset-password/{token}', function (string $token) {
    return view('auth.reset-password', ['token' => $token]);
})->middleware('guest')->name('password.reset');
```

De bijbehorende Blade-view:

```blade theme={null}
{{-- resources/views/auth/reset-password.blade.php --}}

<form method="POST" action="/reset-password">
    @csrf

    <input type="hidden" name="token" value="{{ $token }}">

    <div>
        <label for="email">E-mailadres</label>
        <input id="email" type="email" name="email" value="{{ old('email') }}" required autofocus>
        @error('email')
            <span>{{ $message }}</span>
        @enderror
    </div>

    <div>
        <label for="password">Nieuw wachtwoord</label>
        <input id="password" type="password" name="password" required>
        @error('password')
            <span>{{ $message }}</span>
        @enderror
    </div>

    <div>
        <label for="password_confirmation">Wachtwoord bevestigen</label>
        <input id="password_confirmation" type="password" name="password_confirmation" required>
    </div>

    <button type="submit">Wachtwoord resetten</button>
</form>
```

### 4. Het wachtwoordherstel uitvoeren

Verwerkt het formulier en werkt het wachtwoord bij met `Password::reset()`.

```php theme={null}
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::PasswordReset
        ? redirect()->route('login')->with('status', __($status))
        : back()->withErrors(['email' => [__($status)]]);
})->middleware('guest')->name('password.update');
```

De statusconstanten van `Password::reset()`:

| Statusconstante           | Beschrijving                                          |
| ------------------------- | ----------------------------------------------------- |
| `Password::PasswordReset` | Wachtwoordherstel geslaagd                            |
| `Password::INVALID_TOKEN` | Het token is ongeldig of verlopen                     |
| `Password::INVALID_USER`  | Geen gebruiker gevonden met het opgegeven e-mailadres |

## Geldigheidsduur van tokens

Met de optie `expire` in `config/auth.php` stel je de geldigheidsduur van tokens in minuten in. De standaard is 60 minuten.

```php theme={null}
'passwords' => [
    'users' => [
        'driver' => 'database',
        'expire' => 60, // Verloopt na 60 minuten
        'throttle' => 60,
    ],
],
```

Bij gebruik van de `database`-driver blijven verlopen tokens in de database staan. Gebruik het volgende Artisan-commando om ze periodiek op te ruimen.

```shell theme={null}
php artisan auth:clear-resets
```

Het is aan te raden dit te automatiseren met de scheduler.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

Schedule::command('auth:clear-resets')->everyFifteenMinutes();
```

## Aanpassen

### Een eigen notificatie gebruiken

Om de wachtwoordherstelmail aan te passen, override je de methode `sendPasswordResetNotification` in het `User`-model.

```php theme={null}
use App\Notifications\ResetPasswordNotification;

class User extends Authenticatable
{
    use Notifiable, CanResetPassword;

    /**
     * Verstuur de wachtwoordherstelnotificatie
     */
    public function sendPasswordResetNotification($token): void
    {
        $url = 'https://example.com/reset-password?token='.$token;

        $this->notify(new ResetPasswordNotification($url));
    }
}
```

### De URL van de herstellink aanpassen

Met `ResetPassword::createUrlUsing()` in de `boot`-methode van de `AppServiceProvider` kun je de URL van de herstellink wijzigen. Handig als je wilt doorverwijzen naar een frontend op een andere origin, zoals een SPA.

```php theme={null}
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;

public function boot(): void
{
    ResetPassword::createUrlUsing(function (User $user, string $token) {
        return 'https://example.com/reset-password?token='.$token;
    });
}
```

### Trusted hosts instellen

De wachtwoordherstellink wordt gegenereerd op basis van de `Host`-header van het HTTP-verzoek. Om verzoeken van ongeldige hosts te weren, wordt aanbevolen trusted hosts in te stellen in `bootstrap/app.php`.

```php theme={null}
->withMiddleware(function (Middleware $middleware) {
    $middleware->trustHosts(at: ['example.com']);
})
```

<Warning>
  Controleer bij het implementeren van wachtwoordherstel altijd de trusted hosts-configuratie. Bij een onvoldoende configuratie loop je risico op host header injection-aanvallen.
</Warning>

## Samenvatting

| Wat je wilt doen               | Methode                                        |
| ------------------------------ | ---------------------------------------------- |
| Een herstellink versturen      | `Password::sendResetLink(['email' => $email])` |
| Het wachtwoord resetten        | `Password::reset($credentials, $callback)`     |
| Resetten zonder tabel          | Configureer de `cache`-driver                  |
| Verlopen tokens verwijderen    | `php artisan auth:clear-resets`                |
| De e-mailnotificatie aanpassen | Override `sendPasswordResetNotification`       |
| De herstel-URL aanpassen       | `ResetPassword::createUrlUsing()`              |

## Volgende stappen

<Columns cols={2}>
  <Card title="Introductie authenticatie" icon="lock" href="/nl/authentication">
    Leer hoe het volledige authenticatiesysteem van Laravel werkt.
  </Card>

  <Card title="Notificaties" icon="bell" href="/nl/notifications">
    Leer in detail hoe je e-mailnotificaties aanpast.
  </Card>
</Columns>


## Related topics

- [Laravel Fortify en de starter kits](/nl/advanced/fortify.md)
- [Introductie tot authenticatie](/nl/authentication.md)
- [Een SPA bouwen met Inertia.js](/nl/blog/inertia-introduction.md)
