環境
- Laravel 6.0
- tymon/jwt-auth 1.0.0-rc.5
概要
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