OAuth2-Proxyがどのような振る舞いをするのか気になったので調査しました。
前提
- Versionはv7.7.1です
-
reverse_proxy = true
で動作させています- Nginxとインテグレーションして動かせますが、今回の調査ではReverse Proxyとして動作させています
- https://oauth2-proxy.github.io/oauth2-proxy/configuration/integration/
- 全て localhost で動作させた内容です
-
cookie_secure = false
を指定した挙動です
-
- IdP には Auth0 を使っています
- トークンの保存先にRedisは使っていません
IdPから取得したトークンはどこに保存される?
2種類から選択できるみたいです。
https://oauth2-proxy.github.io/oauth2-proxy/configuration/session_storage
- Cookie
- Redis
Cookieの場合、IdPから取得した各種トークンが cookie-secret
で指定した対称鍵により暗号化されているみたいです。デフォルトではCookieのNameは _oauth2_proxy
となっていました。
認可リクエストのクエリパラメーターはどうなる?
Authorization Code Flowになると想定しつつ、実際の挙動を調査しました。
デフォルトの場合
予想通りAuthorization Code Flowでした。セキュリティに関するパラメーターは state
のみ指定でした。PKCEは自動でやらないようです。
https://example.jp.auth0.com/authorize
?approval_prompt=force
&client_id=2KqFbAjwMB46U......
&redirect_uri=http://localhost:4180/oauth2/callback
&response_type=code
&scope=openid email
&state=123abc
気になったのはデフォルトで approval_prompt=force
が付与されていたことでしょうか。GoogleをIdPとして意識した場合のパラメーターのようです。実害はなさそうなのでスルーで良さそうです。
https://github.com/oauth2-proxy/oauth2-proxy/issues/191
PKCEを使う方法
code_challenge_method=S256
をセットすれば動きました。セキュアになるので、なるべく指定するほうが良さそうです。
IdP固有パラメーターの設定方法
背景
Auth0には認可リクエストにおいて audience
という固有のクエリパラメーターがあります。RFC8707のResource Indicatorに近いパラメーターです。これを認可リクエストに指定することで、どのバックエンドサーバーに向けたアクセストークンを発行するか指定可能です。
また、他にもAuth0 OrganizationsというマルチテナントSaaSを実装するための便利な機能があります。どのようなログイン体験を提供するかに依存しますが、認可リクエスト時に organization
や connection
などのパラメーターを指定することもあります。
https://auth0.com/docs/manage-users/organizations
標準仕様に存在しない独自パラメーターの場合のことを調査しました。
デフォルト
openid-configurationの値を見て、認可リクエストエンドポイントやトークンエンドポイントを取得しています。openid-configurationを利用する場合、OAuth2-Proxyには audience
や organization
を指定する設定はありませんでした。
回避策として、openid-configurationを参照せず、それぞれのエンドポイントのURLを個別に指定する必要がありました。
https://github.com/oauth2-proxy/oauth2-proxy/issues/1250
skip_oidc_discovery = true
login_url = "https://example.jp.auth0.com/authorize?audience=https://example.api"
redeem_url = "https://example.jp.auth0.com/oauth/token"
oidc_jwks_url = "https://example.jp.auth0.com/.well-known/jwks.json"
login_url
に audience
を固定値として埋め込む方法です。あまり美しくありませんが、こうせざるを得ないみたいです。
この方法だと静的パラメーターには対応できますが、 organization
などユーザーごとで動的に切り替わるパラメータには対応できません。(もちろん、 audience
も用途によっては動的に変更したいことがありそうです)
現状だとOAuth2-Proxyをforkしてカスタマイズするしか方法はなさそうです。
バックエンドにトークンを渡す方法
デフォルト
バックエンドサーバーへのリクエストにはトークンが入りません。
pass_access_token = true
pass_access_token = true
を指定するとアクセストークンが x-forwarded-access-token Header にセットされました。バックエンドサーバーでもリクエストを検証する場合は、この設定はしたほうがいいでしょう。
ヘッダーが Authorization: Bearer
ではなく x-forwarded-access-token
になっている点は気になりました。これは私の想像ですが、Authorization
ヘッダーはBasic認証など他の認証とコンフリクトする可能性があるため、あえて別名のヘッダーにしているのではないかと推測してます。
pass_authorization_header = true
pass_authorization_header = true
を指定すると、IDトークンが Authorization: Bearer
でセットされ、バックエンドサーバーにリクエストが届きました。
私の想定だとアクセストークンの方が Authorization: Bearer
に指定されるのかなと思っていたのですが、この挙動は意外でした。これは OAuth2-Proxy の設計思想に答えがありそうです。以下をそのうち読んでみようと思います。
https://github.com/oauth2-proxy/oauth2-proxy/issues/843
トークンとCookie有効期限の関係(リフレッシュトークンなし)
まず Cookie の有効期限に関するパラメーターを調べました。リフレッシュトークンなしで動かしてみます。
cookie_expire
set-cookieされてから期限が切れるまでの時間を指定するようです。試しに 100s
を指定すると、 /oauth/callback
のレスポンスにてセットされた Cookie の有効期限が100秒後になってました。
cookie_refresh
設定ファイルのexampleにドキュメントより丁寧な説明コメントがありました。
## Refresh - (duration) refresh the cookie when duration has elapsed after cookie was initially set.
## Should be less than cookie_expire; set to 0 to disable.
## On refresh, OAuth token is re-validated.
## (ie: 1h means tokens are refreshed on request 1hr+ after it was set)
試してみる
トークンの有効期限が十分に長い状態で cookie_expire = 100s
cookie_refresh = 10s
にしてみました。Cookieの有効期限はいくらブラウザを更新しても、更新されることがありませんでした。トークンを再検証すると書いてあるので、アクセストークンの有効期限が長い場合には影響を受けないみたいです。
今度はアクセストークンの有効期限を短くしてみました。この場合はアクセストークンの有効期限切れのタイミングでアクセスすると、再認証が求められました。
(access_token=20s, cookie_refresh=10s, cookie_expire=100s)
では、アクセストークンの有効期限は十分長いまま、IDトークンの方を短くするとどうなるか?同じように、IDトークンの有効期限切れのタイミングでアクセスしたら再認証が要求されました。どうやらアクセストークン、IDトークンどちらかの有効期限が短い方を優先するみたいです。違和感のない挙動です。
(id_token=20s, cookie_refresh=10s, cookie_expire=100s)
そのため、リフレッシュトークンを利用しない場合だと以下のようにしておくのが良さそうです。
- cookie_expireはトークンの有効期限より短い時間にしておく
- トークンの有効期限が切れているが、Cookieはまだ生きているという状況を考慮しないようにする
- cookie_refreshはcookie_expireの数値を鑑みて適度な感覚にしておく
- 安全面に倒すなら短めくしておくのがいいかも
- JWTの検証によるレイテンシーが気になるなら頻度を少なくしておくとか
トークンとCookie有効期限の関係(リフレッシュトークンあり)
ではリフレッシュトークンを使うとどうなるでしょうか?以下の設定で試してみます。(アクセストークンの有効期限は十分に長いです)
- cookie_expire = 3m
- cookie_refresh = 10s
- id token exp = 1m
- refresh token exp = 2m
この場合、cookie_refreshで指定した時間にリクエストを行うと、IDトークンの有効期限が切れていない場合に関わらず、リフレッシュトークンによる洗い替えが行われました。また、Cookieの有効期限も延長がされました。
そのため、リフレッシュトークンを利用する場合は、以下のような設定にしておくのが良さそうです。
- cookie_expire
- ユーザーのインアクティブな状態が続いた場合に、セッションをクリアする時間
- cookie_refresh
- 指定された時間が経過した場合、アクセストークン・IDトークンの有効期限に関わらずトークン洗い替えを行う
- あまりに短い時間に設定すると、無駄にリフレッシュしてIdPへ負荷をかける可能性がある
- アクセストークン・IDトークンの有効期限が切れたとしても、リフレッシュトークンさえ生きていればトークン再取得が可能なので、アクセストークン・IDトークンの有効期限を鑑みて設定をしたほうがいい