v0.15.0 影響確認
本ドキュメントは v0.15.0 に含まれる破壊的変更・挙動変更と、その利用者側(RP / テナント運用者)への影響をまとめる。
リリースに向けて対象変更が確定し次第、本ドキュメントへ追記していく。各変更は独立したセクション(## N. <変更>)として追加し、あわせて下記「影響まとめ」表に 1 行加える。
影響まとめ
| # | 変更 | 種別 | 対象 |
|---|---|---|---|
| 1 | 外部IdPアカウント連携を追加(新モジュール idp-server-account-linking)。DBマイグレーション 2 本(V0_15_0_1、V0_15_0_2)が必要 | 🟢 追加 / ⚙️ | 全環境(マイグレーション必須) |
| 1 | エンドユーザー向けの連携 API(/{tenant-id}/v1/me/linked-external-accounts)、保管トークンの取得 API(POST /{tenant-id}/v1/linked-external-accounts/{alias}/token)、ブラウザ遷移用の /{tenant-id}/v1/linking/start / /linking/callback/{provider} を追加 | 🟢 追加 | アカウント連携を使う構成。設定しない限 り挙動は変わらない |
| 1 | スコープ linked_account:read / linked_account:manage / linked_account:read_token を新設。テナントの scopes_supported とクライアントの scope の両方に追加しないと常に 403 | 🟢 追加 / 🔒 要点検 | アカウント連携を使う構成 |
| 1 | クライアントの extension に linking_return_uris(連携後の戻り先)、linking_providers(連携を始められる連携先)、linking_token_providers(保管トークンを取り出せる連携先)、linking_max_auth_age(連携に使えるログインの新しさ、既定 300 秒)、linking_acr_values(連携に求めるログインの acr)を追加。未設定なら連携の開始は 400 / 403、保管トークンの取得は 403 | 🟢 追加 / 🔒 要点検 | アカウント連携を使うクライアント |
| 1 | すべてのテナントのログインで __Host-IDP_BROWSER_{tenant-id} Cookie(Secure・HttpOnly・Path=/)を 1 つ多く発行する。/linking/start だけがこれを見て、このブラウザがログインして受け取ったセッションかを確かめる。v0.15.0 より前の OP セッションで連携を始めると 1 回ログインし直し | 🟡 挙動変更 | 全環境(Set-Cookie が増える)。連携は HTTPS 必須・Cookie のパスを書き換えるプロキシでは不可 |
| 1 | /linking/start はブラウザのログインが無いか古いとき、連携を始めた本人としてその場でログインし直させる(認可コードは発行しない)。認証画面(app-view)はこのログインでは同意を省く | 🟢 追加 | アカウント連携を使う構成 |
| 1 | 連携先は federation-configurations に新しい type: account-linking で登録する。payload に revocation_endpoint / duplicate_link_policy(既定 reject)/ multiple_accounts(既定 false)を追加 | 🟢 追加 | アカウント連携の連携先を登録する構成 |
| 1 | 連携先(type: account-linking)の federation-configurations は、登録・更新のときに中身を検査し(必須のエンドポイント、この連携先のコールバック、duplicate_link_policy の値、openid なら issuer / jwks_uri)、使えなければ 400。type と sso_provider は変更できない(400)。連携が残っている間の削除は 409(dry_run=true でも同じ) | 🟡 挙動変更 | アカウント連携の連携先を管理する自動化 |
| 1 | 管理権限 idp:linked-account:read / idp:linked-account:delete と、ユーザーの連携を確認・解除する管理API(システム・組織)を新設。既存テナントには権限のレコードが無い | 🟢 追加 / 🔒 要点検 | 権限を明示列挙しているロール(idp:* を持つロールは自動的に含む) |
| 1 | 管理権限 idp:data-key:read / idp:data-key:rotate と、テナントのデータ鍵を一覧・新しい世代を作る管理API(システム・組織)を新設。既存テナントには権限のレコードが無い | 🟢 追加 / 🔒 要点検 | 権限を明示列挙しているロール(idp:* を持つロ ールは自動的に含む) |
| 1 | セキュリティイベント型が 8 種増えた(external_account_*) | 🟢 追加 | セキュリティイベントフック / SIEM 連携 |
| 1 | POST /v1/admin/operations/delete-expired-data のレスポンス内訳に account_linking_session が増えた | 🟡 挙動変更 | 当該レスポンスを厳密に検証している自動化 |
| 1 | 保管トークンを 2 段の鍵(テナントごとのデータ鍵を鍵暗号化鍵で包む)で暗号化する。環境変数 ENCRYPTION_KEYS / ENCRYPTION_ACTIVE_KEY_ID(任意)と、運用API /v1/admin/operations/data-keys・/data-keys/rewrap・/data-keys/rotate を追加 | 🟢 追加 | アカウント連携を使う構成。未設定なら ENCRYPTION_KEY で動く |
| 1 | 運用API(/v1/admin/operations/*)は本文を省いたリクエストを空の本文として扱う。これまで delete-expired-data で本文を省くと 500 だった | 🟡 挙動変更 | なし(500 が返らなくなるだけ) |
| 1 | delete-expired-data の max_deletion_number は 1〜100000 の整数に限る。範囲外や数でない値は、これまでの 500 ではなく 400 を返す | 🟡 挙動変更 | 範囲外の値を渡している自動化 |
| 1 | 連携は既定で 1 ユーザー×連携先に 1 件。もう一度連携すると更新する(別の外部アカウントは 409)。multiple_accounts: true の連携先では複数持てる。識別子(alias)はどちらも {provider}-{8 文字の乱数} で、解除した連携の識別子は別の連携に使い回さ ない | 🟢 追加 | 識別子の形を前提にしている RP |
| 1 | ユーザーの削除で、そのユーザーの連携と連携セッションも削除し、保管トークンを外部IdPで失効させる(ベストエフォート) | 🟢 追加 | なし(連携が無いユーザーは変わらない) |
| 1 | テナントの削除で、削除の前にテナントの全ての連携の保管トークンを外部IdPで失効させる(ベストエフォート)。連携 1 件ごとに外部IdPを呼ぶので、連携が多いテナントは削除に時間がかかる | 🟡 挙動変更 | 連携があるテナントを削除する運用 |
| 1 | 付属の認可画面(app-view)が、入力した識別子とユーザーの状態を認可リクエストごとに保存する。同じタブの次のフローに、前のフローで入力したメールアドレスが入らなくなった | 🟡 挙動変更 | app-view をそのまま使う構成 |
| 1 | 付属の認可画面(app-view)の同意画面に、新しいスコープの表示名を追加 | 🟢 追加 | app-view をそのまま使う構成 |
| 1 | FederationConfigurationQueryRepository に get(..., boolean includeDisabled) を追加(default 実装なし) | ⚙️ | このインターフェースを自前で実装している構成 |
| 1 | 設定例 config/examples/standard-oidc-web-app/ の public-client.json を client_secret_basic にし、連携のスコープと extension を追加。連携用の設定例を追加 | ⚙️ | この設定例をそのまま使っているローカル環境 |
種別: 🔴 破壊的 / 🟡 挙動変更 / 🟢 追加 / ⚙️ 内部・運用 / 🔒 要点検(更新では直らず、設定の見直しが必要)
1. 外部IdPアカウント連携(Issue #1531)
🟢 追加。 認証済みのユーザーが、自分のアカウントに外部IdPのアカウントを追加で紐付ける機能を追加した。紐付けのときに外部IdPが発行したアクセストークンとリフレッシュトークンを暗号化して保管し、クライアントが後から取り出して外部APIの呼び出しに使える。
ログイン用の Federation とは別の系統で、連携を追加しても Federation の動作は変わらない。設定しないテナント・クライアントの動きも変わらない。
詳細は 外部IdPアカウント連携。
1.1 DBマイグレーション
⚙️ 全環境で必須。
| ファイル | 内容 |
|---|---|
libs/idp-server-database/postgresql/V0_15_0_1__account_linking.sql | linked_external_accounts / account_linking_sessions テーブル + index 4 本 + RLS |
libs/idp-server-database/mysql/V0_15_0_1__account_linking.mysql.sql | 同(MySQL に RLS は無いため tenant_id 述語のみ) |
libs/idp-server-database/postgresql/V0_15_0_2__tenant_data_keys.sql | tenant_data_keys テーブル + RLS、linked_external_accounts.encryption_key_id の index と既定値の削除 |
libs/idp-server-database/mysql/V0_15_0_2__tenant_data_keys.mysql.sql | 同(「active なデータ鍵は 1 本」を生成列の UNIQUE で表す) |
V0_15_0_1 で作ったテーブル以外への変更は無い。tenant_id はテナントの削除に連動して消える(ON DELETE CASCADE)。user_id には外部キーを張っていないため、ユーザー削除時の後始末はアプリケーションが行う(1.8)。
トークンはテナントごとのデータ鍵で暗号化し、データ鍵は鍵暗号化鍵で包んで tenant_data_keys に置く。鍵暗号化鍵は ENCRYPTION_KEYS で渡し、無ければ ENCRYPTION_KEY を使うため、追加の設定なしで動く。鍵の交換は データ鍵と鍵暗号化鍵のローテーション を参照。
oauth_token の暗号化はこれまでどおり ENCRYPTION_KEY を直接使う。ENCRYPTION_KEYS を設定しても ENCRYPTION_KEY は必須のままで、値を替えてはいけない。
1.2 追加したエンドポイント
🟢 追加。
POST /{tenant-id}/v1/me/linked-external-accounts/link/{provider} スコープ linked_account:manage
POST /{tenant-id}/v1/me/linked-external-accounts/complete スコープ linked_account:manage
GET /{tenant-id}/v1/me/linked-external-accounts スコープ linked_account:read または linked_account:manage
DELETE /{tenant-id}/v1/me/linked-external-accounts/{alias} スコープ linked_account:manage
POST /{tenant-id}/v1/linked-external-accounts/{alias}/token クライアント認証 + token に linked_account:read_token
GET /{tenant-id}/v1/linking/start?state= ブラウザセッション
GET /{tenant-id}/v1/linking/callback/{provider}?code&state state + ブラウザ束縛
/linking/start で __Host-IDP_LINK_BINDING_{tenant-id} Cookie(HttpOnly、Secure、Path=/、Domain なし)を発行し、コールバックで照合する。この Cookie の SameSite は Lax 固定で、Secure・Path・Domain も含めてテナントの session_config には従わない。そのため次の構成では連携が成立しない。
start_urlを iframe の中で開く構成(トップレベル遷移かポップアップで開く)- 外部IdPが
response_mode=form_postで返す構成(コールバックは GET のみ) - idp-server を平文の HTTP で公開している構成(
__Host-の Cookie は HTTPS でしか保存されない) - リバースプロキシや API Gateway が Cookie のパスを書き換える構成(
proxy_cookie_pathなど。__Host-の Cookie はPath=/以外だと保存されない) cookie_domainで複数のホストにまたがり、ログインを終えるホストと/linking/startのホストが違う構成(下のブラウザの証が届かず、毎回ログインし直しになる)
あわせて、すべてのテナントのログインで __Host-IDP_BROWSER_{tenant-id} Cookie を 1 つ多く発行する(乱数。OP セッションにはそのハッシュだけを持つ)。/linking/start は、このブラウザがログインして受け取ったセッションかをこの値で確かめる。見るのは /linking/start だけで、SSO などほかの挙動は変わらない。v0.15.0 より前に作られた OP セッションはこの値を持たないので、連携を始めると 1 回ログインし直しになる。
1.3 スコープ
🔒 要点検。
| スコープ | 許可すること |
|---|---|
linked_account:read | 連携の一覧 |
linked_account:manage | 連携の開始・確定・解除と一覧 |
linked_account:read_token | 保管トークンの取得。read / manage のどちらにも含まれず、どちらも含まない |
"scopes_supported": ["openid", "profile", "email", "linked_account:read", "linked_account:manage", "linked_account:read_token"]
テナントの scopes_supported とクライアントの scope の両方に追加する必要がある。片方だけではトークンにスコープが載らず、/me の連携 API は常に 403 insufficient_scope になる。保管トークンの取得も token に linked_account:read_token が無ければ 403 insufficient_scope。
1.4 クライアント設定
🔒 要点検。 クライアントの extension に 3 つ追加した。どれも既定は空。
{
"extension": {
"linking_return_uris": ["https://rp.example.com/linking/callback"],
"linking_providers": ["example-idp"],
"linking_token_providers": ["example-idp"]
}
}
| 項目 | 内容 | 未設定のとき |
|---|---|---|
linking_return_uris | 連携が終わったあとにブラウザを戻す先。link の redirect_uri と完全一致で照合する。認可の redirect_uris とは別 | link が 400 |
linking_providers | 連携を始 められる連携先(sso_provider) | link が 403 |
linking_max_auth_age | /linking/start がそのまま受け入れるログインの新しさ(秒)。古いかログインが無ければ、その場で本人としてログインし直させる。0 で毎回 | 300 |
linking_acr_values | 連携に求めるログインの acr(好ましい順)。ログインが新しくても acr がどれにも当たらなければログインし直させ、そのログインの認可リクエストに acr_values として載せる(acr_values で選ばれる認証ポリシーが当たる)。戻ってもまだ当たらなければ login_required | 制限なし(新しさだけで判断) |
linking_token_providers | 保管トークンを取り出せる連携先(sso_provider) | 保管トークンの取得が 403 unauthorized_client |
link の scope は連携先の設定の scopes_supported の範囲に限り、範囲外は 400。連携先の scopes_supported に openid があるときは、scope に openid を含める必要がある(無ければ 400)。外部アカウントを ID Token の sub で識別するため。
保管トークンの取得は、クライアント認証に成功したクライアントだけが使える。public クライアント(none)は 401 invalid_client。