Laravel にはデフォルトで認証機能が用意されており、大抵の場合は Laravel Fortify などのパッケージを使用して実装するのが一番ラクです。
ただ、Fortifyを使用してみると分かるのですが、このパッケージはWebサイトに来るユーザーを全員同じ User
モデルとして扱うことを前提としているため、 Multi Auth には対応していません。 また、Fortifyに限らず Laravel ではあまり Multi Auth を推奨しておらず、 Multi Auth を実装した公式パッケージはありません。
そこで、本ブログで Multi Auth の実装方法と、その実装を汎用化する方法についての方法を連載形式で解説したいと思います。 第一回目である本記事では、複数のユーザーを認証するログイン機能を実装します。
実装内容
本記事では User
と Admin
の2モデルを使用して実装します。 Admin
は User
をコピペして作るので、実装にほとんど違いはありません。
アプリケーションのインターフェースですが、APIとWebの両方を実装します。 APIはセッション認証とBearerトークン認証の両方を実装します。Bearerトークン認証の実装には laravel/sanctum を使用します。
認証のURLは、Userは http://localhost/user
から始まり、Adminは http://localhost/admin
から始まるように作ります。
なお、本記事では Laravel 9 を使用しており、既に初期の環境構築は済ませた前提で解説しております。
実装手順
1. Adminクラスを作る
Multi Auth実装のため、まずはデフォルトの User
の他に、認証可能な Authenticatable
モデルとして Admin
を作成します。
Admin
は User
のコードをコピペしてファイル名とクラス名だけ書き換えればOKです。
2. Configファイルに設定を追加する
新しく Admin
モデルを作成したので、 config/auth.php
ファイルに追記します。 また、このときに User
モデルの設定もわかりやすいように少し書き換えます。
<?php
return [
'defaults' => [
// webからuserに書き換え
'guard' => 'user',
'passwords' => 'users',
],
'guards' => [
// guard名をwebからuserに書き換え
'user' => [
'driver' => 'session',
'provider' => 'users',
],
// admin guardを追加
'admin' => [
'driver' => 'session',
'provider' => 'admins',
]
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// admins providerを追加
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
]
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'user_password_resets', // 名前をuser_password_resetsに変更
'expire' => 60,
'throttle' => 60,
],
// admins passwordを追加
'admins' => [
'provider' => 'admins',
'table' => 'admin_password_resets',
'expire' => 60,
'throttle' => 60,
]
],
'password_timeout' => 10800,
];
3. パスワードリセットテーブルを追加&修正
先程のconfigファイルでパスワードリセットテーブルの追加と修正を行ったので、関連する 2014_10_12_100000_create_password_resets_table.php
マイグレーションファイルの方も変更します。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// user_password_resetsに名前変更
Schema::create('user_password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
// admin_password_resetsを追加
Schema::create('admin_password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_password_resets');
Schema::dropIfExists('admin_password_resets');
}
};
4. スキャフォールディング
Multi Authの実装は1から自分で作るのは大変なので、 Laravel Breeze というパッケージを利用して、下地となるコードを生成します。
このコマンドを実行することによって、認証に必要な実装コードが生成されます。
composer require laravel/breeze
php artisan breeze:install
5. Routeを設定する
スキャフォールディングによって routes
ディレクトリの直下に auth.php
ファイルが生成され、そこに認証用のルートが設定されています。 ただ、このルートは使用するguardがデフォルトで固定なため、 Multi Auth にするために修正が必要です。
guestとauthでルートを分ける
まずはguest用ルートとauth用ルートでファイルを分割します。 分割したファイルはわかりやすくするため、 routes/auth
ディレクトリを作成し、その下にそれぞれ guest.php
と auth.php
ファイルを作成します。
<?php
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\NewPasswordController;
use App\Http\Controllers\Auth\PasswordResetLinkController;
use App\Http\Controllers\Auth\RegisteredUserController;
use Illuminate\Support\Facades\Route;
Route::get('register', [RegisteredUserController::class, 'create'])
->name('register');
Route::post('register', [RegisteredUserController::class, 'store']);
Route::get('login', [AuthenticatedSessionController::class, 'create'])
->name('login');
Route::post('login', [AuthenticatedSessionController::class, 'store']);
Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
->name('password.request');
Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])
->name('password.email');
Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])
->name('password.reset');
Route::post('reset-password', [NewPasswordController::class, 'store'])
->name('password.store');
guest.php
<?php
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\ConfirmablePasswordController;
use App\Http\Controllers\Auth\EmailVerificationNotificationController;
use App\Http\Controllers\Auth\EmailVerificationPromptController;
use App\Http\Controllers\Auth\PasswordController;
use App\Http\Controllers\Auth\VerifyEmailController;
use Illuminate\Support\Facades\Route;
Route::get('verify-email', [EmailVerificationPromptController::class, '__invoke'])
->name('verification.notice');
Route::get('verify-email/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
->middleware(['signed', 'throttle:6,1'])
->name('verification.verify');
Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
->middleware('throttle:6,1')
->name('verification.send');
Route::get('confirm-password', [ConfirmablePasswordController::class, 'show'])
->name('password.confirm');
Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']);
Route::put('password', [PasswordController::class, 'update'])->name('password.update');
Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
->name('logout');
auth.php
web.php
ファイルの修正
次に、先程作成した guest.php
と auth.php
を web.php
で読み込みます。 読み込む際にguestとauthミドルウェアを設定することで、使用するguardを使い分けることができ、 Multi Auth 認証ができます。
<?php
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Route::middleware('auth:user')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
Route::prefix('user')->name('user.')->group(function () {
Route::middleware('guest:user')->group(base_path('routes/auth/guest.php'));
Route::middleware('auth:user')->group(base_path('routes/auth/auth.php'));
});
Route::prefix('admin')->name('admin.')->group(function () {
Route::middleware('guest:admin')->group(base_path('routes/auth/guest.php'));
Route::middleware('auth:admin')->group(base_path('routes/auth/auth.php'));
});
login.blade.php
ページの修正
とりあえずログインができるようにページ修正します。
@php
$middleware = last(app('router')->getCurrentRoute()->middleware());
$guard = explode(':', $middleware)[1];
@endphp
<x-guest-layout>
<x-auth-card>
<x-slot name="logo">
<a href="/">
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
</a>
</x-slot>
<!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" />
<form method="POST" action="{{ route("$guard.login") }}">
@csrf
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="current-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Remember Me -->
<div class="block mt-4">
<label for="remember_me" class="inline-flex items-center">
<input id="remember_me" type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500" name="remember">
<span class="ml-2 text-sm text-gray-600">{{ __('Remember me') }}</span>
</label>
</div>
<div class="flex items-center justify-end mt-4">
@if (Route::has('password.request'))
<a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('password.request') }}">
{{ __('Forgot your password?') }}
</a>
@endif
<x-primary-button class="ml-3">
{{ __('Log in') }}
</x-primary-button>
</div>
</form>
</x-auth-card>
</x-guest-layout>
6. 確認
最後に php artisan server
を実行してHTTPアプリケーションサーバーを立ち上げます。
立ち上げ後に以下のuserとadminのログインページにアクセスできるので、それぞれのページにアクセスし、ログインに成功すれば成功です。
まとめ
本記事で基本的なMulti Authの実装方法について解説しました。 ただ、この実装内容ではまだログイン以外のことができないため、次回以降はそれ以外の機能の実装を解説していきます。