Laravel BreezeとInertia.jsで二段階認証を実装してみる
Laravel BreezeとInertia.js(React/TypeScript)で二段階認証を実装してみる
この記事では、Laravel BreezeとInertia.js(React/TypeScript)を使って、二段階認証を含むアプリケーションを構築する手順を紹介します。BreezeはLaravelの軽量な認証パッケージで、非常にシンプルに認証システムをセットアップすることが可能です。
1. プロジェクトのセットアップ
まず、Laravelプロジェクトをセットアップします。
$ curl -s "https://laravel.build/two-factor-auth-demo" | bash
$ cd two-factor-auth-demo
Breezeのインストール
次に、Laravel Breezeをインストールし、ReactとTypeScriptを使用する設定を行います。
$ composer require laravel/breeze --dev
$ yarn install
$ php artisan breeze:install react --typescript
$ composer require inertiajs/inertia-laravel
$ ./vendor/bin/sail up -d
$ ./vendor/bin/sail artisan sail:publish
不要なDocker設定を削除し、mysqlの設定等を行ったら、環境を再構築します。
$ rm -rf docker/8.0 docker/8.1 docker/8.2 docker/mariadb docker/pgsql
$ ./vendor/bin/sail build --no-cache
$ ./vendor/bin/sail up -d
2. テーブルとマイグレーションの設定
二段階認証用にテーブルを追加するため、0001_01_01_000000_create_users_table.php
のマイグレーションファイルを編集します。
Schema::create('users', function (Blueprint $table) {
...
+ $table->string('two_factor_auth_code')->nullable();
+ $table->timestamp('two_factor_auth_code_expires_at')->nullable();
...
});
$ ./vendor/bin/sail artisan migrate
4. ミドルウェアの作成
次に、二段階認証を管理するためのミドルウェアを作成します。
$ ./vendor/bin/sail artisan make:middleware TwoFactorAuthenticateMiddleware
ミドルウェアの内容は、ユーザーがログインした後、二段階認証コードがあるかどうかを確認します。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class TwoFactorAuthenticateMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// 2段階認証コードがなければ、そのまま次の処理に進む
if (!Auth::user()->two_factor_auth_code) return $next($request);
return redirect()->route('two-factor.create');
}
}
5. コントローラーとロジックの実装
二段階認証コードの送信と検証を行うため、TwoFactorAuthenticateController
を作成します。
$ ./vendor/bin/sail artisan make:controller Auth/TwoFactorAuthenticateController
ユーザーがコードを入力する画面の表示や、コードの検証ロジックを実装します。
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
class TwoFactorAuthenticateController extends Controller
{
/**
* 2段階認証コードの入力画面を表示
*/
public function create()
{
if (!Auth::user()->two_factor_auth_code) {
return redirect(route('dashboard', absolute: false));
}
return Inertia::render('Auth/TwoFactorAuthenticate');
}
/**
* 2段階認証コードの検証
*/
public function store(Request $request)
{
$request->validate([
'two_factor_auth_code' => 'required|string',
]);
$now = Carbon::now();
$user = User::find(Auth::id());
$two_factor_auth_code = $user->two_factor_auth_code;
$two_factor_expires_at = new Carbon($user->two_factor_auth_code_expires_at);
// 2段階認証コードが一致し、有効期限内であれば、2段階認証を有効にする
if ($request->two_factor_auth_code === $two_factor_auth_code && $now->lt($two_factor_expires_at)) {
$user->two_factor_auth_code = null;
$user->two_factor_auth_code_expires_at = null;
$user->save();
return redirect(route('dashboard', absolute: false));
} else {
return redirect(route('two-factor-authenticate.create', absolute: false));
}
}
}
6. ユーティリティクラスでメール送信処理を作成
二段階認証コードの生成とメール送信を行うUtils/TwoFactorAuthenticate.php
クラスを作成します。
<?php
namespace App\Utils;
use Illuminate\Support\Facades\Mail;
use App\Mail\TwoFactorAuthenticateMail;
use App\Models\User;
use Carbon\Carbon;
class TwoFactorAuthenticate {
/**
* 2段階認証コードを生成してメール送信
*/
public static function sendMail(User $user): void
{
// 2段階認証コードを生成
$twoFactorAuthCode = random_int(100000, 999999);
// 2段階認証コードをDBに保存
$user->two_factor_auth_code = $twoFactorAuthCode;
$user->two_factor_auth_code_expires_at = Carbon::now()->addMinutes(10);
$user->save();
// メール送信
Mail::to($user->email)->send(new TwoFactorAuthenticateMail($twoFactorAuthCode));
}
}
7. 認証と登録プロセスの調整
最後に、ユーザーがログインや新規登録時に二段階認証メールを送信するようにします。
use App\Utils\TwoFactorAuthenticate;
を追加して、それぞれ下記メソッドに追記します。
public function store(LoginRequest $request): RedirectResponse
{
$request->authenticate();
$request->session()->regenerate();
$user = Auth::user();
+ TwoFactorAuthenticate::sendMail($user);
return redirect()->intended(route('dashboard', absolute: false));
}
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|lowercase|email|max:255|unique:'.User::class,
'password' => ['required', 'confirmed', Rules\Password::defaults()],
'tel' => 'required|string|max:255',
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'tel' => $request->tel
]);
event(new Registered($user));
Auth::login($user);
+ TwoFactorAuthenticate::sendMail($user);
return redirect(route('dashboard', absolute: false));
}
8. ルーティングの調整
二段階認証を管理するためのミドルウェア(TwoFactorAuthenticateMiddleware
)を/dashboard
と/logout
に追加します。
+use App\Http\Middleware\TwoFactorAuthenticateMiddleware;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware([
'auth',
'verified',
+ TwoFactorAuthenticateMiddleware::class
])->name('dashboard');
require __DIR__.'/auth.php';
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\RegisteredUserController;
use App\Http\Controllers\Auth\TwoFactorAuthenticateController;
+use App\Http\Middleware\TwoFactorAuthenticateMiddleware;
use Illuminate\Support\Facades\Route;
Route::middleware('guest')->group(function () {
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::middleware('auth')->group(function () {
Route::get('two-factor', [TwoFactorAuthenticateController::class, 'create'])
->name('two-factor.create');
Route::post('two-factor', [TwoFactorAuthenticateController::class, 'store'])
->name('two-factor.store');
// 2段階認証済みのユーザーのみアクセス可能
+ Route::middleware([TwoFactorAuthenticateMiddleware::class])->group(function () {
Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
->name('logout');
});
});
まとめ
この記事では、Laravel Breezeを利用した二段階認証の実装を紹介しました。
今回の記事では主にバックエンドの設定や基本的な認証機能の実装に焦点を当てていますが、フロントエンドのReactコンポーネントの具体的な実装や、メール送信のテンプレートのカスタマイズについては触れていません。これらの詳細な実装は、以下のGitHubリポジトリをご覧ください。
Discussion