そのIDTokenの正体はセッショントークン?それともアサーション?

f:id:ritou:20200108042739p:plain

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

今年も Firebase Authentication や Auth0 とか沢山使われると思いますが、去年からよく聞かれる件をざっくりと書いておきます。

ID Tokenってセッショントークンとして使っていいんですよね?

例えばこちらの記事を見てみましょう。

Google の ID Token をセッショントークンとして使おうとして断念

techblog.kayac.com

認証は GoogleOpenID Connect の Auth Code Flow による認証を採用しています。ユーザと Google 間の認証後の callback で code を受け取り、code exchagne で ID token を取得します。

Google ID token は以下のような Google アカウントのユーザ情報を含む JWT です。

これはいわゆる OpenID Connect の ID Tokenのことです。

Google 発行の ID token は exp(Expiration Time)クレームが1時間なので、この ID token をそのままセッションキーとして使ってしまうと1時間でセッションが切れて再認証が必要になるサービスになってしまいます。ひとつ工夫が必要ですね。

なぜ工夫が必要なのか。なぜ使いづらいのか。こちらのIDTokenはそもそもセッショントークンとしての利用を想定されているものなのか。

この記事では結局諦めたようです。

Google先生に聞いてみます。

Google のドキュメント

developers.google.com

Authenticating the user involves obtaining an ID token and validating it. ID tokens are a standardized feature of OpenID Connect designed for use in sharing identity assertions on the Internet.

GoogleのID TokenはOIDCのID Tokenです。さっきも言った。

After obtaining user information from the ID token, you should query your app's user database. If the user already exists in your database, you should start an application session for that user if all login requirements are met by the Google API response.

検証しておたくのDBと突き合わせ、ユーザーがいたらアプリケーションセッションを始めろと書いてあります。

とりあえず、明確にセッショントークンとして使えとは書いてありません。

OpenID ConnectにおけるID Tokenは

  • アサーションとして認証イベントの結果をIdPがRPにお伝えするためのもの
  • セッション管理などいくつかの拡張などで処理の対象となるユーザーを表現するために指定する

と言う感じです。

ではなぜお母さんはセッショントークンだと思ったのか。Firebaseさんに聞いてみます。

FirebaseにおけるID Token

firebase.google.com

Firebase クライアント アプリがカスタム バックエンド サーバーと通信する場合、そのサーバーに現在ログインしているユーザーを特定する必要が生じる場合があります。これを安全に行うために、正常なログイン後、ユーザーの ID トークンを HTTPS を使ってサーバーに送信します。次に、サーバー上で ID トークンの完全性と信頼度を確認し、ID トークンの uid を取得します。サーバーで現在ログインしているユーザーを安全に特定するために、この方法で送信された uid を使用できます。

これは...セッショントークンっぽいですね。

ユーザーのログインまたは端末でのログインが成功すると、Firebase で独自の ID トークンが作成され、この ID トークンでユーザーまたは端末を特定し、Firebase Realtime Database や Cloud Storage などのリソースへのアクセスを許可します。この ID トークンは、カスタム バックエンド サーバーのユーザーまたは端末を識別するために再利用できます。クライアントから ID トークンを取得するには、ユーザーがログインしたことを確認してから、ログインしたユーザーから ID トークンを取得します。

今時のいわゆるIDaaSと呼ばれるサービスは、ユーザーの認証機能だけではなく、セッション管理まで提供しています。 なので、それを使うサービス(のバックエンド)は、OIDCにおけるClientと言うよりも、むしろFirebaseアカウントに対するリソースサーバー、API提供者として振る舞います。

FirebaseのIDTokenはバックエンドサーバーがいるSPAのセッショントークン的に扱われることを想定していると言えるでしょう。

他のところはどうでしょうか?Auth0を見てみましょう。

Auth0におけるID Token

auth0.com

キーワードっぽいのから拾っていきます。

However, beyond what is required for JWT, ID Tokens also contain claims asserted about the authenticated user, which are pre-defined by the OpenID Connect (OIDC) protocol, and are thus known as standard OIDC claims. Some standard OIDC claims include:

OIDCで定められたclaimを持ってるって書くと、OIDCのID Token、つまりアサーションとしての利用を想定しているのか?と思えてくるような来ないような。

By default, an ID Token is valid for 36000 seconds (10 hours).

しかし有効期限は10時間。これは明らかにアサーションではなくセッショントークンとしての利用を想定しています。

念の為、Auth0の中?というか日本?の方がQiitaに記事を書かれていたので聞いてみました。

qiita.com

やはりセッショントークンのようです。

FIrebaseと一緒というか、むしろ認証フローをある程度隠蔽するFIrebaseに対してOIDCのフローを見せつつそれで取得した ID Token をセッショントークンに使ってと言う混乱の極みであるような気がしてきました。

Kubernetesでも似たような話が?

だいぶ古い記事ではありますが、

qiita.com

このとき、設定した ID トークンの有効期限が切れると認証に失敗してしまいます。一般的に ID トークンの有効期限が切れたら、リフレッシュトークンを使い ID トークンをリフレッシュする必要があり、ユーザエントリで token を使っているとリフレッシュした ID トークンを改めて kubeconfig ファイルに設定し直す必要がありました。 この問題は、kubeconfig ファイルのユーザエントリに token ではなく、OpenID Connect のトークンリフレッシュのフローを実行してくれる auth-provider=oidc を利用することで解決します。

リフレッシュとか言ってます。これ今も同じなんでしょうか。後で見てみます(放置しそう)。

まとめ

セッショントークンなのかそうじゃないのか、どこぞのコーンフレークやら最中やらのお話を思い出させるような展開でございます。

以前もAccessTokenとIDTokenについて整理されていた方にウザ絡みしてしまったことを思い出しました。

qiita.com

ひとまず、現状としては

  • OIDCの定義ではIDTokenをセッショントークンではなく、アサーションとして使うことが想定されている
  • IDaaS的なところではセッショントークンの意味合いでIDTokenと言う名前のトークンが出回っている
  • 有効期限など、両者で要件自体も変わるのでこの2つの意味合いのIDTokenは混ぜるな危険です。開発者はどちらの意味なのかを判断する必要がある(ひどい)

となってそうです。

個人的には 特に紛らわしいAuth0絶対に許さんぞ と思いつつ、 今年も引き続きやっていきましょう。

ではまた。

(追記) 今回の現象は、OIDCのIDTokenに含まれる「ユーザーの属性情報」「IdPでの認証状況に関する情報」「iss,aud,iat,expなどのメタデータ」が

  • ID連携を行うRP側の「認証処理」そのもの
  • ステートレスなセッショントークンを用いたセッション管理

の両方で利用可能なために、同じIDTokenという名前のまま 異なる用途で使われているのだと考えています。 しかし、両者で異なる要件の部分、例えば

  • アサーション : 時刻ずれなどを意識しつつも最低限の有効期限、リプレイアタック防止のためのnonceの値を含む(OIDC IDTokenの仕様)
  • セッショントークン : 有効期限はそれなりに長く、nonceの値は不要、IdP側で更新のために必要な情報を含むかも?

と言ったあたりを利用者が意識できずに用途を間違えて使用すると「ログインセッションが10分で切れるのでやり直し」と言ったおかしなUXとなる可能性があります。 個人的にはセッショントークンとしての用途の場合は別の名前をつけてもらいたい気もしていますが、現状は利用者が気をつけてハンドリングしていくしかないでしょう。