概要
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
プリフライトリクエストのキャッシュ時間 0
0
にすると毎回プリフライトリクエストを送る'supports_credentials' => false
クッキーを含めたリクエストを許可するか? false
Token認証ではクッキーを使わないため 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:seed
tinkerで確認
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!