OAuth 2.0のAccess TokenへのJSON Web Token(JSON Web Signature)の適用

こんばんは、ritouです。

久々の投稿な気がしますが、今回はOAuth 2.0のリソースアクセス時の設計の話です。
ずーっと前から書こうと思いつつ書いてなかったので、ここに書いておきます。
出てくる用語や仕様は、下記の翻訳リンクを参照してください。

想定する環境

わりとよくある環境を想定しています。


  • OAuth 2.0で認可サーバーとリソースサーバーがある
  • 認可サーバーがAccess Tokenを発行
  • リソースサーバーがAPIリクエストに含まれるAccess Tokenを検証する

よくある実装とその悩みどころを、JSON Web Token(JSON Web Signature)により軽減できるかもという話です。

よくある実装 : Access Tokenに一見ランダムな文字列を利用、DBなりで紐付け

パフォーマンスとか無視して素直に実装しようとするとこんな感じになるのではないでしょうか?

認可サーバー
  • 認可情報を(DBなどに)保存 : user_id, client_id, scope, 有効期限など
  • Access Tokenはランダムっぽい文字列を利用して、それらと紐付けられる
リソースサーバー
  • Access Tokenの文字列を利用してDB接続や認可サーバーへのHTTPリクエスト、その他いろいろな手段でAccess Tokenに紐づく情報を問い合わせる : Access Tokenが有効であることを確認

認可サーバーにHTTPなどで問い合わせる感じだと、こんな図になるでしょう。

この場合、基本的に受け取ったAccess Tokenに対して全て問い合わせが走るので、どうしても負荷がきつくなりがちでしょう。
無駄な問い合わせを省略するためには、Access Tokenにタイムスタンプ含めるとか、文字列のフォーマットをチェックするとかはできるかもしれませんが、規模が大きくなると辛いのかなと思ったりします。

提案 : JWT(JWS)形式のAccess Tokenに情報を詰め込む

JWTを用いて、リソースサーバーから認可サーバーへ問い合わせを減らせるのでは?という提案です。

認可サーバー
  • 認可情報を(DBなどに)保存 : user_id, client_id, scope, 有効期限など
  • "認可情報もしくはAccess Tokenに対して一意"なtoken_id的なものを払い出し、認可情報とは別で有効なtoken_idを管理しておく
  • 認可情報とtoken_idを含むJWT(署名つきなので正確にはJWS)をAccess Tokenの値とする
  • 署名には認可サーバーの秘密鍵を利用(alg=RS256とか)し、リソースサーバーには公開鍵を配布するなどして有効な公開鍵を確認できるようにしておく
リソースサーバー
  • Access Tokenの署名検証を行う : 認可サーバーが発行した文字列であることを確認
  • 有効期限を確認する : 有効期限内のAccess Tokenであることを確認
  • JWTに含まれるtoken_idが有効かどうかを認可サーバーに問い合わせる : 何らかの事情でAccess Tokenが無効化されていないことを確認

AccessTokenの有効期限を短くして最後のtoken_idのチェックをスキップみたいな話もあるが、今回は割愛します。

メリット/デメリット

メリット
  • リソースサーバーは無効なAccess Tokenを署名検証 + 有効期限チェックである程度判定できる
  • DBや認可サーバーも、token_idの管理だけできればいいので負荷が抑えられる
デメリット
  • リソースサーバー側で行う処理が増える。この手の処理はApacheモジュールとか、WAFのプラグインとかでやれるしくみを用意すると幸せになれそう
  • JWT使うとAccess Tokenの文字列は長くなる

Access TokenのPayloadサンプル

JWTのPayloadに含む最低限の値はこんな感じかと。token_idなんかはHeaderでも良いかも。
3文字ルールの中にtoken_idとかscopeとか入れるの気持ち悪かったら何でも良いですし、構造化されててもかまわないと思います。

{
 "sub": "(リソースオーナーのユーザー識別子)",
 "token_id": "(認可情報 or Access Tokenに対して一意な識別子)",
 "aud": "(client_id)",
 "exp": (有効期限のタイムスタンプ),
 "scope": "(このAccess Tokenに関連付けられたScope)"
}

まとめ

  • OAuth 2.0のリソースアクセスの部分で、ベタに実装すると負荷とかきつくなりそうな部分をJWTを用いて改善できるかもという話を書いたつもり
  • 昔からWebサービスのSession Cookieなどでこ独自encode+signatureとか使っているところはあったと思うし、SessionをCookieに保存するしくみがあるが、それにも適用可能(最近話題のmicroservicesとかでOAuth 2.0のAccess Tokenをサービス内で使ってるところとかも)

参考になりそうな資料

ではまた。