0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OAuth2-Proxyの挙動を調べた

Last updated at Posted at 2025-01-28

OAuth2-Proxyがどのような振る舞いをするのか気になったので調査しました。

前提

  • Versionはv7.7.1です
  • reverse_proxy = true で動作させています
  • 全て localhost で動作させた内容です
    • cookie_secure = false を指定した挙動です
  • IdP には Auth0 を使っています
  • トークンの保存先にRedisは使っていません

image.png

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を実装するための便利な機能があります。どのようなログイン体験を提供するかに依存しますが、認可リクエスト時に organizationconnection などのパラメーターを指定することもあります。
https://auth0.com/docs/manage-users/organizations

標準仕様に存在しない独自パラメーターの場合のことを調査しました。

デフォルト

openid-configurationの値を見て、認可リクエストエンドポイントやトークンエンドポイントを取得しています。openid-configurationを利用する場合、OAuth2-Proxyには audienceorganization を指定する設定はありませんでした。

回避策として、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_urlaudience を固定値として埋め込む方法です。あまり美しくありませんが、こうせざるを得ないみたいです。

この方法だと静的パラメーターには対応できますが、 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トークンの有効期限を鑑みて設定をしたほうがいい
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?