「Googleでログイン」を実装してみた〜トークンリフレッシュ編〜

タム

2024.04.11

4

こんにちは。タムです。

今回はGoogleでログインの第5回目です。

IDトークンの有効期限を迎えた際にどうすべきか思索に耽っていましたが、

どうやら泥沼にはまってしまったようなので、とりあえず現状の

自分の考えをまとめようと思います。

考察

基本的な方針の比較

最もセキュリティレベルが高いのは、都度認証でしょう。

また、バックエンドから認証エラーが返ってきたときにログイン画面にリダイレクトすればいいだけなので、

実装的にも最も簡単だと思われます。

ただ、当然のことながらIDトークンの有効期限が切れるたびに認証を突破しなければいけないため、

UX的には最も劣る方法でもあります。


次にトークンをリフレッシュする方法を考えます。

これなら有効期限が切れてもユーザの手を煩わせることもありません。

フロント側でリフレッシュトークンを持っておいて、バックエンドから認証エラーが返ってきたときに

トークンエンドポイントに対してリフレッシュトークンを送信し、新しいIDトークンを受け取ります。

ただこの場合、リフレッシュトークンが万が一漏れた場合に他人になりすまされてしまいます。

フロント側ではリフレッシュトークンを安全に保管することはできません。


バックエンド側でトークンを保管することを考えます。

そうするとフロントとバックエンドでセッションを共有するための別の要素が必要になってきます。

ログインに成功したら、バックエンドでセッションIDを発行し、トークン3セットと紐付けておきます。

レスポンスとしてはトークン3セットの代わりにセッションIDを返すようにしましょう。

そうすると、フロントはIDトークンの有効期限など考えずに、単純にセッションIDを

Cookieに保存しておき、毎回のリクエストに含めるだけで良くなります。

IDトークンの有効期限が来た場合は、よしなにバックエンドでトークンをリフレッシュし、

新しいトークンに交換しておきます。

ただこの場合も、セッションIDが漏れた場合になりすまされてしまいます。

結局、リフレッシュトークンがセッションIDに置き換わっただけで、本質的には何も変わっていません。

リフレッシュトークンとセッションIDの比較

リフレッシュトークンもセッションIDの違いについて考えてみます。

まず、リフレッシュトークンには有効期限がありますがセッションIDにはありません。

しかし、セッションIDをCookieで保存するなら有効期限は設定できます。

次に、セッションIDはリクエストのたびに送るものですが、リフレッシュトークンは

トークンの有効期限が切れた場合のみ送るものです。

なので、ネットワークを経由する頻度としてはリフレッシュトークンのほうが圧倒的に

少ないと言えます。

最後に、漏洩した場合のリスクですが、セッションIDについては明示的にログアウトしない限り有効です。

リフレッシュトークンについて言えば、基本的に1回使ったら古いものは使えないはずです。

※リフレッシュトークンの再利用はできないようにすべきですが、OAuth2.0の仕様として決められているわけではないようです。

例えば, 認可サーバーはアクセストークンリフレッシュレスポンスの都度新しいリフレッシュトークンを発行するリフレッシュトークンローテーションを採用することができる. 


総合的に見て、単なるセッションIDよりもリフレッシュトークンのほうが

セキュリティレベルが高いような気がしてきました。

演習

インプリシットフローとリフレッシュトークン

Googleの場合には、インプリシットフローではリフレッシュトークンを取得することができませんでした。

やはりURLフラグメントで丸見えになってしまうからでしょうか。

リフレッシュトークンは有効期限が長く漏洩した場合のダメージが大きいため、

インプリシットフローではそもそも禁止されているものと思われます。

なので、その場合は都度認証しか手段がないですね。

リフレッシュトークンのローテーション検証

上の比較検討のところで、

リフレッシュトークンについて言えば、基本的に1回使ったら古いものは使えないはずです。

と書きましたが、本当にそうでしょうか?

試しにGoogleの認可サーバが対応しているかどうか試してみましょう。


1回目の使用


2回目の使用


普通に使えちゃってますね・・・意外でした。


ユーザがもう一度認可した場合は、古いリフレッシュトークンは利用できるんでしょうか?

(再度認可しました)


普通に使えちゃってますね・・・

これだと、セッションIDを使うのとあまり変わらないかもしれないです。


後になって気づいたのですが、トークンリフレッシュレスポンスに

リフレッシュトークンは含まれていませんでした。

つまり、元々最初に発行されたリフレッシュトークンを使いまわすことを

あえて意図しているようです。

リフレッシュトークンの有効期限について調べてみましたが、

明確な記述は見つかりませんでした。

かなり長く設定されているようだ、みたいな話もあるみたいです。

まとめ

以下、あくまでも自分の意見ですがまとめました。

  • ユーザがいないときにアクセストークンを利用するユースケース(毎日特定の時間にGoogleドライブにアクセスするなど)がないのであれば、都度認証が最も望ましい
    • 1時間毎だと煩わしいのであれば、3時間毎にするとか。セキュリティ面とのバランスでチューニングする
  • 都度認証が許容できないのであれば、リフレッシュトークンをフロント側にもたせるのもアリ
この記事をシェアする