Laravel tymon/jwt-auth のブラックリストをデータベースに保存する
Laravel/Lumen 用の JWT 認証パッケージである tymon/jwt-auth では、発行した有効なトークンを保持するのではなく、ログアウトしたりリフレッシュされた古いトークンをブラックリストという形で保存しておく。デフォルトではキャッシュに保存されるので、Redis などで共有していないとスケールアウトできない。ということで、データベースに保存してみる。
環境
- Laravel 6
- tymon/jwt-auth 1.0.0
手順
テーブル jwt_auth_storage を作成
database/migrations 配下に定義してマイグレーションする。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJwtAuthStorageTable extends Migration
{
    public function up(): void
    {
        Schema::create('jwt_auth_storage', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->timestamp('expires_at')->nullable();
            $table->string('key')->index();
            $table->string('value');
        });
    }
    public function down(): void
    {
        Schema::dropIfExists('jwt_auth_storage');
    }
}
jwt_auth_storage テーブルの Eloquent Model を作成
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
class JwtAuthStorage extends Model
{
    protected $table = 'jwt_auth_storage';
    protected $fillable = [
        'key', 'value', 'expires_at',
    ];
    protected $dates = ['expires_at'];
}
Storage を継承したアクセスクラスを作成
Tymon\JWTAuth\Contracts\Providers\Storage を継承し、各メソッドを実装したクラスを作成する。デフォルトでは Tymon\JWTAuth\Providers\Storage\Illuminate::class が使用されるので、これを参考にする。
<?php
namespace App\Repository;
use App\Model\JwtAuthStorage;
use DateTime;
use Tymon\JWTAuth\Contracts\Providers\Storage;
class JwtAuthStorageRepository implements Storage
{
    protected $jwtAuthStorageModel;
    public function __construct(JwtAuthStorage $jwtAuthStorageModel)
    {
        $this->jwtAuthStorageModel = $jwtAuthStorageModel;
    }
    public function add($key, $value, $minutes): void
    {
        $expiresAt = (new DateTime('now'))->modify('+ ' . $minutes . ' minutes');
        $this->jwtAuthStorageModel->newQuery()
            ->create([
                'key' => $key,
                'value' => serialize($value),
                'expires_at' => $expiresAt,
            ]);
    }
    public function forever($key, $value): void
    {
        $this->jwtAuthStorageModel->newQuery()
            ->create([
                'key' => $key,
                'value' => serialize($value),
            ]);
    }
    public function get($key)
    {
        $now = new DateTime('now');
        $data = $this->jwtAuthStorageModel->newQuery()
            ->where('key', $key)
            ->where('expires_at', '>', $now)
            ->orderBy('expires_at', 'desc')
            ->first();
        if ($data) {
            return unserialize($data->value);
        } else {
            return null;
        }
    }
    public function destroy($key): bool
    {
        return !!$this->jwtAuthStorageModel->newQuery()
            ->where('key', $key)
            ->delete();
    }
    public function flush()
    {
    }
}
設定ファイルを publish
設定を変更するため、config/jwt.php をプロジェクトに展開する。
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
設定
設定ファイル(config/jwt.php)の provider.storage に、カスタマイズしたアクセスクラスを指定する。ブラックリストの読み書きはこのクラスを経由しておこなわれる。
<?php
return [
    ...
    'providers' => [
        ...
        'storage' => App\Repository\JwtAuthStorageRepository::class,
    ],
];