Skip to content

パスワードのリセット

はじめに

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

Note

すぐに始めたいですか?新しい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言語ファイル内にあります。

Note

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

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

Note

パスワードリセットを手動で実装する場合、ビューとルートの内容を自分で定義する必要があります。すべての必要な認証と検証ロジックを含むスキャフォールディングを希望する場合は、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コマンドを使用して作成できます。

次に進む前に、LaravelがPasswordファサードのresetメソッドを呼び出す際に、どのようにアプリケーションのデータベースからユーザーレコードを取得するのか疑問に思うかもしれません。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));
}

ユーザーノート