Skip to main content

Laravel での tymon/jwt-auth による JWT トークンの自動更新

環境

概要

JWT トークンはトークン自体に有効期限が埋め込まれており、期限を更新するにはトークン自体を置き換えなければならない。 フロントで有効期限を管理してトークンのリフレッシュをせずに、スライディングセッションを実現したい。

トークンは Cookie でフロントに保存するものとする。サーバー側で期限切れを検出したら新しいトークンを Cookie に設定してあげる。

トークンの有効期限(JWT_TTL:デフォルト1時間)が切れても、リフレッシュ有効期限(JWT_REFRESH_TTL:デフォルト2週間)内であればそのトークンを用いて新しいトークンを発行することができる。

ログインシーケンス

トークン更新シーケンス

実装

サーバー側でトークンの有効期限切れを検出し、リフレッシュをおこなって返すミドルウェアを作成する。不正なトークンや更新に失敗したりした場合はこのあとの通常処理で 401 になるので、何もしない。

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;

class RefreshToken extends BaseMiddleware
{
public function handle($request, Closure $next)
{
$token = $newToken = null;
try {
$token = $this->auth->parseToken();
$token->authenticate();
} catch (TokenExpiredException $e) {
// Token expired: try refresh
try {
$newToken = $token->refresh();
} catch (JWTException $e) {
// Refresh failed (refresh expired)
}
} catch (JWTException $e) {
// Invalid token
}

$response = $next($request);
if ($newToken) {
// Send the refreshed token back to the client.
$response->withCookie(cookie(
'token',
$newToken,
config('jwt.refresh_ttl'), // minutes
null, // path
null, // domain
$request->getScheme() === 'https', // secure
true // httpOnly
));
}
return $response;
}
}

このミドルウェアを有効にする。

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
...
protected $middlewareGroups = [
'api' => [
...
\App\Http\Middleware\RefreshToken::class, // JWTトークン更新 ※bindings の前に置く
'bindings',
...
],
];
}

ブラックリスト猶予期間を設定

リフレッシュがおこなわれると、即時古いトークンは使えなくなるため、フロントから同時に複数のリクエストをおこなった場合、一部のリクエストが失敗してしまう可能性がある。これを防止するため、一定時間は古いトークンも使用できるようにする。.env にブラックリスト猶予期間(JWT_BLACKLIST_GRACE_PERIOD)を設定しておく。(例: 15秒)

JWT_BLACKLIST_GRACE_PERIOD=15