パスワードリセット
イントロダクション
ほとんどのWebアプリケーションには、ユーザーが忘れたパスワードをリセットする方法が用意されています。Laravelは、作成するすべてのアプリケーションでこの機能をいちいち再実装することを強制するのではなく、パスワードリセットリンクを送信し、安全にパスワードをリセットするための便利なサービスを提供しています。
すぐに始めたいですか?新しいLaravelアプリケーションにLaravelアプリケーションスターターキットをインストールしてください。Laravelのスターターキットは、忘れたパスワードのリセットを含む、認証システム全体のスカフォールドを処理します。
モデルの準備
Laravelのパスワードリセット機能を使用する前に、アプリケーションのApp\Models\UserモデルでIlluminate\Notifications\Notifiableトレイトを使用する必要があります。通常、このトレイトは、新しいLaravelアプリケーションで作成されるデフォルトのApp\Models\Userモデルにすでに含まれています。
次に、App\Models\UserモデルがIlluminate\Contracts\Auth\CanResetPassword契約(contract)を実装していることを確認してください。フレームワークに含まれているApp\Models\Userモデルは、すでにこのインターフェイスを実装しており、Illuminate\Auth\Passwords\CanResetPasswordトレイトを使用して、インターフェイスを実装するために必要なメソッドを含んでいます。
データベースの準備
アプリケーションのパスワードリセットトークンを保存するためのテーブルを作成する必要があります。通常、これはLaravelのデフォルトの0001_01_01_000000_create_users_table.phpデータベースマイグレーションに含まれています。
信頼するホストの設定
デフォルトでは、LaravelはHTTPリクエストのHostヘッダの内容に関係なく、受信したすべてのリクエストに応答します。さらに、Webリクエスト中にアプリケーションへの絶対URLを生成する際にもHostヘッダの値が使用されます。
通常は、特定のホスト名に一致するリクエストのみをアプリケーションに送信するように、NginxやApacheなどのWebサーバを設定する必要があります。しかし、Webサーバを直接カスタマイズできず、特定のホスト名にのみ応答するようにLaravelに指示する必要がある場合は、アプリケーションのbootstrap/app.phpファイルでtrustHostsミドルウェアメソッドを使用してそうすることができます。これは、アプリケーションがパスワードリセット機能を提供する場合に特に重要です。
このミドルウェアメソッドについて詳しく知りたい場合は、TrustHostsミドルウェアのドキュメントを参照してください。
ルーティング
ユーザーがパスワードをリセットできるようにするサポートを適切に実装するには、いくつかのルートを定義する必要があります。まず、ユーザーがメールアドレス経由でパスワードリセットリンクをリクエストできるようにするためのルートが2つ必要です。次に、ユーザーがメールで送信されたパスワードリセットリンクにアクセスし、パスワードリセットフォームを完了した後に、実際にパスワードをリセットするためのルートが2つ必要です。
パスワードリセットリンクの要求
パスワードリセットリンクリクエストフォーム
まず、パスワードリセットリンクをリクエストするために必要なルートを定義します。手始めに、パスワードリセットリンクリクエストフォームを持つビューを返すルートを定義します。
1Route::get('/forgot-password', function () {2 return view('auth.forgot-password');3})->middleware('guest')->name('password.request');
このルートによって返されるビューには、ユーザーが特定のメールアドレスのパスワードリセットリンクをリクエストできるようにするemailフィールドを含むフォームが必要です。
フォーム送信の処理
次に、「パスワードを忘れた場合」ビューからのフォーム送信リクエストを処理するルートを定義します。このルートは、メールアドレスを検証し、対応するユーザーにパスワードリセットリクエストを送信する責任を負います。
1use Illuminate\Http\Request; 2use Illuminate\Support\Facades\Password; 3 4Route::post('/forgot-password', function (Request $request) { 5 $request->validate(['email' => 'required|email']); 6 7 $status = Password::sendResetLink( 8 $request->only('email') 9 );10 11 return $status === Password::ResetLinkSent12 ? back()->with(['status' => __($status)])13 : back()->withErrors(['email' => __($status)]);14})->middleware('guest')->name('password.email');
次に進む前に、このルートを詳しく見てみましょう。まず、リクエストのemail属性が検証されます。次に、Laravelの組み込み「パスワードブローカー」(Passwordファサード経由)を使用して、ユーザーにパスワードリセットリンクを送信します。パスワードブローカーは、指定されたフィールド(この場合はメールアドレス)でユーザーを取得し、Laravelの組み込み通知システムを介してユーザーにパスワードリセットリンクを送信する処理を担当します。
sendResetLinkメソッドは「ステータス」のスラッグを返します。このステータスは、Laravelのローカリゼーションヘルパーを使用して翻訳し、リクエストのステータスに関するユーザーフレンドリーなメッセージを表示できます。パスワードリセットステータスの翻訳は、アプリケーションのlang/{lang}/passwords.php言語ファイルによって決定されます。ステータスの各スラッグの取り得る値のエントリは、passwords言語ファイル内にあります。
デフォルトでは、Laravelアプリケーションスケルトンにはlangディレクトリは含まれていません。Laravelの言語ファイルをカスタマイズしたい場合は、lang:publish Artisanコマンドを介して公開できます。
PasswordファサードのsendResetLinkメソッドを呼び出すときに、Laravelがアプリケーションのデータベースからユーザーレコードを取得する方法を不思議に思うかもしれません。Laravelのパスワードブローカーは、認証システムの「ユーザープロバイダ」を利用してデータベースレコードを取得します。パスワードブローカーが使用するユーザープロバイダは、config/auth.php設定ファイルのpasswords設定配列内で設定されます。カスタムユーザープロバイダの作成について詳しく知りたい場合は、認証のドキュメントを参照してください。
パスワードリセットを手動で実装する場合、ビューとルートの内容を自分で定義する必要があります。必要なすべての認証と検証ロジックを含むスカフォールドが必要な場合は、Laravelアプリケーションスターターキットをチェックしてください。
パスワードのリセット
パスワードリセットフォーム
次に、ユーザーがメールで送信されたパスワードリセットリンクをクリックし、新しいパスワードを入力した後に、実際にパスワードをリセットするために必要なルートを定義します。まず、ユーザーがリセットパスワードリンクをクリックしたときに表示されるリセットパスワードフォームを表示するルートを定義しましょう。このルートは、後でパスワードリセットリクエストを検証するために使用するtokenパラメータを受け取ります。
1Route::get('/reset-password/{token}', function (string $token) {2 return view('auth.reset-password', ['token' => $token]);3})->middleware('guest')->name('password.reset');
このルートによって返されるビューには、emailフィールド、passwordフィールド、password_confirmationフィールド、およびルートが受け取った秘密の$tokenの値を含むべき隠しtokenフィールドを含むフォームを表示する必要があります。
フォーム送信の処理
もちろん、実際にパスワードリセットフォームの送信を処理するためのルートを定義する必要があります。このルートは、受信リクエストを検証し、データベース内のユーザーのパスワードを更新する責任を負います。
1use App\Models\User; 2use Illuminate\Auth\Events\PasswordReset; 3use Illuminate\Http\Request; 4use Illuminate\Support\Facades\Hash; 5use Illuminate\Support\Facades\Password; 6use Illuminate\Support\Str; 7 8Route::post('/reset-password', function (Request $request) { 9 $request->validate([10 'token' => 'required',11 'email' => 'required|email',12 'password' => 'required|min:8|confirmed',13 ]);14 15 $status = Password::reset(16 $request->only('email', 'password', 'password_confirmation', 'token'),17 function (User $user, string $password) {18 $user->forceFill([19 'password' => Hash::make($password)20 ])->setRememberToken(Str::random(60));21 22 $user->save();23 24 event(new PasswordReset($user));25 }26 );27 28 return $status === Password::PasswordReset29 ? redirect()->route('login')->with('status', __($status))30 : back()->withErrors(['email' => [__($status)]]);31})->middleware('guest')->name('password.update');
次に進む前に、このルートを詳しく見てみましょう。まず、リクエストのtoken、email、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コマンドを使用して、これらのレコードを簡単に削除できます。
1php artisan auth:clear-resets
このプロセスを自動化したい場合は、このコマンドをアプリケーションのスケジューラに追加することを検討してください。
1use Illuminate\Support\Facades\Schedule;2 3Schedule::command('auth:clear-resets')->everyFifteenMinutes();
カスタマイズ
リセットリンクのカスタマイズ
ResetPassword通知クラスが提供するcreateUrlUsingメソッドを使用して、パスワードリセットリンクのURLをカスタマイズできます。このメソッドは、通知を受け取るユーザーインスタンスとパスワードリセットリンクトークンを受け取るクロージャを引数に取ります。通常、このメソッドはApp\Providers\AppServiceProviderサービスプロバイダのbootメソッドから呼び出すべきです。
1use App\Models\User; 2use Illuminate\Auth\Notifications\ResetPassword; 3 4/** 5 * Bootstrap any application services. 6 */ 7public function boot(): void 8{ 9 ResetPassword::createUrlUsing(function (User $user, string $token) {10 return 'https://example.com/reset-password?token='.$token;11 });12}
リセットメールのカスタマイズ
ユーザーにパスワードリセットリンクを送信するために使用される通知クラスを簡単に変更できます。始めるには、App\Models\UserモデルのsendPasswordResetNotificationメソッドをオーバーライドします。このメソッド内で、自分で作成した任意の通知クラスを使用して通知を送信できます。パスワードリセットの$tokenは、メソッドが受け取る最初の引数です。この$tokenを使用して、選択したパスワードリセットURLを構築し、ユーザーに通知を送信できます。
1use App\Notifications\ResetPasswordNotification; 2 3/** 4 * Send a password reset notification to the user. 5 * 6 * @param string $token 7 */ 8public function sendPasswordResetNotification($token): void 9{10 $url = 'https://example.com/reset-password?token='.$token;11 12 $this->notify(new ResetPasswordNotification($url));13}