コンテンツにスキップ

パスワードのリセット

はじめに

ほとんどのWebアプリケーションは、ユーザーが忘れたパスワードをリセットする方法を提供しています。Laravelは、作成するすべてのアプリケーションでこれを手動で再実装するのではなく、パスワードリセットリンクを送信し、パスワードを安全にリセットするための便利なサービスを提供します。

lightbulb

すぐに始めたいですか? 新しいLaravelアプリケーションにLaravel アプリケーションスターターキット をインストールします。Laravelのスターターキットは、忘れたパスワードのリセットを含む、認証システム全体のスカフォールディングを行います。

モデルの準備

Laravelのパスワードリセット機能を使用する前に、アプリケーションの `App\Models\User` モデルは `Illuminate\Notifications\Notifiable` トレイトを使用する必要があります。 通常、このトレイトは、新しいLaravelアプリケーションで作成されるデフォルトの `App\Models\User` モデルに既に含まれています。

次に、`App\Models\User` モデルが `Illuminate\Contracts\Auth\CanResetPassword` コントラクトを実装していることを確認します。 フレームワークに含まれる `App\Models\User` モデルは既にこのインターフェースを実装しており、`Illuminate\Auth\Passwords\CanResetPassword` トレイトを使用して、インターフェースを実装するために必要なメソッドを含めています。

データベースの準備

アプリケーションのパスワードリセットトークンを格納するテーブルを作成する必要があります。 通常、これはLaravelのデフォルトの `0001_01_01_000000_create_users_table.php` データベースマイグレーションに含まれています。

信頼できるホストの設定

デフォルトでは、LaravelはHTTPリクエストの `Host` ヘッダーの内容に関係なく、受信するすべてのリクエストに応答します。 さらに、`Host` ヘッダーの値は、Webリクエスト中にアプリケーションへの絶対URLを生成するときに使用されます.

通常は、NginxやApacheなどのWebサーバーを設定して、特定のホスト名と一致するリクエストのみをアプリケーションに送信する必要があります。 ただし、Webサーバーを直接カスタマイズできず、特定のホスト名にのみ応答するようにLaravelに指示する必要がある場合は、アプリケーションの `bootstrap/app.php` ファイルで `trustHosts` ミドルウェアメソッドを使用することでこれを行うことができます。 これは、アプリケーションがパスワードリセット機能を提供する場合に特に重要です。

このミドルウェアメソッドの詳細については、`TrustHosts` ミドルウェアドキュメント を参照してください。

ルーティング

ユーザーがパスワードをリセットできるようにするためのサポートを適切に実装するには、いくつかのルートを定義する必要があります。 まず、ユーザーがメールアドレスを介してパスワードリセットリンクをリクエストできるようにするためのルートのペアが必要です。 次に、ユーザーがメールで送信されたパスワードリセットリンクにアクセスしてパスワードリセットフォームに入力したら、実際にパスワードをリセットするためのルートのペアが必要です。

まず、パスワードリセットリンクをリクエストするために必要なルートを定義します。 まず、パスワードリセットリンクリクエストフォームを含むビューを返すルートを定義します

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

このルートによって返されるビューには、`email` フィールドを含むフォームが必要です。これにより、ユーザーは指定されたメールアドレスのパスワードリセットリンクをリクエストできます。

次に、「パスワードを忘れた」ビューからのフォーム送信リクエストを処理するルートを定義します。 このルートは、メールアドレスを検証し、対応するユーザーにパスワードリセットリクエストを送信する役割を担います

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

先に進む前に、このルートを詳しく見てみましょう。 まず、リクエストの `email` 属性が検証されます。 次に、Laravelの組み込み「パスワードブローカー」(`Password` ファサード経由)を使用して、ユーザーにパスワードリセットリンクを送信します。 パスワードブローカーは、指定されたフィールド(この場合はメールアドレス)によってユーザーを取得し、Laravelの組み込み通知システムを介してユーザーにパスワードリセットリンクを送信します。

`sendResetLink` メソッドは、「ステータス」スラッグを返します。 このステータスは、Laravelのローカライゼーションヘルパーを使用して翻訳し、リクエストのステータスに関するユーザーフレンドリーなメッセージをユーザーに表示できます。 パスワードリセットステータスの翻訳は、アプリケーションの `lang/{lang}/passwords.php` 言語ファイルによって決まります。 ステータススラッグの各可能な値のエントリは、`passwords` 言語ファイル内にあります。

lightbulb

デフォルトでは、Laravelアプリケーションスケルトンには `lang` ディレクトリが含まれていません。 Laravelの言語ファイルをカスタマイズする場合は、`lang:publish` Artisanコマンドを使用して公開できます。

