Laravelに標準搭載されている認証の処理について詳細に追いかけていきます。
バージョン
> php artisan --version
Laravel Framework 6.20.30
// 以下を実行して標準搭載の認証機能が実装されている前提です
> composer require laravel/ui:^1.0 --dev
> php artisan ui vue --auth
ログイン処理の流れを追う
以下の流れで追いかけていきます!
- 0. ルーティングの確認
- 1. ログインフォームの表示処理
- 2. ログイン処理
0. ルーティングの確認
ログイン処理に使用するのは項番を振った2つだけです。
> php artisan route:list
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth |
|1 | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web |
|2 | POST | login | | App\Http\Controllers\Auth\LoginController@login | web |
| | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web |
| | GET|HEAD | password/confirm | password.confirm | App\Http\Controllers\Auth\ConfirmPasswordController@showConfirmForm | web,auth |
| | POST | password/confirm | | App\Http\Controllers\Auth\ConfirmPasswordController@confirm | web,auth |
| | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web |
| | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web |
| | POST | password/reset | password.update | App\Http\Controllers\Auth\ResetPasswordController@reset | web |
| | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web |
| | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest |
| | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
1. GET | login | App\Http\Controllers\Auth\LoginController@showLoginForm
ログインフォームを表示するためのルーティングです。
2. POST | login | App\Http\Controllers\Auth\LoginController@login
ログイン処理を行うためのルーティングです。
POSTされたリクエストのバリデーション、実際のログイン処理、POSTされたリクエストでログインできない場合のリダイレクトなど内部的に多くの処理を行っています。
1. ログインフォームの表示処理
1.1 ログインフォームの表示処理
/login
へのGETリクエストが発行されると、LoginController
のshowLoginForm
メソッドが呼び出されます。
public function showLoginForm()
{
return view('auth.login');
}
resouce/views/auth
フォルダにあるlogin.blade.php
を返すだけのものですね。簡単!
寄り道:トレイトについて
実はLoginController
のコードを見ると、ほとんど何も記載されていません。
代わりにコントローラ内には下記の記述があります。
use AuthenticatesUsers; // trait AuthenticatesUsersを使用する宣言
上記のshowLoginForm
メソッドを含むほとんどのメソッドは、コントローラ内ではなくAuthenticatesusers
トレイトに実装されています。
PHP は、コードを再利用するための「トレイト」という仕組みを実装しています。
トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。
トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメンバーに追加できるようになります。
引用:https://www.php.net/manual/ja/language.oop5.traits.php
AuthenticatesUsers
トレイトではtrait AuthenticatesUsers
のような形式でトレイトを宣言しており、トレイトの中にログインに使用されるメソッドをまとめています。
LoginController
ではトレイトの使用を宣言しているので、継承しなくてもトレイトに記述されたメソッドをLoginController
に記述されているかのように使用できます。
この後に流れを追っていくメソッド群も基本的にLoginController
ではなくAuthenticatesUsers
など他のトレイトに実装されています。
またトレイトのメソッドはuse
宣言している側でオーバーライドできるので、処理をカスタマイズしたい場合はカスタマイズしたいメソッドだけをuse
側で実装することができます。
標準搭載のログイン処理の中で要件に合わせてカスタマイズしたいものがあれば、LoginController
でそのメソッドだけをオーバーライドすれば簡単にオリジナルのログイン処理が実装できそうです!
2. ログイン処理
/loginへのPOSTリクエストが発行されると、LoginControllerのloginメソッドが呼び出されます。
やっていることが非常に多いので、細かく分割して見ていきます。
// 2. ログイン処理
public function login(Request $request)
{
// 2.1. ログインリクエストのバリデーション処理
$this->validateLogin($request);
// 2.2. ログイン失敗回数のチェック処理
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
// 2.3. ログイン処理
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// 2.4. ログイン失敗回数を加算する処理
$this->incrementLoginAttempts($request);
// 2.5. ログイン失敗のレスポンスを返す処理
return $this->sendFailedLoginResponse($request);
}
2.1. ログインリクエストのバリデーション処理
// 2.1. ログインリクエストのバリデーション処理
$this->validateLogin($request);
POSTリクエストに対してバリデーション処理を行います。
validateLogin
メソッドを使っていますが、このメソッドもAuthenticatesUsers
内に実装されています。
protected function validateLogin(Request $request)
{
$request->validate([
$this->username() => 'required|string',
'password' => 'required|string',
]);
}
ここではPOSTされたusername()
とpassword
に対してバリデーションを行っています。
username()
は認証に使用する情報を指定するメソッドであり、デフォルトではemailが設定されています。
public function username()
{
return 'email';
}
つまりはリクエストのemail
password
それぞれに対して
- required(必須項目であること)
- string(文字列の形式であること)
のチェックを行っているということですね。
バリデーションに成功すれば以降のバリデーション以降の処理が実行され、失敗すれば適切なエラーレスポンスが生成されてlogin
メソッドの処理は止まります。
validateメソッドの掘り下げは記事の本旨からは外れますので、使い方の引用にとどめます。
バリデーションロジック
Illuminate\Http\Requestオブジェクトが提供する、validateメソッドを使います。バリデーションルールに成功すると、コードは通常通り続けて実行されます。逆にバリデーションへ失敗すると例外が投げられ、ユーザーに対し自動的に適切なエラーレスポンスが返されます。伝統的なHTTPリクエストの場合は、リダイレクトレスポンスが生成され、一方でAJAXリクエストにはJSONレスポンスが返されます。
実行したいバリデーションルールをvalidateメソッドへ渡します。繰り返しますが、バリデーションに失敗すれば、適切なレスポンスが自動的に生成されます。バリデーションに成功すれば、コントローラは続けて通常通り実行されます。
引用:https://readouble.com/laravel/6.x/ja/validation.html
2.2. ログイン失敗回数のチェック処理
Webアプリケーションではパスワードリスト攻撃などを回避する目的で、連続でログインに失敗した場合一定時間ログインできない時間を設けることが良くあります。
Laravelの認証においてはこの機能も標準で実装されています。すごい。
// 2.2. ログイン失敗回数のチェック処理
if (method_exists($this, 'hasTooManyLoginAttempts') &&
// 2.2.1. ログイン失敗回数が設定した回数を超えていないかのチェック処理
$this->hasTooManyLoginAttempts($request)) {
// 2.2.2. ログイン失敗回数超過によるイベント発火処理
$this->fireLockoutEvent($request);
// 2.2.3. ログイン失敗回数超過によるログイン失敗レスポンスの作成処理
return $this->sendLockoutResponse($request);
}
2.2.1. ログイン失敗回数が設定した回数を超えていないかのチェック処理
hasTooManyLoginAttempts
メソッドは\vendor\laravel\framework\src\Illuminate\Foundation\Auth\ThrottlesLogins.php
に記載されています。
設定した回数ログインに失敗している場合hasTooManyLoginAttempts
がtrueを返し、2.2.2~2.2.3の処理が実行されます。
protected function hasTooManyLoginAttempts(Request $request)
{
// 現在のユーザのログイン失敗回数と、ログイン失敗の最大回数を引数にtooManyAttemptsメソッドを呼ぶ
return $this->limiter()->tooManyAttempts(
$this->throttleKey($request), $this->maxAttempts()
);
}
tooManyLoginAttempts
メソッドもThrottlesLogins
トレイトに記載されています。
public function tooManyAttempts($key, $maxAttempts)
{
// 現在のユーザのログイン失敗回数が、設定した回数よりも多い場合
if ($this->attempts($key) >= $maxAttempts) {
// キャッシュに:timerというキーが保持されている場合
if ($this->cache->has($key.':timer')) {
// ログイン失敗回数超過、trueを返す
return true;
}
// キャッシュに:timerというキーが保持されていない場合、ログイン失敗回数をリセットする
$this->resetAttempts($key);
}
// 現在のユーザのログイン失敗回数が、設定した回数に達していない場合、falseを返して2.3.以降の後続処理を実行する
return false;
}
tooManyAttempts
メソッドはまずattempts($key)
(キャッシュに保存された現在のログイン失敗回数)と、maxAttempts
(連続ログイン失敗を許容する回数)を比較して、連続でログインに失敗した回数が許容する回数を超えていないかを判定します。
許容されるログインの失敗回数を超過している場合、if ($this->cache->has($key.':timer'))
でキャッシュに:timer
という値が保持されているかどうかを見ています。
後述する処理で登場しますが、ログインに連続で失敗した場合、ユーザに対してしばらくログインできないペナルティ時間が設けられます。
そのログインできない時間はキャッシュ上に:timer
という項目が設けられます(ペナルティ時間が過ぎている場合は:timer
項目は空になります)。
つまりif ($this->cache->has($key.':timer'))
では、決められたログインできない時間がもう終わっているか?ということを判定しています。
($this->cache->has($key.':timer'))
がtrueである(=まだログインできない時間である)場合は、tooManyLoginAttempts
メソッドはtrueを返して2.2.2以降のログイン失敗回数が超過していた場合の処理に移行します。
($this->cache->has($key.':timer'))
がfalseである(=ログインできない時間が過ぎたので、もうログインできる)場合は、後続のresetAttempts
メソッドでログイン失敗回数をリセットし、2.3.以降のログイン処理を実行します。
2.2.2. ログイン失敗回数超過によるイベント発行処理
// 2.2.2. ログイン失敗回数超過によるイベント発行処理
$this->fireLockoutEvent($request);
fireLockoutEvent
メソッドもThrottlesLogin
トレイトに記載。
protected function fireLockoutEvent(Request $request)
{
event(new Lockout($request));
}
event
ヘルパに新しく作成したLockout
イベントのインスタンスを渡すことで、イベントを発行しています。
リスナーを登録しておくことで、ログイン回数超過のイベントの発生を検知し、様々な処理を行うことができます。
イベントの掘り下げについては記事の本旨からは外れますので、割愛します。
2.2.3. ログイン失敗回数超過によるログイン失敗レスポンスの作成処理
// 2.2.3. ログイン失敗回数超過によるログイン失敗レスポンスの作成処理
return $this->sendLockoutResponse($request);
sendLockoutResponse
メソッドでは、バリデーションエラーの例外を投げます。
protected function sendLockoutResponse(Request $request)
{
// エラーメッセージに表示する、ログインできない時間の残りを計算する処理
$seconds = $this->limiter()->availableIn(
$this->throttleKey($request)
);
// バリデーションエラーの例外を投げる
throw ValidationException::withMessages([
$this->username() => [Lang::get('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
])],
])->status(Response::HTTP_TOO_MANY_REQUESTS);
}
エラーメッセージはusername()
に対してresources\lang\en\auth.php
に記載されたthrottle
が設定されます。
// ログイン試行回数が多すぎます。<:seconds>秒後にまた試してください。
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
throttle
メッセージの内容を書き換えれば、好きなログイン失敗メッセージを設定できますね。
minutes(seconds / 60)
変数も標準で用意されているので、分単位での表示も簡単にできそうです。
2.3. ログイン処理
ようやく実際のログイン処理です!
2.3.1.の認証処理がtrueを返せば、2.3.2.でログインに成功した旨のレスポンスを返す...という流れのようです。
// 2.3. ログイン処理
// 2.3.1. ログインの認証処理
if ($this->attemptLogin($request)) {
// 2.3.2 ログイン成功のレスポンス返却処理
return $this->sendLoginResponse($request);
}
2.3.1. ログインの認証処理
attemptLogin
メソッドではデフォルトに指定したガードのattempt
メソッドを呼び出しているだけで、その結果を論理値で返します。
protected function attemptLogin(Request $request)
{
return $this->guard()->attempt(
// 引数1:credentialsはリクエストから`username`と`password`以外の属性を削除した結果を返す
// 引数2:filledはフォームの「remember me」チェックボックスにチェックが入っていたかどうかを返す
$this->credentials($request), $request->filled('remember')
);
}
attempt
メソッドは、これまたやっていることが多いので分割して見ます。
public function attempt(array $credentials = [], $remember = false)
{
// 2.3.1.1. ログイン認証のイベント発行処理
$this->fireAttemptEvent($credentials, $remember);
// 2.3.1.2. ユーザ情報の取得処理
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
// 2.3.1.3. 取得したユーザとテーブルに保持するユーザの情報が一致するかのチェック処理
if ($this->hasValidCredentials($user, $credentials)) {
// 2.3.1.4. ログイン処理
$this->login($user, $remember);
return true;
}
return false;
}
2.3.1.1. ログイン認証のイベント発行処理
$this->fireAttemptEvent($credentials, $remember);
こちらも、ログイン認証が発生していることのイベントを発行しています。
2.2.2.同様、イベントについては割愛します。
2.3.1.2. ユーザ情報の取得処理
// 2.3.1.2. ユーザ情報の取得処理
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
public function retrieveByCredentials(array $credentials)
{
// $credentialsが空でないか
if (empty($credentials) ||
// $credentials配列の長さが1でないか(=usernameとpasswordのいずれかだけでないか)
(count($credentials) === 1 &&
// $credentials配列に'password'キーが含まれているか
array_key_exists('password', $credentials))) {
return;
}
$query = $this->newModelQuery();
foreach ($credentials as $key => $value) {
// $keyが'password'なら次のループへ(=passwordではない方のパラメータでテーブル検索をしたい)
if (Str::contains($key, 'password')) {
continue;
}
// $credentialに含まれる認証情報(=デフォルトはemail)が、テーブルに存在するか確認する
if (is_array($value) || $value instanceof Arrayable) {
$query->whereIn($key, $value);
} else {
$query->where($key, $value);
}
}
// 取得したユーザを返す
return $query->first();
}
retrieveByCredentials
メソッドで、リクエストされたユーザに合致するものがテーブルに存在するかどうか検索します。
(ここではpassword
の検証は行わず、username()
に基づくユーザの有無だけを検証します)
指定した情報に当てはまるユーザが見つかった場合、$user
に対象のユーザが設定されます。
2.3.1.3. 取得したユーザとテーブルに保持するユーザの情報が一致するかのチェック処理
// 2.3.1.3. 取得したユーザとテーブルに保持するユーザの情報が一致するかのチェック処理
if ($this->hasValidCredentials($user, $credentials)) {
protected function hasValidCredentials($user, $credentials)
{
return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
}
取得したユーザとリクエストの認証情報を基にhasValidCredentials
メソッドを実行します。
hasValidCredentials
メソッドでは「$user
がnullでないこと(=2.3.1.2.でユーザ情報が取得できていること)」「validateCredentials
メソッドの返り値がtrueであること」をを確認しています。
public function validateCredentials(UserContract $user, array $credentials)
{
// リクエストに含まれる平文のパスワード
$plain = $credentials['password'];
// 平文のパスワードをハッシュ化したものと、テーブルに含まれるパスワードが同一かチェックし、合致すればtrueを返す
return $this->hasher->check($plain, $user->getAuthPassword());
}
validCredentials
メソッドでは平文のパスワードをハッシュ化したものと、テーブルに含まれるパスワードが同一かチェックしています。
このhasValidCredentials
メソッドの返り値がtrueであれば、ユーザがシステムに存在する&パスワードが一致するので、ようやくlogin
メソッドが呼べます!長かった...。
2.3.1.4. ログイン処理
// 2.3.1.4. ログイン処理
$this->login($user, $remember);
login
メソッドは以下の通りです。
public function login(AuthenticatableContract $user, $remember = false)
{
// セッションにユーザ情報を格納する
$this->updateSession($user->getAuthIdentifier());
// 「remember me」のチェックが入っていた場合
if ($remember) {
// ランダムな60文字の文字列からなるトークンを作成しUserテーブルのremember_tokenに保存する
$this->ensureRememberTokenIsSet($user);
// Cookieに上記と同じトークンを保存する
$this->queueRecallerCookie($user);
}
// ログインイベントを発行する
$this->fireLoginEvent($user, $remember);
// userにログインしたユーザ情報をセットする
$this->setUser($user);
}
一番最初の処理でセッションにユーザを識別する情報(デフォルトでは$user->id)を保存しており、これが実際のログインの処理に当たります。「ログインしている状態」とは具体的にはセッションにユーザIDが保存されている状態ということですね。
「remember me」にチェックを入れてログインした場合if ($remember)
がtrueになるのでUserテーブルのremember_tokenカラムにトークン用に生成された文字列が格納されます。
セッションが切れていてもCookieに含まれるトークンとUserテーブルのremember_tokenが一致すれば同じユーザであることが保証されるので再度のログイン処理が不要になります。
末尾の処理ではsetUserメソッドで$this
(ここでloginメソッドはSessionGuardクラスのメソッドなので、SessionGuardインスタンス)のuser
にログインに成功したユーザ情報を設定します。
ログイン後にAuth::user()
などでユーザを簡単に取得できるのはここでセットしているおかげのようですね!
2.3.2 ログイン成功のレスポンス返却処理
ログイン認証処理が非常に長くなったので現在位置をおさらいします。
// 2.3. ログイン処理
// 2.3.1. ログインの認証処理
if ($this->attemptLogin($request)) { // <-ログインに成功し、attemptLoginの戻り値がtrueになったところ
// 2.3.2 ログイン成功のレスポンス返却処理
return $this->sendLoginResponse($request);
}
2.3.1.でログインに成功しif($this->attemptLogin($request))
がtrueになったので、ログイン成功のレスポンスを作成する処理が実行されるのでした。
sendLoginResponse
もやることが多いので、分割して中身を見ていきます。
protected function sendLoginResponse(Request $request)
{
// セッションを再作成する
$request->session()->regenerate();
// ログイン失敗回数をリセットする
$this->clearLoginAttempts($request);
// リダイレクトする
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
session()->regenerate()
の役割についてはドキュメントより以下の通りです。
セッションIDの再生成
セッションIDの再生成は多くの場合、悪意のあるユーザーからの、アプリケーションに対するsession fixation攻撃を防ぐために行います。
Laravelに組み込まれているLoginControllerを使用していれば、認証中にセッションIDは自動的に再生成されます。しかし、セッションIDを任意に再生成する必要があるのでしたら、regenerateメソッドを使ってください。
clearLoginAttempts
ではログインに成功したので、加算されていたログイン失敗回数をリセットします。
redirectPath
メソッドではredirectTo
変数が設定されているかをチェックし、設定されている場合は$redirectTo
で指定したパスに、設定されていない場合は/home
にリダイレクトされます。
public function redirectPath()
{
if (method_exists($this, 'redirectTo')) {
return $this->redirectTo();
}
return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
}
redirectTo
にはLoginController
内で、ホーム画面へのパスが指定されています。
ログイン後に遷移する画面を変更したい場合、redirectTo
のパス指定を変更するだけ簡単に実現できます。
// デフォルトではhome
protected $redirectTo = RouteServiceProvider::HOME;
ログインに成功すればログイン成功のレスポンスがreturnされるため、コントローラの処理はここで終了です。
下記の2.4.以降の処理はログインに失敗した場合に行われる処理になります。
2.4. ログイン失敗回数を加算する処理
returnされずにこの処理に到達したということはログインに失敗しているので、ログイン失敗回数を加算します。
// 2.4. ログイン失敗回数を加算する処理
$this->incrementLoginAttempts($request);
incrementLoginAttempts
メソッドでは、ログイン失敗回数を保持するキーと再度ログインを試行できるようになるまでの時間(デフォルトでは60秒)を引数にhit
メソッドを呼び出します。
protected function incrementLoginAttempts(Request $request)
{
$this->limiter()->hit(
$this->throttleKey($request), $this->decayMinutes() * 60
);
}
hit
メソッドでは先に登場した通り、キャッシュに:timer
をセットします。
この:timer
がキャッシュ上に残っている限り、規定回数パスワードを誤った後の再度ログインを試行できないというやつですね。
public function hit($key, $decaySeconds = 60)
{
// キャッシュに':timer'をセットする
$this->cache->add(
$key.':timer', $this->availableAt($decaySeconds), $decaySeconds
);
// ログイン失敗回数「0」をキャッシュに保存する
$added = $this->cache->add($key, 0, $decaySeconds);
// キャッシュに保存されたログイン失敗回数に+1する
$hits = (int) $this->cache->increment($key);
// $addedがfalseである(=既にキャッシュにキーが存在したため、今回が初めてのログイン失敗ではない)
// かつ、hitsの値が1である場合
if (! $added && $hits == 1) {
// ログイン失敗回数「1」をキャッシュに保存する
$this->cache->put($key, 1, $decaySeconds);
}
return $hits;
}
cache->add
ではキャッシュに新たなキーと値の組を保存しますが、保存できたかどうかがわかるように保存した場合true
、できなかった場合false
を返してくれます。
つまり既にキーが存在する場合(=すでにログインに失敗しており、ログイン失敗回数が保持されている場合)はキーを作成せずにfalseが返され、まだキーが存在しない場合(=初めてのログイン失敗)は新たにログイン失敗回数として「0」をキャッシュに保存しtrueを返します。
また、add
メソッドは第三引数としてそのキャッシュが消滅するまでの時間を指定できるので、ログインの失敗は$decaySeconds
が経過すれば無かったことになるわけですね。
そのまま下のincrement
で失敗回数を増やしているので、キーが存在すればそのまま今までの失敗回数に1が加算され、存在しなければ新たに失敗回数として1が記録されます。
if (! $added && $hits == 1)
は「すでに失敗回数のキーが存在したかつ**$hitsの値が1である**(=incrementの結果、失敗回数が「1」になった)場合にログイン失敗回数に「1」を設定するというものです...が、どういったケースの時にこの条件に当てはまるのかが怪しいです...。
このhit
メソッド外で既に失敗回数「0」のキャッシュが作成されていた場合でしょうか?
2.5. ログイン失敗のレスポンスを返す処理
// 2.5. ログイン失敗のレスポンスを返す処理
return $this->sendFailedLoginResponse($request);
sendFailedLoginResponse
メソッドで作成されたレスポンスが返されます。
protected function sendFailedLoginResponse(Request $request)
{
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}
sendFailedLoginResponse
メソッドで、2.2.2.のログイン失敗回数超過の場合と同様にバリデーションの例外を投げます。
エラーメッセージはusername
に対してresources\lang\en\auth.php
に記載されたfailed
が設定されます。
// 入力された認証情報に一致するレコードがありません
'failed' => 'These credentials do not match our records.',
こちらもfailed
メッセージの内容を書き換えれば、好きなログイン失敗メッセージを設定できますね。
まとめ
ログイン処理を細かく追いかけることで、「ログイン処理で何が起きているか」「何をもってログイン状態とするか」について理解することができました。
どこで何を行ってログイン処理が成り立っているかが分かったので、特定の機能をカスタマイズするときに実装する必要がある箇所の目星がつけやすくなりました。
ログインと同時に自動生成されるパスワードリセット処理についても、時間を作ってそちらも追いかけてみたいと思います