コードロード

エラー討伐

【Laravel】419 CSRF token mismachエラーの対処記録

Laravel Sanctumを使ってログイン機能を実装中にエラーに遭遇。 思考の整理と次に時間を溶かさないようにするためのメモ

結論

.env での SESSION_DOMAIN='localhost' ここが間違えていた。

.env にてセッションを設定するドメインを設定しているので、ドメインを設定する。

開発環境なので localhost で良い。

本番環境のドメインを指定してしまっていたので、セッションクッキーがセットされていなかったよう。考えてみれば当たり前。デプロイ時は本番のドメインを指定する。

ちなみに localhost:8000 だとエラーになるのでポート番号はいらない!

環境

  • PHP 7.4.16
  • Laravel 8.83.17
  • MAMP
  • フロントはReactで、LaravelはAPIサーバとして開発

Sanctumによるログインの流れ

  1. Laravel Sanctumをインストール
  2. 自動で sanctum/csrf-cookie というルーティングが追加されるから、ここにGETリクエストすることで、CSRF保護を初期化する。( /login へPOSTする前に必ず実行する必要あり。これを行わないと、CSRFが照合できずエラーになる )
  3. レスポンスヘッダーに XSRF-TOKEN というクッキーが返ってくる。
  4. これを X-XSRF-TOKEN というキーでリクエストヘッダーにセットして、 /login へリクエストする。
  5. $request->session()->regenerate(); でフロントにセッションクッキーをセット。

流れは間違っていなかったが、ドメインが間違えていたのでセッションクッキーをセットできていなかった。

調べたあれこれ

guard

// config/auth.php

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            // 'driver' => 'session',
            'provider' => 'users',
            'hash' => false,
        ],
    ],
  • Laravel Sanctumによる認証は、webのguardを使用する。
  • regiter、login、logoutあたりはセッションクッキーを使用するので、sessionドライバーを設定しているweb guardを使用する。
  • api guardはデフォルトではtokenドライバーが設定されている。これをsessionに書き換えてもいいようだけど、それだと本来のAPIの仕様が薄れちゃうから微妙という記事もあった。
  • 書き換えてみたら CSRF token mismachエラーはでなくなったものの、 Auth::attempt() が使用できなくなり断念。 undefindエラーが出た。やっぱりセッションクッキーだからapiではなくてwebの方を使えということなのかな?

CSRF

  • \App\Http\Middleware\VerifyCsrfToken::class の箇所でtokenの検証をしている。
  • フロントから送られてくる X-XSRF-TOKEN とLaravel側のトークンを比べている。
// app/Http/Kernel.php  

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class, ←ここ
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

VerifyCsrfToken クラスの詳しい動きは下記の記事が細かく載っていた。

LaravelのCSRF対策の処理を実際のコードから見てみる - Qiita

その他

  • config/cors.phpに下記を追記。
    • paths : ここの部分はクロスドメイン周りのエラーを回避するよってこと?
    • supports_credentials :デフォルトでは false だったので true にする。GET以外のHTTPメソッドのときにトークン認証を毎回するよってこと。
// config/cors.php

    'paths' => [
        'api/*',
        'login',    // 追記
        'logout',   // 追記
        'register', // 追記
        'sanctum/csrf-cookie'
    ],

    'supports_credentials' => true,

参考

Laravel7 + Sanctum でSPAログイン認証機能を実装 - Qiita

Laravel API SanctumでSPA認証する - Qiita

【attemptメソッドが使えない?】Call to undefined method TokenGuard::attempt() の解決法

Laravelの標準Authentication(Auth)の動きを調べてみる - Qiita

Laravel5.4でAuth::attemptを使わずに認証処理をカスタマイズ

LaravelでSPA開発が可能なLaravel Sanctumの概要と導入手順を紹介する

Next.js(axios)とlaravelを使ってhttp通信がしたい(POST、PUT、DELETE)