Rails では認証時に JWT を返す knock という gem があります。
この gem の Issue では様々な開発者から JWT に関する質問があり、それに対する著者の返答が勉強になるため、紹介します。
サーバーサイドからユーザーを強制的に「ログアウト」させるにはどうすればいいですか?
悪いニュース: JWTでは、これをすぐに実行する方法がありません。
これは、トークンベースの認証がステートレスであるという事実に起因しています。つまり、セッションなどとは異なり、サーバーサイドで状態(「ユーザーがログインしているか」など)を管理しないということです。
ステートレスAPIを持つことのシンプルさとスケーラビリティの面でのメリットは大きく、強制ログアウトのような機能を犠牲にする価値があると私は考えています。
トークンのブラックリストを使ってこのような機能を再実装しようとするのは良いアイデアとは言えません。
代替案としては、トークンの有効期限を短くして、頻繁にローテーションすることが考えられます。
Q. トークンをデータベースに保存するのはどうでしょうか?
これは良い考えではありません。リクエストのたびに追加のデータベースクエリが必要になります。また、セッションを管理しなくて済むというメリットも大きく減ります。トークンを使う最大の利点の一つは、トークンをサーバーに保存しなくても、トークンが有効かどうかを判断できることです。
これは refresh token に対応しているのでしょうか?
いいえ、Knock はリフレッシュトークンをサポートしていません。私はその必要性を感じたことはありませんし、寿命の長いリフレッシュトークンを追加することは、セキュリティ上の問題を追加し、JWTを使う上でのシンプルさを損なうと考えています。
Q. トークンの有効期限が1日で切れることは知っていますが(デフォルトでは)、それはユーザーが再度ログインしなければならないということですか?
トークンの有効期限が切れた後、ユーザーは再度ログインする必要があります。
リフレッシュトークンのようなものを実装したい理由は理解しています。しかし、この種の問題に対処する方法は他にもあると思います。例えば、スライディングセッションを考えてみましょう。
スライディングセッションでは、ユーザーが認証済みのアクションを行うたびに、新しい短命のトークンを送信することになります。ユーザーがアクティブである限り、そのユーザーは認証された状態を維持します。ユーザーが有効期限切れのトークンを送信した場合は、しばらくアクティブでない状態が続いていることを意味します。
あえて refresh token に対応していないと。著者の主張もわかりますが、例えば toC 向けのアプリで2日後にアプリを開いたら再度ログインを求められることになります。 knock を採用する上で UX とセキュリティのどちらを取るかは検討すべき箇所になります。
ぼやき
仮に knock を使わずに JWT の access token と refresh token を用意する方法を取ったとして、refresh token が漏れた場合に refresh token を無効化することができない。なので、knock のように token の有効期限を短くして、頻繁にローテーションしようとなると思われる。
その場合 JWT token の有効期限は甘く見積もっても1日~1週間ほどだろうか。
UX のためにもっと長く認証期間を保持したいとなると、JWT は検討から外れてきそう。
(refresh token だけブラックリスト運用を検討してみても良いかも知れない。)