#はじめに
多くのウェブアプリケーションは、ユーザーがアプリケーションに認証して「ログイン」できる仕組みを提供しています。この機能を実装するのは複雑でリスクを伴う場合があります。そのため、Laravelは認証を迅速かつ安全に、簡単に実装できるツールを提供しています。
Laravelの認証機能は基本的に「ガード」と「プロバイダー」で構成されています。ガードは各リクエストでユーザーをどのように認証するかを定義します。例えば、Laravelにはセッションストレージとクッキーを使って状態を管理するsessionガードが標準で用意されています。
プロバイダーは永続的なストレージからユーザーをどのように取得するかを定義します。LaravelはEloquentやデータベースクエリビルダーを使ったユーザー取得をサポートしていますが、必要に応じて独自のプロバイダーを定義することも可能です。
アプリケーションの認証設定ファイルはconfig/auth.phpにあります。このファイルにはLaravelの認証サービスの動作を調整するための詳細なオプションが含まれています。
ガードとプロバイダーは「ロール」や「パーミッション」とは異なります。パーミッションによるユーザー操作の認可については、authorizationのドキュメントを参照してください。
#スターターキット
すぐに始めたい場合は、新しいLaravelアプリケーションにLaravelアプリケーションスターターキットをインストールしてください。データベースのマイグレーション後、ブラウザで/registerやアプリケーションに割り当てられた任意のURLにアクセスすると、スターターキットが認証システム全体のスキャフォールドを自動で行います。
最終的にスターターキットを使わない場合でも、Laravel Breezeをインストールすることで、Laravelの認証機能を実際のプロジェクトでどのように実装するか学ぶ良い機会になります。 Laravel Breezeは認証コントローラー、ルート、ビューを生成するため、これらのファイルのコードを調べることでLaravelの認証機能の実装方法を理解できます。
#データベースの考慮事項
デフォルトでLaravelはapp/ModelsディレクトリにApp\Models\UserというEloquentモデルを含みます。このモデルはデフォルトのEloquent認証ドライバーで使用できます。Eloquentを使わない場合は、Laravelのクエリビルダーを使うdatabase認証プロバイダーを利用できます。
App\Models\Userモデルのデータベーススキーマを作成する際、パスワードカラムは少なくとも60文字以上にしてください。もちろん、新しいLaravelアプリケーションに含まれるusersテーブルのマイグレーションはすでにこの長さを超えるカラムを作成しています。
また、users(または同等の)テーブルに100文字のnullableな文字列型remember_tokenカラムがあることを確認してください。このカラムは「ログイン状態を保持する」オプションを選択したユーザーのトークンを保存するために使われます。こちらも新しいLaravelアプリケーションのデフォルトのusersテーブルマイグレーションに含まれています。
#エコシステムの概要
Laravelは認証に関連する複数のパッケージを提供しています。ここではLaravelの認証エコシステム全体を概観し、それぞれのパッケージの目的を説明します。
まず認証の仕組みを考えましょう。ウェブブラウザを使う場合、ユーザーはログインフォームでユーザー名とパスワードを入力します。認証情報が正しければ、アプリケーションは認証済みユーザーの情報をユーザーのセッションに保存します。ブラウザに発行されるクッキーにはセッションIDが含まれ、以降のリクエストでユーザーと正しいセッションを関連付けます。セッションIDを受け取ったアプリケーションはセッションデータを取得し、認証情報がセッションに保存されていることを確認してユーザーを「認証済み」とみなします。
リモートサービスがAPIにアクセスする際は、ウェブブラウザがないため通常クッキーは使いません。代わりにリモートサービスはAPIトークンを各リクエストに送信します。アプリケーションは有効なAPIトークンのテーブルと照合し、そのトークンに紐づくユーザーとしてリクエストを「認証」します。
#Laravelの組み込みブラウザ認証サービス
Laravelには通常AuthとSessionファサードを通じてアクセスする組み込みの認証およびセッションサービスがあります。これらはウェブブラウザからのリクエストに対してクッキーを使った認証を提供します。ユーザーの認証情報を検証し認証するメソッドを備え、認証データを自動的にユーザーのセッションに保存し、セッション用クッキーを発行します。これらのサービスの使い方は本ドキュメントで説明しています。
アプリケーションスターターキット
本ドキュメントで説明したように、これらの認証サービスを手動で操作して独自の認証レイヤーを構築できますが、より早く始められるように、認証レイヤー全体の堅牢でモダンなスキャフォールドを提供する無料パッケージを公開しています。これらはLaravel Breeze、Laravel Jetstream、Laravel Fortifyです。
_Laravel Breeze_はログイン、登録、パスワードリセット、メール認証、パスワード確認を含むLaravelの認証機能をシンプルかつ最小限に実装したものです。ビューはTailwind CSSでスタイルされたシンプルなBladeテンプレートで構成されています。始めるにはLaravelのアプリケーションスターターキットのドキュメントを参照してください。
_Laravel Fortify_はLaravelのヘッドレス認証バックエンドで、クッキー認証や二要素認証、メール認証など本ドキュメントで説明する多くの機能を実装しています。FortifyはLaravel Jetstreamの認証バックエンドとして使われるほか、Laravel Sanctumと組み合わせてSPAの認証にも単独で利用できます。
_Laravel Jetstream_はLaravel Fortifyの認証サービスを利用し、Tailwind CSS、Livewire、および/またはInertiaによる美しくモダンなUIを提供する堅牢なアプリケーションスターターキットです。二要素認証、チームサポート、ブラウザセッション管理、プロフィール管理、Laravel Sanctumとの統合によるAPIトークン認証もオプションで含まれています。LaravelのAPI認証については後述します。
#LaravelのAPI認証サービス
LaravelはAPIトークンの管理とAPIトークンを使ったリクエスト認証を支援する2つのオプションパッケージ、PassportとSanctumを提供しています。これらのライブラリとLaravelの組み込みクッキー認証は排他的ではありません。これらのライブラリは主にAPIトークン認証に特化し、組み込み認証サービスはクッキーを使ったブラウザ認証に特化しています。多くのアプリケーションは両方を併用します。
Passport
PassportはOAuth2認証プロバイダーで、様々なOAuth2「グラントタイプ」を提供し多様なトークン発行を可能にします。一般的にAPI認証において強力かつ複雑なパッケージですが、多くのアプリケーションはOAuth2仕様の複雑な機能を必要とせず、ユーザーや開発者にとって混乱の元となっています。特にSPAやモバイルアプリのOAuth2認証プロバイダーとしての使い方は歴史的に混乱が多いです。
Sanctum
OAuth2の複雑さと開発者の混乱に対応するため、ウェブブラウザからのファーストパーティリクエストとAPIトークンを使ったリクエストの両方を扱えるシンプルで効率的な認証パッケージを開発しました。これがLaravel Sanctumであり、APIに加えてファーストパーティのウェブUIを提供するアプリケーションや、バックエンドLaravelアプリケーションとは別に存在するSPA、モバイルクライアントを持つアプリケーションに推奨される認証パッケージです。
Laravel SanctumはウェブとAPIのハイブリッド認証パッケージで、アプリケーションの認証プロセス全体を管理できます。Sanctumベースのアプリケーションはリクエストを受け取ると、まずセッションクッキーが認証済みセッションを参照しているかを判定します。これは先に説明したLaravelの組み込み認証サービスを呼び出すことで実現しています。セッションクッキーで認証されていない場合はAPIトークンを探し、存在すればそのトークンで認証します。この仕組みの詳細はSanctumの"仕組み"を参照してください。
Laravel SanctumはLaravel Jetstreamスターターキットに含まれているAPIパッケージで、多くのウェブアプリケーションの認証ニーズに最適だと考えています。
#まとめとスタックの選択
まとめると、ブラウザからアクセスされるモノリシックなLaravelアプリケーションの場合は、Laravelの組み込み認証サービスを使います。
次に、アプリケーションがサードパーティによって利用されるAPIを提供する場合は、APIトークン認証を実装するためにPassportかSanctumのどちらかを選択します。一般的に、SanctumはAPI認証、SPA認証、モバイル認証をシンプルかつ包括的にサポートし、「スコープ」や「アビリティ」も扱えるため、可能な限り推奨されます。
Laravelのバックエンドで動作するシングルページアプリケーション(SPA)を構築する場合は、Laravel Sanctumを使用すべきです。Sanctumを使う場合は、独自にバックエンドの認証ルートを実装するか、登録、パスワードリセット、メール認証などの機能を提供するヘッドレス認証バックエンドサービスとしてLaravel Fortifyを利用します。
OAuth2仕様が提供するすべての機能が絶対に必要な場合は、Passportを選択できます。
すぐに始めたい場合は、Laravelの組み込み認証サービスとLaravel Sanctumを使った推奨認証スタックを備えた新しいLaravelアプリケーションを素早く開始できるLaravel Breezeをおすすめします。
#認証クイックスタート
このドキュメントの部分では、Laravelアプリケーションのスターターキットを使ったユーザー認証について説明しています。これにはすぐに始められるUIスキャフォールドが含まれます。Laravelの認証システムを直接統合したい場合は、手動でユーザーを認証する方法のドキュメントを参照してください。
#スターターキットのインストール
まず、Laravelアプリケーションのスターターキットをインストールしてください。現在のスターターキットであるLaravel BreezeとLaravel Jetstreamは、新しいLaravelアプリケーションに認証を組み込むための美しい出発点を提供します。
Laravel Breezeは、ログイン、登録、パスワードリセット、メール認証、パスワード確認など、Laravelの認証機能を最小限かつシンプルに実装したものです。ビュー層はシンプルなBladeテンプレートで構成され、Tailwind CSSでスタイリングされています。さらに、BreezeはLivewireまたはInertiaをベースにしたスキャフォールドオプションを提供し、Inertiaベースの場合はVueかReactを選択できます。
Laravel Jetstreamはより強力なスターターキットで、LivewireまたはInertiaとVueを使ったスキャフォールドをサポートします。さらに、Jetstreamは二要素認証、チーム管理、プロフィール管理、ブラウザセッション管理、Laravel SanctumによるAPIサポート、アカウント削除などのオプション機能も備えています。
#認証済みユーザーの取得
認証スターターキットをインストールし、ユーザーが登録・認証できるようにした後は、現在認証されているユーザーとやり取りする必要がよくあります。リクエスト処理中に、Authファサードのuserメソッドを使って認証ユーザーにアクセスできます:
use Illuminate\Support\Facades\Auth;
// 現在認証されているユーザーを取得...
$user = Auth::user();
// 現在認証されているユーザーのIDを取得...
$id = Auth::id();
または、ユーザーが認証された後は、Illuminate\Http\Requestインスタンスを通じて認証ユーザーにアクセスできます。型宣言されたクラスはコントローラーのメソッドに自動的に注入されるため、Illuminate\Http\Requestオブジェクトを型宣言することで、アプリケーション内の任意のコントローラーメソッドからリクエストのuserメソッドを使って認証ユーザーに簡単にアクセスできます:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class FlightController extends Controller
{
/**
* 既存のフライト情報を更新します。
*/
public function update(Request $request): RedirectResponse
{
$user = $request->user();
// ...
return redirect('/flights');
}
}
#現在のユーザーが認証されているか判定する
HTTPリクエストを送信しているユーザーが認証済みかどうかを判定するには、Authファサードのcheckメソッドを使います。このメソッドは認証されていればtrueを返します:
use Illuminate\Support\Facades\Auth;
if (Auth::check()) {
// ユーザーはログインしています...
}
checkメソッドでユーザーの認証状態を判定できますが、通常はミドルウェアを使って認証済みかどうかを検証し、特定のルートやコントローラーへのアクセスを制限します。詳しくはルートの保護のドキュメントを参照してください。
#ルートの保護
ルートミドルウェアを使うと、認証済みユーザーだけが特定のルートにアクセスできるようにできます。Laravelにはauthミドルウェアが用意されており、これはIlluminate\Auth\Middleware\Authenticateクラスを参照しています。このミドルウェアはアプリケーションのHTTPカーネルに登録済みなので、ルート定義にミドルウェアを付けるだけで使えます:
Route::get('/flights', function () {
// 認証済みユーザーのみがこのルートにアクセスできます...
})->middleware('auth');
#未認証ユーザーのリダイレクト
authミドルウェアが未認証ユーザーを検出すると、そのユーザーをloginの名前付きルートにリダイレクトします。この動作はアプリケーションのapp/Http/Middleware/Authenticate.phpファイル内のredirectTo関数を更新することで変更できます:
use Illuminate\Http\Request;
/**
* ユーザーをリダイレクトすべきパスを取得します。
*/
protected function redirectTo(Request $request): string
{
return route('login');
}
#ガードの指定
authミドルウェアをルートに付ける際、どの「ガード」を使ってユーザーを認証するか指定できます。指定するガードはauth.php設定ファイルのguards配列のキーのいずれかに対応している必要があります:
Route::get('/flights', function () {
// 認証済みユーザーのみがこのルートにアクセスできます...
})->middleware('auth:admin');
#ログイン試行のスロットリング
Laravel BreezeやLaravel Jetstreamのスターターキットを使っている場合、ログイン試行に自動的にレート制限が適用されます。デフォルトでは、数回の失敗後に正しい認証情報が提供されなければ、1分間ログインできなくなります。このスロットリングはユーザーのユーザー名/メールアドレスとIPアドレスに基づいています。
アプリケーション内の他のルートに対してレート制限をかけたい場合は、レート制限のドキュメントを参照してください。
#手動でユーザーを認証する
Laravelのアプリケーションスターターキットに含まれる認証スキャフォールドを使う必要はありません。これを使わない場合は、Laravelの認証クラスを直接使ってユーザー認証を管理する必要があります。心配いりません、とても簡単です!
Laravelの認証サービスにはAuthのファサードを通じてアクセスするので、クラスの先頭でAuthファサードをインポートする必要があります。次にattemptメソッドを見てみましょう。attemptメソッドは通常、アプリケーションの「ログイン」フォームからの認証試行を処理するために使います。認証に成功したら、セッションを再生成してセッション固定攻撃を防ぎます:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* 認証試行を処理します。
*/
public function authenticate(Request $request): RedirectResponse
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('dashboard');
}
return back()->withErrors([
'email' => '提供された認証情報が記録と一致しません。',
])->onlyInput('email');
}
}
attemptメソッドは最初の引数にキー/値の配列を受け取ります。この配列の値はデータベースのユーザーテーブルからユーザーを検索するために使われます。上記の例では、emailカラムの値でユーザーを取得します。ユーザーが見つかると、データベースに保存されたハッシュ化されたパスワードと、配列で渡されたpassword値が比較されます。リクエストのpassword値はハッシュ化しないでください。フレームワークが自動的にハッシュ化して比較します。ハッシュ化されたパスワードが一致すれば、認証済みセッションが開始されます。
Laravelの認証サービスは、認証ガードの「プロバイダー」設定に基づいてデータベースからユーザーを取得します。デフォルトのconfig/auth.php設定ファイルでは、Eloquentユーザープロバイダーが指定され、ユーザー取得時にApp\Models\Userモデルを使うよう指示されています。アプリケーションの要件に応じてこれらの値は設定ファイルで変更できます。
attemptメソッドは認証に成功するとtrueを返し、失敗するとfalseを返します。
Laravelのリダイレクターが提供するintendedメソッドは、認証ミドルウェアにより遮断される前にユーザーがアクセスしようとしていたURLにリダイレクトします。意図した遷移先が利用できない場合のフォールバックURIも指定できます。
#追加条件の指定
必要に応じて、ユーザーのメールアドレスとパスワードに加えて認証クエリに追加の条件を付けることもできます。これを実現するには、attemptメソッドに渡す配列に条件を追加するだけです。例えば、ユーザーが「active」とマークされていることを確認できます:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// 認証に成功しました...
}
複雑なクエリ条件の場合は、認証情報の配列にクロージャを渡せます。このクロージャはクエリインスタンスを受け取り、アプリケーションの要件に応じてクエリをカスタマイズできます:
use Illuminate\Database\Eloquent\Builder;
if (Auth::attempt([
'email' => $email,
'password' => $password,
fn (Builder $query) => $query->has('activeSubscription'),
])) {
// 認証に成功しました...
}
これらの例ではemailは必須オプションではなく、単なる例です。データベースの「ユーザー名」に対応するカラム名を使ってください。
attemptWhenメソッドは第2引数にクロージャを受け取り、実際にユーザーを認証する前により詳細な検査を行えます。クロージャは認証候補のユーザーを受け取り、認証可能かどうかを示すtrueまたはfalseを返す必要があります:
if (Auth::attemptWhen([
'email' => $email,
'password' => $password,
], function (User $user) {
return $user->isNotBanned();
})) {
// 認証に成功しました...
}
#特定のガードインスタンスへのアクセス
Authファサードのguardメソッドを使うことで、認証時に使用するガードインスタンスを指定できます。これにより、異なる認証可能モデルやユーザーテーブルを使って、アプリケーションの別々の部分の認証を管理できます。
guardメソッドに渡すガード名は、auth.php設定ファイルで構成されているガードのいずれかに対応している必要があります。
if (Auth::guard('admin')->attempt($credentials)) {
// ...
}
#ユーザーの記憶(Remembering Users)
多くのウェブアプリケーションでは、ログインフォームに「ログイン状態を保持する」チェックボックスを設けています。アプリケーションでこの機能を提供したい場合、attemptメソッドの第2引数に真偽値を渡せます。
この値がtrueの場合、Laravelはユーザーを無期限に認証状態に保つか、ユーザーが手動でログアウトするまで認証を維持します。usersテーブルにはremember_tokenという文字列カラムが必要で、ここに「ログイン状態を保持する」トークンが保存されます。新規Laravelアプリケーションに含まれるusersテーブルのマイグレーションにはすでにこのカラムが含まれています。
use Illuminate\Support\Facades\Auth;
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// ユーザーが記憶されています...
}
アプリケーションで「ログイン状態を保持する」機能を提供している場合、viaRememberメソッドを使って現在認証されているユーザーが「ログイン状態を保持する」クッキーを使って認証されたかどうかを判定できます。
use Illuminate\Support\Facades\Auth;
if (Auth::viaRemember()) {
// ...
}
#その他の認証方法
#ユーザーインスタンスの認証
既存のユーザーインスタンスを現在の認証ユーザーとして設定したい場合、Authファサードのloginメソッドにユーザーインスタンスを渡せます。渡すユーザーインスタンスはIlluminate\Contracts\Auth\Authenticatable 契約を実装している必要があります。Laravelに含まれるApp\Models\Userモデルはすでにこのインターフェースを実装しています。この認証方法は、ユーザー登録直後など、すでに有効なユーザーインスタンスがある場合に便利です。
use Illuminate\Support\Facades\Auth;
Auth::login($user);
loginメソッドの第2引数に真偽値を渡すこともできます。この値は認証セッションに「ログイン状態を保持する」機能を適用するかどうかを示します。つまり、セッションは無期限に認証された状態を維持するか、ユーザーが手動でログアウトするまで認証が続きます。
Auth::login($user, $remember = true);
必要に応じて、loginメソッドを呼ぶ前に認証ガードを指定できます。
Auth::guard('admin')->login($user);
#ユーザーIDによる認証
データベースの主キーを使ってユーザーを認証したい場合、loginUsingIdメソッドを使えます。このメソッドは認証したいユーザーの主キーを受け取ります。
Auth::loginUsingId(1);
loginUsingIdメソッドの第2引数に真偽値を渡すこともできます。この値は認証セッションに「ログイン状態を保持する」機能を適用するかどうかを示します。つまり、セッションは無期限に認証された状態を維持するか、ユーザーが手動でログアウトするまで認証が続きます。
Auth::loginUsingId(1, $remember = true);
#一時的なユーザー認証
onceメソッドを使うと、単一リクエストに対してユーザーを認証できます。このメソッドを使うとセッションやクッキーは使用されません。
if (Auth::once($credentials)) {
// ...
}
#HTTP Basic認証
HTTP Basic認証は、専用の「ログイン」ページを用意せずにアプリケーションのユーザーを認証する簡単な方法です。始めるには、ルートにauth.basic ミドルウェアを適用します。auth.basicミドルウェアはLaravelフレームワークに含まれているため、定義は不要です。
Route::get('/profile', function () {
// 認証済みユーザーのみがこのルートにアクセスできます...
})->middleware('auth.basic');
ミドルウェアをルートに適用すると、ブラウザでそのルートにアクセスした際に自動的に認証情報の入力を求められます。デフォルトでは、auth.basicミドルウェアはusersテーブルのemailカラムをユーザー名として扱います。
#FastCGIに関する注意
PHP FastCGIとApacheでLaravelアプリケーションを提供している場合、HTTP Basic認証が正しく動作しないことがあります。この問題を解決するには、アプリケーションの.htaccessファイルに以下の行を追加してください。
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
#ステートレスなHTTP Basic認証
HTTP Basic認証を使ってセッションにユーザー識別子のクッキーを設定せずに認証することもできます。これは主にAPIへのリクエスト認証にHTTP認証を使う場合に便利です。これを実現するには、onceBasicメソッドを呼ぶミドルウェアを定義します。onceBasicメソッドがレスポンスを返さなければ、リクエストはアプリケーションに渡されます。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class AuthenticateOnceWithBasicAuth
{
/**
* 受信リクエストを処理します。
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return Auth::onceBasic() ?: $next($request);
}
}
次に、このミドルウェアをルートに適用します。
Route::get('/api/user', function () {
// 認証済みユーザーのみがこのルートにアクセスできます...
})->middleware(AuthenticateOnceWithBasicAuth::class);
#ログアウト
ユーザーを手動でログアウトさせるには、Authファサードのlogoutメソッドを使います。これによりユーザーのセッションから認証情報が削除され、以降のリクエストは認証されなくなります。
logoutメソッドを呼んだ後は、ユーザーのセッションを無効化し、CSRFトークンを再生成することを推奨します。通常はログアウト後にアプリケーションのルートにリダイレクトします。
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
/**
* アプリケーションからユーザーをログアウトさせます。
*/
public function logout(Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
#他デバイスのセッション無効化
Laravelは、現在のデバイスのセッションは維持しつつ、他のデバイスでアクティブなユーザーのセッションを無効化(ログアウト)する仕組みも提供しています。この機能は、ユーザーがパスワードを変更・更新する際に、他のデバイスのセッションを無効化しつつ現在のデバイスは認証状態のままにしたい場合に使います。
始める前に、Illuminate\Session\Middleware\AuthenticateSessionミドルウェアがセッション認証を必要とするルートに含まれていることを確認してください。通常はルートグループにこのミドルウェアを適用し、アプリケーションのほとんどのルートに適用します。デフォルトでは、AuthenticateSessionミドルウェアはアプリケーションのHTTPカーネルでauth.sessionというルートミドルウェアエイリアスとして定義されています。
Route::middleware(['auth', 'auth.session'])->group(function () {
Route::get('/', function () {
// ...
});
});
その後、AuthファサードのlogoutOtherDevicesメソッドを使えます。このメソッドはユーザーに現在のパスワードの確認を求めます。パスワードはアプリケーションの入力フォームで受け取る必要があります。
use Illuminate\Support\Facades\Auth;
Auth::logoutOtherDevices($currentPassword);
logoutOtherDevicesメソッドを呼ぶと、ユーザーの他のセッションは完全に無効化され、以前認証されていたすべてのガードからログアウトされます。
#パスワード確認
アプリケーション開発中に、特定の操作を実行する前や、ユーザーを機密性の高いエリアにリダイレクトする前にパスワードの確認を求めたい場合があります。Laravelはこの処理を簡単にする組み込みミドルウェアを提供しています。この機能を実装するには、パスワード確認を求めるビューを表示するルートと、パスワードの有効性を検証してユーザーを目的の場所にリダイレクトするルートの2つを定義します。
以下のドキュメントはLaravelのパスワード確認機能との直接的な統合方法を説明していますが、より簡単に始めたい場合はLaravelアプリケーションのスターターキットにこの機能のサポートが含まれています!
#設定
パスワードを確認すると、ユーザーは3時間の間再度パスワード確認を求められません。ただし、config/auth.php設定ファイルのpassword_timeout値を変更することで、再確認までの時間を調整できます。
#ルーティング
#パスワード確認フォーム
まず、ユーザーにパスワード確認を求めるビューを表示するルートを定義します。
Route::get('/confirm-password', function () {
return view('auth.confirm-password');
})->middleware('auth')->name('password.confirm');
予想通り、このルートが返すビューにはpasswordフィールドを含むフォームが必要です。加えて、ユーザーが保護されたエリアに入ろうとしていることやパスワード確認が必要であることを説明するテキストをビューに含めても構いません。
#パスワードの確認
次に、「パスワード確認」ビューからのフォームリクエストを処理するルートを定義します。このルートはパスワードの検証と、ユーザーを目的の場所にリダイレクトする役割を担います。
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;
Route::post('/confirm-password', function (Request $request) {
if (! Hash::check($request->password, $request->user()->password)) {
return back()->withErrors([
'password' => ['入力されたパスワードが記録と一致しません。']
]);
}
$request->session()->passwordConfirmed();
return redirect()->intended();
})->middleware(['auth', 'throttle:6,1']);
このルートを詳しく見てみましょう。まず、リクエストのpasswordフィールドが認証済みユーザーのパスワードと一致するか確認します。パスワードが有効なら、Laravelのセッションにユーザーがパスワードを確認したことを知らせる必要があります。passwordConfirmedメソッドは、ユーザーのセッションにタイムスタンプを設定し、Laravelが最後にパスワード確認した時刻を判定できるようにします。最後に、ユーザーを目的の場所にリダイレクトします。
#ルートの保護
パスワードの再確認が必要な操作を行うルートには、必ず password.confirm ミドルウェアを割り当ててください。このミドルウェアはLaravelのデフォルトインストールに含まれており、ユーザーの意図した遷移先をセッションに自動的に保存します。これにより、パスワード確認後にその場所へリダイレクトできます。ユーザーの遷移先をセッションに保存した後、ミドルウェアは password.confirm のnamed routeへリダイレクトします:
Route::get('/settings', function () {
// ...
})->middleware(['password.confirm']);
Route::post('/settings', function () {
// ...
})->middleware(['password.confirm']);
#カスタムガードの追加
Auth ファサードの extend メソッドを使って独自の認証ガードを定義できます。extend メソッドの呼び出しはサービスプロバイダー内に記述してください。Laravelにはすでに AuthServiceProvider があるので、そのプロバイダーにコードを追加できます:
<?php
namespace App\Providers;
use App\Services\Auth\JwtGuard;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
class AuthServiceProvider extends ServiceProvider
{
/**
* アプリケーションの認証/認可サービスを登録します。
*/
public function boot(): void
{
Auth::extend('jwt', function (Application $app, string $name, array $config) {
// Illuminate\Contracts\Auth\Guard のインスタンスを返します...
return new JwtGuard(Auth::createUserProvider($config['provider']));
});
}
}
上記の例のように、extend メソッドに渡すコールバックは Illuminate\Contracts\Auth\Guard の実装を返す必要があります。このインターフェイスにはカスタムガードを定義するために実装すべきメソッドがいくつか含まれています。カスタムガードを定義したら、auth.php の guards 設定でそのガードを参照できます:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
#クロージャリクエストガード
HTTPリクエストベースのカスタム認証システムを実装する最も簡単な方法は、Auth::viaRequest メソッドを使うことです。このメソッドを使うと、単一のクロージャで認証処理を素早く定義できます。
はじめに、AuthServiceProvider の boot メソッド内で Auth::viaRequest を呼び出します。viaRequest は最初の引数に認証ドライバー名を受け取ります。この名前はカスタムガードを表す任意の文字列で構いません。第二引数には、HTTPリクエストを受け取り、ユーザーインスタンスを返すか認証失敗時は null を返すクロージャを渡します:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* アプリケーションの認証/認可サービスを登録します。
*/
public function boot(): void
{
Auth::viaRequest('custom-token', function (Request $request) {
return User::where('token', (string) $request->token)->first();
});
}
カスタム認証ドライバーを定義したら、auth.php の guards 設定でドライバーとして設定できます:
'guards' => [
'api' => [
'driver' => 'custom-token',
],
],
最後に、認証ミドルウェアをルートに割り当てる際にこのガードを参照できます:
Route::middleware('auth:api')->group(function () {
// ...
});
#カスタムユーザープロバイダーの追加
従来のリレーショナルデータベースを使わずにユーザーを管理する場合は、独自の認証ユーザープロバイダーをLaravelに拡張する必要があります。Auth ファサードの provider メソッドを使ってカスタムユーザープロバイダーを定義します。ユーザープロバイダーのリゾルバーは Illuminate\Contracts\Auth\UserProvider の実装を返す必要があります:
<?php
namespace App\Providers;
use App\Extensions\MongoUserProvider;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
class AuthServiceProvider extends ServiceProvider
{
/**
* アプリケーションの認証/認可サービスを登録します。
*/
public function boot(): void
{
Auth::provider('mongo', function (Application $app, array $config) {
// Illuminate\Contracts\Auth\UserProvider のインスタンスを返します...
return new MongoUserProvider($app->make('mongo.connection'));
});
}
}
provider メソッドでプロバイダーを登録したら、auth.php の設定で新しいユーザープロバイダーに切り替えられます。まず、新しいドライバーを使う provider を定義します:
'providers' => [
'users' => [
'driver' => 'mongo',
],
],
最後に、このプロバイダーを guards 設定で参照します:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
#ユーザープロバイダー契約
Illuminate\Contracts\Auth\UserProvider の実装は、MySQLやMongoDBなどの永続ストレージから Illuminate\Contracts\Auth\Authenticatable の実装を取得する役割を担います。この2つのインターフェイスにより、ユーザーデータの保存方法や認証ユーザーを表すクラスの種類に関わらず、Laravelの認証機構が動作し続けます。
Illuminate\Contracts\Auth\UserProvider 契約を見てみましょう:
<?php
namespace Illuminate\Contracts\Auth;
interface UserProvider
{
public function retrieveById($identifier);
public function retrieveByToken($identifier, $token);
public function updateRememberToken(Authenticatable $user, $token);
public function retrieveByCredentials(array $credentials);
public function validateCredentials(Authenticatable $user, array $credentials);
}
retrieveById 関数は通常、MySQLの自動増分IDなどユーザーを表すキーを受け取ります。このIDに対応する Authenticatable の実装を取得して返すべきです。
retrieveByToken 関数は、ユニークな $identifier と「remember me」用の $token でユーザーを取得します。通常は remember_token のようなデータベースカラムに保存されています。前述のメソッド同様、トークンが一致する Authenticatable の実装を返します。
updateRememberToken メソッドは、$user インスタンスの remember_token を新しい $token に更新します。成功した「remember me」認証やログアウト時に新しいトークンが割り当てられます。
retrieveByCredentials メソッドは、Auth::attempt メソッドに渡された認証情報の配列を受け取ります。このメソッドは、認証情報に合致するユーザーを永続ストレージから「クエリ」して取得します。通常は $credentials['username'] に一致するユーザーレコードを検索する「where」条件のクエリを実行します。Authenticatable の実装を返すべきです。このメソッドはパスワードの検証や認証を行ってはいけません。
validateCredentials メソッドは、与えられた $user と $credentials を比較して認証を行います。例えば、Hash::check メソッドを使って $user->getAuthPassword() と $credentials['password'] の値を比較します。パスワードが有効かどうかを示す true または false を返します。
#Authenticatable契約
UserProvider の各メソッドを見たので、次は Authenticatable 契約を見てみましょう。ユーザープロバイダーは retrieveById、retrieveByToken、retrieveByCredentials メソッドでこのインターフェイスの実装を返す必要があります:
<?php
namespace Illuminate\Contracts\Auth;
interface Authenticatable
{
public function getAuthIdentifierName();
public function getAuthIdentifier();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
}
このインターフェイスはシンプルです。getAuthIdentifierName メソッドはユーザーの「主キー」フィールド名を返し、getAuthIdentifier はユーザーの「主キー」値を返します。MySQLを使う場合は、ユーザーレコードに割り当てられた自動増分の主キーが該当します。getAuthPassword はユーザーのハッシュ化されたパスワードを返します。
このインターフェイスにより、どのようなORMやストレージ抽象化レイヤーを使っていても認証システムが動作します。Laravelはデフォルトで app/Models ディレクトリにこのインターフェイスを実装した App\Models\User クラスを含んでいます。
#イベント
Laravelは認証プロセス中にさまざまなイベントを発行します。これらのイベントにリスナーを EventServiceProvider で登録できます:
/**
* アプリケーションのイベントリスナーのマッピング。
*
* @var array
*/
protected $listen = [
'Illuminate\Auth\Events\Registered' => [
'App\Listeners\LogRegisteredUser',
],
'Illuminate\Auth\Events\Attempting' => [
'App\Listeners\LogAuthenticationAttempt',
],
'Illuminate\Auth\Events\Authenticated' => [
'App\Listeners\LogAuthenticated',
],
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
'Illuminate\Auth\Events\Failed' => [
'App\Listeners\LogFailedLogin',
],
'Illuminate\Auth\Events\Validated' => [
'App\Listeners\LogValidated',
],
'Illuminate\Auth\Events\Verified' => [
'App\Listeners\LogVerified',
],
'Illuminate\Auth\Events\Logout' => [
'App\Listeners\LogSuccessfulLogout',
],
'Illuminate\Auth\Events\CurrentDeviceLogout' => [
'App\Listeners\LogCurrentDeviceLogout',
],
'Illuminate\Auth\Events\OtherDeviceLogout' => [
'App\Listeners\LogOtherDeviceLogout',
],
'Illuminate\Auth\Events\Lockout' => [
'App\Listeners\LogLockout',
],
'Illuminate\Auth\Events\PasswordReset' => [
'App\Listeners\LogPasswordReset',
],
];