`Password` ファサードの `sendResetLink` メソッドを呼び出すときに、Laravelがアプリケーションのデータベースからユーザーレコードを取得する方法を知っていることに疑問に思うかもしれません。 Laravelパスワードブローカーは、認証システムの「ユーザープロバイダ」を利用してデータベースレコードを取得します。 パスワードブローカーが使用するユーザープロバイダは、`config/auth.php` 設定ファイルの `passwords` 設定配列内で設定されます。 カスタムユーザープロバイダの作成の詳細については、認証ドキュメント を参照してください。

lightbulb

パスワードリセットを手動で実装する場合、ビューとルートの内容を自分で定義する必要があります。 必要なすべての認証と検証ロジックを含むスカフォールディングが必要な場合は、Laravelアプリケーションスターターキット を確認してください。

パスワードのリセット

パスワードリセットフォーム

次に、ユーザーがメールで送信されたパスワードリセットリンクをクリックして新しいパスワードを入力したら、実際にパスワードをリセットするために必要なルートを定義します。 まず、ユーザーがパスワードリセットリンクをクリックしたときに表示されるパスワードリセットフォームを表示するルートを定義しましょう。 このルートは、後でパスワードリセットリクエストを検証するために使用する `token` パラメータを受け取ります

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

このルートから返されるビューには、email フィールド、password フィールド、password_confirmation フィールド、そしてルートで受け取ったシークレットの $token の値を含む非表示の token フィールドを含むフォームが表示される必要があります。

フォーム送信の処理

もちろん、パスワードリセットフォームの送信を実際に処理するルートを定義する必要があります。このルートは、受信リクエストの検証とデータベース内のユーザーパスワードの更新を担当します。

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

先に進む前に、このルートをさらに詳しく見てみましょう。最初に、リクエストの tokenemail、および password 属性が検証されます。次に、Laravelの組み込みの「パスワードブローカー」(Password ファサード経由)を使用して、パスワードリセットリクエストの資格情報を検証します。

パスワードブローカーに渡されたトークン、メールアドレス、およびパスワードが有効な場合、reset メソッドに渡されたクロージャが呼び出されます。このクロージャ内では、ユーザーインスタンスとパスワードリセットフォームに提供されたプレーンテキストのパスワードを受け取り、データベース内のユーザーのパスワードを更新できます。

reset メソッドは「ステータス」スラッグを返します。このステータスは、Laravelのローカライゼーションヘルパーを使用して翻訳することで、リクエストのステータスに関するユーザーフレンドリーなメッセージをユーザーに表示できます。パスワードリセットステータスの翻訳は、アプリケーションの lang/{lang}/passwords.php 言語ファイルによって決定されます。ステータススラッグの各可能な値のエントリは、passwords 言語ファイル内にあります。アプリケーションに lang ディレクトリがない場合は、lang:publish Artisanコマンドを使用して作成できます。

先に進む前に、Password ファサードの reset メソッドを呼び出すときに、Laravelがアプリケーションのデータベースからユーザーレコードを取得する方法をどのように認識しているのか疑問に思われるかもしれません。Laravelパスワードブローカーは、認証システムの「ユーザープロバイダー」を利用してデータベースレコードを取得します。パスワードブローカーによって使用されるユーザープロバイダーは、config/auth.php 設定ファイルの passwords 設定配列内で設定されます。カスタムユーザープロバイダーの作成の詳細については、認証ドキュメントを参照してください。

期限切れトークンの削除

期限切れのパスワードリセットトークンは、データベース内に残っています。ただし、auth:clear-resets Artisanコマンドを使用して、これらのレコードを簡単に削除できます。

php artisan auth:clear-resets

このプロセスを自動化したい場合は、アプリケーションのスケジューラにコマンドを追加することを検討してください。

use Illuminate\Support\Facades\Schedule;
 
Schedule::command('auth:clear-resets')->everyFifteenMinutes();

カスタマイズ

ResetPassword 通知クラスによって提供される createUrlUsing メソッドを使用して、パスワードリセットリンクのURLをカスタマイズできます。このメソッドは、通知を受信するユーザーインスタンスとパスワードリセットリンクトークンを受け取るクロージャを受け入れます。通常、このメソッドは App\Providers\AppServiceProvider サービスプロバイダーの boot メソッドから呼び出す必要があります。

use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
ResetPassword::createUrlUsing(function (User $user, string $token) {
return 'https://example.com/reset-password?token='.$token;
});
}

リセットメールのカスタマイズ

ユーザーにパスワードリセットリンクを送信するために使用される通知クラスを簡単に変更できます。開始するには、App\Models\User モデルの sendPasswordResetNotification メソッドをオーバーライドします。このメソッド内では、独自の通知クラスを使用して通知を送信できます。パスワードリセット $token は、メソッドが受け取る最初の引数です。この $token を使用して、任意のパスワードリセットURLを作成し、ユーザーに通知を送信できます。

use App\Notifications\ResetPasswordNotification;
 
/**
* Send a password reset notification to the user.
*
* @param string $token
*/
public function sendPasswordResetNotification($token): void
{
$url = 'https://example.com/reset-password?token='.$token;
 
$this->notify(new ResetPasswordNotification($url));
}