認可画面を別サイトに置く
このドキュメントの目的
認可画面(サインイン・同意の画面)を idp-server と別のサイトに置いても、Safari を含むブラウザでログインと SSO が成立するようにすることが目標です。
「別サイト」は登録可能ドメイン(eTLD+1)が違うことを指します。auth.example.com と api.example.com は同一サイトなので対象外です。auth.example-login.com と api.example.com のような組み合わせが対象です。
所要時間
⏱️ 約20分(自作の認可画面の改修は含みません)
前提条件
- 管理者トークンを取得済み
- 組織ID(organization-id)とテナントID(tenant-id)を取得済み
- 認可画面を別サイトで配信済み(
ui_config.base_url、または バリアント のbase_urlがそのサイトを指している)
なぜ設定が要るのか
認可画面は、認証や同意の API を XHR で呼びます。画面が別サイトにあると、これらの呼び出しはブラウザにとってサードパーティになり、Safari は idp-server の Cookie を送らず、保存もしません。その結果、次の 2 つが成立しなくなります。
- ブラウザの束縛(
IDP_AUTH_SESSION):認可リクエストを始めたブラウザかを確かめる Cookie が届かない - OP セッション(
IDP_IDENTITY):ログインしても Cookie が保存されず、SSO が効かない
別サイト認可画面モードでは、Cookie に頼っていたところを次のように置き換えます。
| 置き換えるもの | 置き換え先 |
|---|---|
| 認可画面からの呼び出しが、始めたブラウザからか | 認可リクエストの応答で認可画面に渡す値 view_binding。認可画面は呼び出しのたびに x-view-binding ヘッダーで送る |
| 認証したブラウザの識別 | 認証ステップの応答で渡す使い捨ての値 auth_proof |
| OP セッションの保存 | 最後に idp-server をトップレベル遷移で経由させ、そこで保存する(/complete) |
仕組み
XHR はページ がその場に居たまま裏で呼ぶので、アドレスバーは動きません。トップレベル遷移はタブ自体が移動します。idp-server の Cookie がファーストパーティとして扱われるのは、アドレスバーが idp-server を指しているときだけです。
| # | アドレスバー | やっていること |
|---|---|---|
| 1 | RP | ログインボタンで認可リクエストへ遷移させる(今までどおり) |
| 2 | idp-server /v1/authorizations | IDP_AUTH_SESSION を保存し、認可画面へ 302。認可画面の URL の fragment に view_binding を付ける |
| 3 | 認可画面 | 認証と authorize を XHR で呼ぶ。呼び出しには x-view-binding ヘッダーで view_binding を付ける。認証ステップの応答で auth_proof ① を受け取り、authorize に付ける。authorize は code を返さず、/complete 用の auth_proof ② を返す |
| 4 | idp-server /v1/authorizations/{id}/complete?auth_proof=② | 認可画面が遷移させる。IDP_AUTH_SESSION と ② を照合し、OP セッションの Cookie を保存して RP へ 302 |
| 5 | RP | 今までどおりのコールバック(code と state) |
RP の実装は変わりません。 変わるのは認可画面と idp-server です。
view_binding は、認可リクエストのたびに作り直す値で、DB にはハッシュだけを置きます。URL の fragment はサーバーに送られないので、認可リクエストを始めたブラウザの認可画面だけが受け取ります。認可画面からの呼び出しは、この値で始めたブラウザのものだと確か めます。値が無い・違う呼び出しは 401 です。
/complete では 2 つを照合します。auth_proof ② は「認証したブラウザか」、IDP_AUTH_SESSION は「認可リクエストを始めたブラウザか」を確かめます。どちらかが欠けると、攻撃者が始めた認可を被害者に完了させる、またはその逆が通ってしまうため、両方が必要です。
設定
テナント全体を切り替える
ui_config に cross_site を追加します。テナント設定の更新は全置換なので、GET したボディに追記して PUT してください。
"ui_config": {
"base_url": "https://auth.example-login.com",
"signin_page": "/signin",
"signup_page": "/signup",
"cross_site": true
}
クライアントごとに切り替える
クライアントの extension に cross_site_authorization_view を追加します。指定があればテナントの ui_config.cross_site より優先されます。一部の RP で試す、または対応の遅れた RP だけ従来の経路に残す、といった移行に使います。
"extension": {
"cross_site_authorization_view": true
}
テナント cross_site | クライアント cross_site_authorization_view | 動き |
|---|---|---|
false(既定) | 指定なし | 従来の経路 |
false | true | このクライアントだけ別サイト認可画面モード |
true | 指定なし | 別サイト認可画面モード |
true | false | このクライアントだけ従来の経路 |
画面の配信先をクライアントごとに変えたい場合は、バリアント で別サイトの画面を宣言し、RP が認可リクエストにバリアント名を付けます。バリアントが切り替えるのは画面の配信先だけで、別サイト認可画面モードは有効になりません。別サイトの画面に振り分けるクライアントには、cross_site_authorization_view: true もあわせて設定してください。