概要
next.js・nginx・laravel・mysql環境で開発環境を立ち上げた際、初めてLaravel Sanctumを使ってみることにした。その際の手順を記します。
環境
"php": "^8.2",
"laravel/framework": "^12.0",
"laravel/sanctum": "^4.0",
Sanctum の認証方式
Sanctum には 「SPA認証(クッキー方式)」 と 「Token認証」 の2つの方法がある。
| 認証方式 | 説明 | 使用する機能 |
|---|---|---|
| SPA認証(クッキー方式) | フロントエンドとAPIが同じオリジン(localhost:3000 → localhost:8000 など)で動く場合に最適。ブラウザのセッションを利用する |
sanctum/csrf-cookie |
| Token認証 | APIをモバイルアプリや外部サービスから使う場合に適している。ユーザーごとに発行したアクセストークンを使って認証する | createToken() |
今回の実装では、Token 認証を採用。
理由は、今後勉強のため外部APIサービスを導入する可能性があるから。
例えば、Stripe などの決済API、他のバックエンドサービスとの連携、モバイルアプリからのAPI利用 を想定すると、Token認証の方が適しているため、今回はこの方式を選択した。
Sanctum のセットアップ手順
-
Sanctum をインストール
composer require laravel/sanctum -
Sanctum の公開
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"Sanctum の設定ファイルを
config/sanctum.phpにコピー する処理 -
マイグレーションの実行
php artisan migrate -
App\Models\UserにHasApiTokensトレイトを追加use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; } -
Sanctum のクッキー認証を有効にするための CORS 設定
/config/cors.php
<?php return [ 'paths' => ['api/*'], 'allowed_methods' => ['*'], 'allowed_origins' => ['http://localhost:3000'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['*'], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => false, ];この設定ファイルは
CORS(Cross-Origin Resource Sharing)
のルールを管理するもの。
フロントエンド(Next.js)とバックエンド(Laravel API)が
異なるオリジン(ドメイン)
で動作するとき、APIリクエストを許可するかどうかを決める。
🔥 各設定の意味
設定キー 役割 設定値 説明 'paths' => ['api/*']CORSを適用するAPIのパス ['api/*']api/以下のすべてのAPIリクエストにCORSを適用する'allowed_methods' => ['*']許可するHTTPメソッド ['*']GET,POST,PUT,DELETEなどすべてのリクエストを許可'allowed_origins' => ['http://localhost:3000']許可するオリジン(アクセス元のドメイン) ['http://localhost:3000']Next.js(フロントエンド)の開発サーバーからのリクエストを許可 'allowed_origins_patterns' => []正規表現で許可するオリジン []特定のパターンを許可したい場合に使う(今回は未使用) 'allowed_headers' => ['*']許可するリクエストヘッダー ['*']すべてのヘッダー( Authorization,Content-Typeなど)を許可'exposed_headers' => []クライアントがアクセスできるレスポンスヘッダー []フロントエンド側で見えるレスポンスヘッダー(今回は未使用) 'max_age' => 0プリフライトリクエストのキャッシュ時間 00にすると毎回プリフライトリクエストを送る'supports_credentials' => falseクッキーを含めたリクエストを許可するか? falseToken認証ではクッキーを使わないため false -
仮でAPI設定
/config/auth.php
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'sanctum' => [ 'driver' => 'sanctum', 'provider' => 'users', ], ],bootstrap/app.php
<?php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', // ✅ これを追加して `routes/api.php` を有効化! commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // })->create();Laravel 11 から
routes/api.phpがデフォルトでなくなったので、新規作成する必要があるroutes/api.phpに仮でAPIを設定
<?php use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use App\Http\Controllers\AuthController; Route::post('/login', [AuthController::class, 'login']); Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum'); Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); });app/Http/Controllers/AuthController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use App\Models\User; class AuthController extends Controller { public function login(Request $request) { $credentials = $request->validate([ 'email' => 'required|email', 'password' => 'required' ]); if (!Auth::attempt($credentials)) { return response()->json(['message' => 'Unauthorized'], 401); } $user = Auth::user(); $token = $user->createToken('auth-token')->plainTextToken; return response()->json(['token' => $token, 'user' => $user]); } public function logout(Request $request) { $request->user()->tokens()->delete(); return response()->json(['message' => 'Logged out']); } } -
テストしてみる
適当にテストユーザーを作成
database/seeders/DatabaseSeeder.php
<?php namespace Database\Seeders; use App\Models\User; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\Hash; class DatabaseSeeder extends Seeder { /** * Seed the application's database. */ public function run(): void { // User::factory(10)->create(); User::factory()->create([ 'name' => 'Test User', 'email' => 'test@example.com', 'password' => Hash::make('password'), ]); } }php artisan db:seedtinkerで確認
Psy Shell v0.12.7 (PHP 8.3.17 — cli) by Justin Hileman > $user = \App\Models\User::first(); $user->createToken('test-token')->plainTextToken; = "*********************"トークン作成はOK。
リクエストしてみる
curl -X POST http://localhost:8000/api/login \ -H "Content-Type: application/json" \ -d '{"email": "test@example.com", "password": "password"}'レスポンス
{ "token": "*********************************", "user": { "id": 1, "name": "Test User", "email": "test@example.com", "email_verified_at": "2025-03-08T05:42:05.000000Z", "created_at": "2025-03-08T05:42:05.000000Z", "updated_at": "2025-03-08T05:42:05.000000Z" } }認証成功!
認証確認
% curl -X GET http://localhost:8000/api/user \ -H "Authorization: Bearer {token}"結果
{ "id": 1, "name": "Test User", "email": "test@example.com", "email_verified_at": "2025-03-08T05:42:05.000000Z", "created_at": "2025-03-08T05:42:05.000000Z", "updated_at": "2025-03-08T05:42:05.000000Z" }OK!