以前こんな記事を書いていた
もう一度Google OAuth認証に対応していていろいろ誤解していたことがわかったので訂正する記事
1. 前提
この記事の前提は
- 自分でなにかアプリを作って
- その認証処理を(自力で実装するのはリスキーなので)GoogleなどのIdPにオフロードしたい
というユースケースを想定している
2. Google認証では2種類の方法がある
Googleは概ね以下のような2種類の形式で認証を提供している。
- 「ログイン成功後にIDトークンを戻す」方法が「ユーザがログインに成功したことを証明する認証方法」で、
- 「ログイン後に認証コード(auth code)を戻す」方法が「ユーザがGoogle APIを使用する権利があることを証明する認証方法」
2.1. ログイン成功後にIDトークンを戻す認証方法
ログイン成功後に戻されるIDトークンにはベンダー内で一意のユーザID(sub claim)が含まれているので、認証されたユーザを特定することができる。
クライアントはIDトークンをアプリに提出し、アプリ側ではIDトークンを検証し、問題なければ後続の処理(実際にアプリを使わせたり、独自のセッショントークンを発行したり)を行う。
この方式ではGoogle(IdP)はアプリのセッションについてはなにも関与していない。アプリはセッション管理の仕組みを独自に実装する必要がある。
2.1.1. ここで(自作の)セッショントークンが漏洩するとどうなるか?
セッショントークンはあくまでアプリが独自に発行しているものなので、被害はそのアプリの中に留まる。IdPおよびIdPが提供しているサービスへのアクセスが行われることはない。
今風の言い方にすれば blast radius(不具合発生時の影響範囲) を最小化することができる。
2.2. ログイン後に認証コード(auth code)を戻す認証方法
ログイン成功後に戻される認証コードを(アプリ経由で)IdPに提出すると、(認証時に明示的に提示したアクセス範囲に基づいて)アクセストークン、リフレッシュトークンが得られる。ここで得られるアクセストークン、リフレッシュトークンは、あくまでIdP(ここではGoogle)が提供するAPIへのアクセスのためのトークン。
このアクセストークン、リフレッシュトークンはアプリのセッション管理に流用できるように見えるし、実際にやってみるとそれなりに妥当と思われるような動作になる。
2.2.1. ここで(Googleから提供された)セッショントークンが漏洩するとどうなるか?
仮に IdP から発行されたアクセストークン、リフレッシュトークンをアプリのセッション管理に流用していたとして、このトークンが漏洩すると IdP の提供する API にアクセスできてしまう。
つまり上記の 2.1.1. の場合に比べて blast radius が大きくなる。
2.3. どっちがいいのか?
GoogleのAPIに依存せずアプリ単体で自立しているなら「2.1. ログイン成功後にIDトークンを戻す認証方法」がいい。セッショントークンは自前でなんとかする。
アプリの動作にGoogle APIへのアクセスが必要な場合は「2.2. ログイン後に認証コード(auth code)を戻す認証方法」がいい。例えば draw.io のような作図結果をGoogleドライブに保存したりするようなアプリの場合、この方式が向いている。
3. 私は何を誤解したか
「2.2. ログイン後に認証コード(auth code)を戻す認証方法」を、トークン漏洩の結果置きうるリスクを考慮せず、簡便にアクセストークン、リフレッシュトークンが得られる方法として使用した。
その背景には、「一般的にセッションを管理する方法としてJWTを選択しておけば大きな問題にはならない」という思い込みがあった。
JWTの利点の1つはステートレス認証でありセッションを管理するDBを必要としないこと。逆に言えば、ステートを管理できる十分に速いDB(例えばRedis, Valkeyなど)で予想される負荷を十分に捌けるのであれば、セッション管理をJWTにする必要はない。
実際に試作してみたところ、APIサーバとValkeyを同一ホストに持っている場合トークンの処理時間は 300us〜4ms(4000us) 程度だった。アクセスパターンにもよるけど、ざっくり毎秒200~3000アクセス程度の処理であればこの構成で捌けるということになる。さらにValkey, Redisはクラスタ化してAPIサーバを外出し複数立てることで、毎秒数万アクセスという規模でもおそらく捌けるのではないかと推測できる。本当に本当に大規模なサービスとか、エンドポイントがIP anycastとかで複数拠点に散らばるとか、そういうのでない限りJWT必要ない。たぶん。
あと最初にIDトークンを出してくれるGoogleのlimitも気にしないといけない。昔は10000トークン/日って書かれていた気がするけど、今見たら記述が消えてるね・・・。 "Dependent on application history, developer reputation, and riskiness" だそうで。
セッション管理の話はこの記事の本題からずれるのでここまで。
4. さいごに
すいませんでしたあぁぁぁ!!!!
