😺

Laravel × React の認証処理 - Sanctum を使ったログイン機能の実装

に公開

背景

フロントエンド(React)とバックエンド(Laravel API)を分離して開発する際、
Sanctum を使った API 認証を適切に設定することが必要です。

このブログでは、Laravel Sanctum を使用したフロントエンド(React)とバックエンド(Laravel API)の認証処理 について解説します。


Sanctum を利用するための準備

Sanctum を利用することで、React(フロントエンド)と Laravel API(バックエンド)間の認証を簡単に実装できます。
以下の手順でセットアップを行います。

1. Sanctum のインストール

まず、Laravel に Sanctum をインストールします。

composer require laravel/sanctum

次に、Sanctum の設定ファイルを公開し、マイグレーションを適用します。

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

2. Sanctum の設定

(1) app/Http/Kernel.php にミドルウェアを追加(Laravel 9 以下のみ必要)

👉 Laravel 10 以降ではこの設定は不要です!
Laravel 10 以降では Sanctum のミドルウェアはデフォルトで含まれているため、追加する必要はありません。
Laravel 9 以下を使用している場合のみEnsureFrontendRequestsAreStatefulapi グループに追加してください。

protected $middlewareGroups = [
    'api' => [
        \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // Laravel 9 以下のみ
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

(2) config/sanctum.phpstateful を修正

👉 Laravel 10 以降ではデフォルトで localhost が含まれているため、この設定も不要
フロントエンド(React)と API のドメインが異なる場合、Sanctum を適切に機能させるため Laravel 9 以下では config/sanctum.phpstatefullocalhost を追加します。

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),

(3) CORS の設定を config/cors.php で変更

👉 Laravel 10 以降でも supports_credentialstrue に変更推奨
フロントエンド(React)からのリクエストを許可するために CORS の設定を修正します。

return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*'], // フロントエンドのURL
    'allowed_headers' => ['*'],
    'supports_credentials' => true, // Laravel 10 でも変更
];

(4) .env の設定を修正

Sanctum の認証が Cookie ベースで動作するよう、.env ファイルの SESSION_DRIVERcookie に設定し、CORS 設定と合わせます。

SESSION_DRIVER=cookie
SANCTUM_STATEFUL_DOMAINS=localhost:3000
SESSION_DOMAIN=localhost

Sanctum を利用したログイン API

app/Http/Controllers/API/AuthController.php にログイン処理を追加します。

public function login(Request $request)
{
    Log::info('login request', ['request' => $request->all()]);

    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
    ]);

    $user = User::where('email', $request->email)->first();

    if (!$user || !Hash::check($request->password, $user->password)) {
        return response()->json(['message' => 'Unauthorized'], 401);
    }

    // Sanctum 用のトークン発行
    $token = $user->createToken('auth_token')->plainTextToken;

    return response()->json(['token' => $token], 200);
}

フロントエンド(React)からのログイン処理

React 側では、fetch を使用してログイン API にリクエストを送ります。

const handleLogin = async (e: React.FormEvent) => {
  e.preventDefault();

  try {
    // CSRF クッキーを取得(Sanctum用)
    await fetch("http://localhost:9000/sanctum/csrf-cookie", {
      credentials: "include",
    });

    // ログイン API を呼ぶ
    const response = await fetch("http://localhost:9000/api/login", {
      method: "POST",
      credentials: "include", // Cookie を送る
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password }),
    });

    const data = await response.json();
    console.log(data);

    if (response.ok) {
      login(data.token);
      navigate("/dashboard");
    } else {
      setError("ログインに失敗しました。");
    }
  } catch (err) {
    console.error(err);
    setError("通信エラーが発生しました。");
  }
};

エラーと対処法

1. Laravel の CORS 設定が原因でリクエストがブロックされる

config/cors.phpsupports_credentialstrue に設定する

'supports_credentials' => true,

2. createToken() の返り値が accessToken ではなく plainTextToken になる

createToken('auth_token')->plainTextToken を使用する

3. fetch のリクエストが 401 Unauthorized になる

sanctum/csrf-cookie を先にリクエストする

await fetch("http://localhost:9000/sanctum/csrf-cookie", {
  credentials: "include",
});

まとめ

  1. Sanctum をインストールし、API 認証を有効化
  2. Laravel の config/cors.php で CORS 設定を修正
  3. Sanctum 用の plainTextToken をフロントエンドで適切に取得
  4. sanctum/csrf-cookie をリクエストして CSRF トークンを取得
  5. Laravel 10 以降ではミドルウェアの追加は不要

これで Laravel Sanctum を利用した React + Laravel のログイン認証機能 が完成しました!

Discussion