OAuthのハイブリッド型アクセストークン実装に関するあれこれ

おはようございます。ritouです。

f:id:ritou:20200503025039p:plain

先日オンラインで開かれたAuthlete社の勉強会で、アクセストークンの実装パターンについて触れられていました。

www.authlete.com (titleはもうちょっとなんとかする方が良さそう)

ドキュメントでいうとこの辺りでしょうか。 qiita.com

  • 識別子型
  • 内包型
  • ハイブリッド型

の3種類があると説明されていました。 この中でハイブリッド型の中にも色々あるよなってところで、ちょっと自分の思うところを書いてみようと思います。

上記ドキュメントのハイブリッド型の説明

説明としては

ハイブリッド型の実装方法では、内包型アクセストークンを生成しつつも、それに付随するデータを認可サーバーのデータベース内に持ちます。

という感じで、クライアントから隠蔽すべき情報の扱いとして、

ハイブリッド型アクセストークンを利用し、秘密にしたい情報をアクセストークンには含めず、サーバー側のデータベース内のみに保存する。

とあります。

今回取り上げる実装パターン

こんな感じの2種類の実装パターンを考えます。

  • 識別子のみを内包
  • イントロスペクト可能な内包型

これらはどちらも上記の動画や資料にあるハイブリッド型の範囲に収まっており、どこまで情報を持つかの違いですね。

識別子のみを内包

最低限の情報しか含まない実装です。 Payloadの例はこんな感じになるでしょう。

   {
     "jti": "abcdefghijklmn01234567890",
     "iss": "https://authorization-server.example.com/",
     "aud": "https://rs.example.com/",
     "exp": 1544645174
   }

特徴としては

  • 識別子型 + 最低限のメタデータ + 署名
  • "sub" とかも含まない。データを引くときは "jti" を使って参照する
  • RSによる検証のために "iss", "aud", "exp" を残している

というところです。

識別子型での実装の場合、RSからトークンイントロスペクションなり独自の仕組みでの問い合わせが毎回発生します。 それはしょうがないんでしょうけれども、"hogehogehogehoge" みたいな文字列とかで問い合わせを行うのはもったいないです。 そして有効期限の切れているアクセストークンを弾きたい時も、タイムスタンプ機能を加えたアクセストークン文字列の設計が必要となるでしょう。 そこで、最低限のメタデータと一緒にJWTにしちゃって、諸々の検証によって無駄な問い合わせを軽減しようってのがこの実装です。

次行きましょう。

イントロスペクト可能な内包型

これは上記資料にある内包型+α的な実装です。

   {
     "jti": "abcdefghijklmn01234567890",
     "iss": "https://authorization-server.example.com/",
     "sub": " 5ba552d67",
     "aud":   "https://rs.example.com/",
     "exp": 1544645174,
     "client_id": "s6BhdRkqt3_",
     "scope": "openid profile reademail"
   }
  • Clientから見えて良い情報を内包しちゃう
  • "jti" を使って生存確認ができる

こちらもトークンイントロスペクションもしくは独自の方法で問い合わせを"行える"前提ですが、必須にする必要はないだろうというお話です。

  • そんなにセンシティブな情報を扱わない RS であれば手元の検証だけでOK
  • センシティブな情報を扱うRSの場合、手元の検証 + 問い合わせまでやる

前者はプロフィール画像などほぼ公開されているデータを返すAPI、後者は決済系のAPIなどですね。 個人的には、APIでこれらが混在しても、そんなに大きな問題は起こらないと思っており、最初から「(識別型、内包型、ハイブリッドの)3種類のうちどれ選ぶ!?!?」となる必要はないんだよと思っています。

まとめ

  • ハイブリッド型と呼ばれる実装の中で、内包されるデータが微妙に異なる2種類に注目した
  • 識別子のみを内包し、検証によって無駄な通信を削減するのはどうだろう
  • 識別子の問い合わせは用意しつつ、必須にしないという実装もありそう
  • eyJ! eyJ!

というお話でした。まぁ、細けぇ話ですね。

ではまた。

おまけ

そういえば、6年ぐらい前から同じようなこと言ってました。

ritou.hatenablog.com

しかし見直すと色々怪しいですね。

f:id:ritou:20200503031212p:plain

この記事の例だとユーザー識別子を "sub" で持っていますが、いわゆる "OAuth as a Authentication" に囚われていたのかもしれません。 あくまでOAuthはリソースアクセスすることが目的なので、発行対象のユーザー識別子を必ずしも含んでClientが(見ようと思えば)見れる必要はない、というのが今の考えです。

そして "aud" の値が Client ID になっています。 OIDCのID TokenはClientが検証するためのものなのでこれでいいんですが、OAuthのAccessTokenだと "aud" はアクセストークンを受け付ける Resource Server の値を含むべきでしょう。

今後もぼくのかんがえるさいきょうのおーおーすとーくんせっけいを考えていきたいと思います。