【Laravel】JWTトークンをCookieに保存する(Laravel9.x + tymon/jwt-auth)

JWT
LaravelでJWT認証のつづき。JWTトークンをCookieに保存して受け渡していく処理を追加します






Cookieの名前は「token」にする

jwt-auth では、JWT トークンを以下の順番で検索する。

・Authorization ヘッダ (Bearer)
・Request->query(‘token’)
・Request->input(‘token’)
・Route パラメタ (token)
・Cookie (token)

ということで、トークンを発行したら、token という名前の Cookie を設定してあげればよい。

Laravel tymon/jwt-auth でトークンを Cookie に保存する – Qiita


これをすることで何が起きるかというと、何も設定しなくてもlaravel/uiでインストールされるAuthがCookieからJWTトークンを取得してチェックしてくれるようになります。有効なJWTトークンがCookieに「token」という名前で保存されている状態だと、次のコードでuserモデルが取得出来ます。


$user = auth()->user();


本来であれば、JWTトークンをチェックし、ペイロードからユーザーID(「sub」に格納されています)を取りだし、Modelからデータを取得し……という実装が昼用なんですがそれを全部Authとjwt-authがやってくれるという。便利すぎ。



Cookieの保存・削除はLaravelのライブラリを使う

setcookie()を使っても良いんですが、Laravelのライブラリを使っておいた方が楽です。例えばこんな感じ。

Cookieの保存

use Cookie;

Cookie::queue(Cookie::make(
   'token',
   $token,
   config('jwt.refresh_ttl'),  // minutes
   '/',                        // path
   'example.com',              // domain
   true,                       // secure
   true,                       // httpOnly
   false,                      // raw
   'lax'                       // samesite
));


Cookie::queue() 関数は、レスポンスをブラウザに返す時にCookieをヘッダに追加するように予約するための関数。Cookie::make() はほぼほぼ setcookie() 関数と同じですが違いはタイムスタンプではなく有効分数で指定するようになっていること。で、このコードを前回「app\Http\Controllers\Auth\LoginController.php」に実装したlogin() 内に実装します。それでログイン時にCookie保存はOK。


Cookieの削除

削除の方はこんな感じ。


use Cookie;

Cookie::queue(Cookie::forget(
   'token',
   '/',                        // path
   'example.com',              // domain
));


Cookie::forget() 関数はCookieを削除するための関数で、実際には値をnull、有効期限を過去(-2628000分)に設定してブラウザに削除させています。Cookie::forget() 関数の引数は、名前、path、domainの3つだけですが、この3つを保存時の値と同じにしないと削除出来ません。で、これを今度は logout() 内に実装します。



以上でJWTトークンをCookieに保存して運用する実装は概ね終わりです。

その他いくつか細かいことを。



その他細かいこと

Cookieの暗号化をどうするか

Laravelだけで完結するシステムなら何も考える必要はありません。ただ今回はLaravel外のシステムとの連携を考える必要があったので、暗号化を無効にしました(暫定)。暗号化の無効はMiddleware「EncryptCookies」をコメントアウトしても良いですがそれだと全てのCookieの暗号化が解除されてしまいます。それはさすがにやり過ぎ感。

EncryptCookiesには $except という設定があり、ここに対象となるCookieの名前を指定するとその名前のCookieのみ暗号化しなくなります。


/**
* The names of the cookies that should not be encrypted.
*
* @var array
*/
protected $except = [
   'token'
];


これで解決。

JWTのリフレッシュには工夫が必要

上の例ではJWTトークンの有効期限を2週間にして運用していますが、JWTトークンは一回発行すると無効化するのが難しいので、

  • 有効期限は短め(デフォルトの設定は60分)
  • 必要に応じて有効期限を延長する(デフォルトで有効期限切れから2週間)= リフレッシュ

というのが基本的な考えのようです。jwt-Authではデフォルトでリフレッシュしてくれているようですが、Laravel外で使っているライブラリ(例えばfirebase/php-jwt)ではデフォルトでリフレッシュしてくれないのでアクセス時になんらかの追加実装を行う必要がありそうです(まだ未着手)。



まとめ

まだまだ絶賛プロジェクト進行中(しかもまだ超序盤、要件定義も固まりきってないくらい)ということでどこかで仕様変更が入る可能性も捨てきれませんが、LaravelとJWTで認証を行う基本的な部分は作れたかなと。

Laravelのソースコードを読んで処理の仕組みを知っておくことは大事ですが、最終的に実装するときには最大限ライブラリを使ってやりたいですね。個人のサービスだったら好きにやれば良いですけど、複数の人間が開発に携わるようなプロジェクトなら特に。せっかくフレームワーク使ってるんだしね。