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

# Reset della password

> Come implementare il reset della password in Laravel

## Introduzione

Il reset della password è un flusso di autenticazione fondamentale nelle applicazioni web. Con uno starter kit la funzionalità viene costruita automaticamente, ma nei progetti solo-API o con UI personalizzata serve l'implementazione manuale.

**Casi in cui serve l'implementazione manuale:**

* Backend solo-API (con frontend SPA o app mobile)
* Se costruisci autonomamente l'UI di autenticazione senza starter kit
* Se vuoi personalizzare completamente la notifica email o il design dell'URL di reset

<Info>
  Se hai creato il progetto con uno starter kit (`laravel new`) il reset password è già implementato. Questa pagina descrive come implementarlo senza starter kit.
</Info>

Il flusso completo del reset password:

```mermaid theme={null}
flowchart TD
    A["Utente"] --> B["Inserisce l'email<br>GET /forgot-password"]
    B --> C["Invia il link di reset<br>POST /forgot-password"]
    C --> D["Riceve l'email<br>URL con token"]
    D --> E["Mostra il form di reset<br>GET /reset-password/{token}"]
    E --> F["Cambia password<br>POST /reset-password"]
    F --> G["Redirect alla<br>schermata di login"]
```

## Configurazione

Configura il reset password nella chiave `passwords` di `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` — modalità di salvataggio dati (`database` o `cache`)
* `expire` — validità del token in minuti. Default 60 minuti
* `throttle` — tempo di attesa (in secondi) prima di consentire un nuovo invio

## Driver

### Driver database

È il driver predefinito. Salva il token di reset nella tabella `password_reset_tokens` del database. Questa tabella è inclusa nelle migration predefinite di 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,
    ],
],
```

### Driver cache

<Tip>
  Nuova opzione disponibile da Laravel 11. Senza tabella nel database implementi il reset con una configurazione minimale.
</Tip>

Il driver `cache` salva il token nel cache store. Non serve la migration di `password_reset_tokens`. Il token è indicizzato usando l'email come chiave, quindi assicurati di non usare la stessa email come chiave di cache altrove nell'app.

```php theme={null}
'passwords' => [
    'users' => [
        'driver' => 'cache',
        'provider' => 'users',
        'store' => 'passwords', // opzionale: store di cache dedicato
        'expire' => 60,
        'throttle' => 60,
    ],
],
```

Indicando un cache store dedicato nella chiave `store` eviti che `php artisan cache:clear` elimini i dati di reset. Il valore deve corrispondere al nome dello store configurato in `config/cache.php`.

## Preparazione del modello

Per usare il reset password, il modello `App\Models\User` deve avere due trait.

```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` — necessario per inviare notifiche email
* `CanResetPassword` — fornisce i metodi per generare e verificare i token di reset

<Info>
  Il modello `User` predefinito di Laravel include già questi trait. Nelle nuove installazioni non serve aggiungerli.
</Info>

## Implementazione delle route

Per il reset password servono 4 route.

### 1. Form di richiesta del link di reset

Mostra un form per inserire l'email.

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

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

Vista Blade corrispondente:

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

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

    <div>
        <label for="email">Email</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">Invia link di reset</button>
</form>
```

### 2. Invio del link di reset

Riceve il form e invia l'email con `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()` restituisce una stringa di stato.

| Costante di stato           | Descrizione                     |
| --------------------------- | ------------------------------- |
| `Password::ResetLinkSent`   | Link di reset inviato           |
| `Password::INVALID_USER`    | Nessun utente con quell'email   |
| `Password::RESET_THROTTLED` | L'invio è limitato (throttling) |

### 3. Form di reset password

Mostra un form per inserire la nuova password quando l'utente clicca il link nell'email.

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

Vista Blade corrispondente:

```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">Email</label>
        <input id="email" type="email" name="email" value="{{ old('email') }}" required autofocus>
        @error('email')
            <span>{{ $message }}</span>
        @enderror
    </div>

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

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

    <button type="submit">Reset password</button>
</form>
```

### 4. Elaborazione del reset

Riceve il form e aggiorna la password con `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');
```

Costanti di stato di `Password::reset()`:

| Costante di stato         | Descrizione                   |
| ------------------------- | ----------------------------- |
| `Password::PasswordReset` | Reset password riuscito       |
| `Password::INVALID_TOKEN` | Token non valido o scaduto    |
| `Password::INVALID_USER`  | Nessun utente con quell'email |

## Scadenza del token

Con l'opzione `expire` di `config/auth.php` imposti la validità del token in minuti. Default 60.

```php theme={null}
'passwords' => [
    'users' => [
        'driver' => 'database',
        'expire' => 60, // scade dopo 60 minuti
        'throttle' => 60,
    ],
],
```

Con il driver `database`, i token scaduti restano nel DB. Per pulirli periodicamente usa il comando Artisan:

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

Automatizzalo con lo scheduler.

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

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

## Personalizzazione

### Uso di una notifica personalizzata

Per personalizzare l'email di reset, sovrascrivi il metodo `sendPasswordResetNotification` sul modello `User`.

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

class User extends Authenticatable
{
    use Notifiable, CanResetPassword;

    /**
     * Invia la notifica di reset password
     */
    public function sendPasswordResetNotification($token): void
    {
        $url = 'https://example.com/reset-password?token='.$token;

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

### Personalizzazione dell'URL del link di reset

Nel metodo `boot` di `AppServiceProvider`, con `ResetPassword::createUrlUsing()` puoi cambiare l'URL del link di reset. Utile per reindirizzare a un frontend su un'altra origin (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;
    });
}
```

### Impostazione dei trusted host

L'URL del link di reset viene generato usando l'header `Host` della richiesta HTTP. Per prevenire richieste da host non validi, si consiglia di configurare i trusted host in `bootstrap/app.php`.

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

<Warning>
  Quando implementi il reset password verifica sempre la configurazione dei trusted host. Configurazioni insufficienti espongono al rischio di host header injection.
</Warning>

## Riepilogo

| Obiettivo                     | Metodo                                         |
| ----------------------------- | ---------------------------------------------- |
| Inviare il link di reset      | `Password::sendResetLink(['email' => $email])` |
| Reset della password          | `Password::reset($credentials, $callback)`     |
| Reset senza tabella           | Configura il driver `cache`                    |
| Eliminare token scaduti       | `php artisan auth:clear-resets`                |
| Personalizzare la notifica    | Sovrascrivere `sendPasswordResetNotification`  |
| Personalizzare l'URL di reset | `ResetPassword::createUrlUsing()`              |

## Prossimi passi

<Columns cols={2}>
  <Card title="Introduzione all'autenticazione" icon="lock" href="/it/authentication">
    Impara il funzionamento generale del sistema di autenticazione di Laravel.
  </Card>

  <Card title="Notifiche" icon="bell" href="/it/notifications">
    Approfondisci la personalizzazione delle notifiche email.
  </Card>
</Columns>


## Related topics

- [Laravel Fortify e gli starter kit](/it/advanced/fortify.md)
- [Aggiornamento da Laravel 8 a 9](/it/blog/upgrade-8-to-9.md)
- [Costruire una SPA con Inertia.js](/it/blog/inertia-introduction.md)
- [Guida alla migrazione da laravel/ui a Fortify](/it/blog/ui-to-fortify.md)
- [Personalizzare il rate limiting](/it/advanced/rate-limiting.md)
