メインコンテンツまでスキップ

v0.12.0 影響確認

本ドキュメントは v0.12.0 に含まれる破壊的変更・挙動変更と、その利用者側(RP / テナント運用者)への影響をまとめる。

リリースに向けて対象変更が確定し次第、本ドキュメントへ追記していく。各変更は独立したセクション(## N. <変更>)として追加し、あわせて下記「影響まとめ」表に 1 行加える。

影響まとめ

#変更種別対象
1外部パスワード認証で step_definitions[].allow_registration を強制(未許可なら JIT せず 400 user_not_found🟡 挙動変更外部認証 API(http_request executor + user_resolve.user_mapping_rules)でパスワード認証するテナント
1ポリシー未設定(null step)も allow_registration=false 扱い=secure by default(JIT 禁止)🔴 破壊的上記のうち、認証ポリシーを設定せず JIT 作成に依存していた構成
2response_resolve_configs の JSON スキーマを配列 [...] に統一(入力は旧 {"configs":[...]} も後方互換で受理、GET/DB 保存は配列に正規化)🟡 挙動変更http_request を持つ全設定(authentication / security-event-hook / federation / email 等)の管理 API レスポンスを解析する利用者
2authentication 側の response_resolve_configs実行時に適用されるよう修正(従来は無視=デッドフィールド)🟡 挙動変更authentication-configurations で response_resolve_configs を設定していた構成(設定済みルールが今後は実際に効く)
3パスワード 2 要素認証(requires_user: true)でパスワードの検証対象をセッションの認証済みユーザーに固定(CWE-287 認証識別子切り替え対策の強化)🟡 挙動変更password を 2nd factor に使うテナント(正規フローへの影響なし)
4SMS/Email 検証フェーズで UserTooManyFoundResultException(電話/メールが複数ユーザーにマッチ)を 500 → 400 invalid_request に変換🟡 挙動変更電話番号/メールにユニーク制約のないテナント(同一値で複数ユーザーが存在しうる構成)
4検証フェーズ user_not_found の Security Event 型を是正(Email sms_verification_challenge_failureemail_verification_failure / SMS →sms_verification_failure🟡 挙動変更Security Event フック / SIEM でこれらのイベント型をフィルタ・集計している運用者
5自己登録(initial-registration)の重複判定を identity_unique_key_type 準拠(preferred_username)に変更(従来は email 固定)🟡 挙動変更email 以外をユニークキーにするテナント(PHONE / USERNAME / EXTERNAL_USER_ID 等)。同一 email・別ユニークキーの登録が可能に

種別: 🔴 破壊的 / 🟡 挙動変更 / 🟢 追加 / ⚙️ 内部・運用


1. 外部パスワード認証の allow_registration 強制(Issue #1538 / PR #1658)

単独 password ステップ(requires_user: false)で外部認証 API が成功(200)を返したとき、step_definitions[].allow_registration を確認せず未存在ユーザーを INITIALIZED ステータスで JIT 作成していた問題を修正した。Email / SMS interactor と同じ allowRegistration パターンに整合させ、登録不許可なら認証失敗(400 user_not_found を返す。

1.1 変更内容

Before(無条件 JIT)

外部認証 API が 200 を返すと、external_user_id に対応するユーザーがテナントに存在しなくても、新規ユーザーを INITIALIZED ステータスで JIT 作成していた。allow_registration: false を設定しても無視されていた。

// password-authentication の応答(外部認証成功・未存在ユーザー)
{
"user_id": "orphan-...",
"user": { "sub": "...", "status": "INITIALIZED" }
}

After(allow_registration ガード)

resolveUserFromExternalAuth() が現在の step 定義の allow_registration を確認する。

既存ユーザー検索(provider + external_user_idallow_registration結果
見つかる(不問)既存ユーザーで認証成功
見つからないtrue新規 JIT 作成(従来どおり)
見つからないfalse(既定)400 user_not_found で認証失敗(JIT しない)
// password-authentication の応答(allow_registration:false・未存在ユーザー) — HTTP 400
{ "error": "user_not_found", "error_description": "User not found." }

実装: PasswordAuthenticationInteractor.resolveUserFromExternalAuth()libs/idp-server-authentication-interactors)。User.notFound() を返し、呼び出し元が 400 user_not_found に変換する。

対象は外部認証パスのみ: この変更は user_resolve.user_mapping_rules を持つ外部認証(http_request executor)構成にのみ作用する。user_mapping_rules を持たない通常のパスワード認証(DB を preferred_username で検索)は、元々ユーザー未存在時に notFound を返しており挙動は変わらない

1.2 Behavioral change — secure by default(破壊的)

allow_registrationAuthenticationStepDefinition既定値が falseallow_registration = false; // default: no registration)。さらに、認証ポリシー未設定・該当 step 未定義で getCurrentStepDefinition()null を返す場合も allow_registrationfalse 扱いとなり、JIT 作成は禁止される。

これは Email / SMS interactor と同じ「secure by default(明示的に許可しない限り新規登録しない)」挙動に揃えたもの。

影響: 外部パスワード認証で従来「認証ポリシーを設定しないまま JIT 作成」に依存していた構成は、本バージョンから user_not_found を返すようになる。JIT 作成を継続したい場合は step_definitions[].allow_registration: true明示する(§1.3)。

1.3 移行手順

JIT 作成(外部認証 API 成功時に未存在ユーザーを自動作成)を継続したいテナントは、対象 flow(例: oauth)の認証ポリシーに、password ステップを allow_registration: true で定義する。

// authentication-policies(管理API)
{
"flow": "oauth",
"enabled": true,
"policies": [
{
"available_methods": ["password"],
"step_definitions": [
{
"method": "password",
"order": 1,
"requires_user": false,
"allow_registration": true,
"user_identity_source": "email"
}
],
"success_conditions": {
"any_of": [[
{ "path": "$.password-authentication.success_count", "type": "integer", "operation": "gte", "value": 1 }
]]
}
}
]
}

逆に「事前登録済みユーザー限定」(外部認証は通すが未登録は拒否)を実現したい場合は、allow_registration: false(既定)のままにする。本変更により、ポリシーだけで「事前登録必須」を表現できるようになった(Issue #1538 の本来の要件)。

同梱テンプレートは追従済み: 外部パスワード認証ユースケースの認証ポリシーテンプレート(config/templates/use-cases/external-password-auth / id-service-migration)に、現行どおり JIT を維持する allow_registration: true の password ステップを追加済み。id-service-migrationsetup.sh は同テンプレートから ciba-ext-pw 設定も生成するため、両ユースケースが追従する。特に id-service-migration(レガシーID移行)は JIT インポートが存在意義のため true が必須。config/generated/ は gitignore 対象で、setup.sh 実行時にテンプレートから再生成される。

DB スキーマ変更(マイグレーション)は無し。設定(認証ポリシー)の追従のみ。

1.4 動作確認

ケースE2E テスト
allow_registration:false → 拒否 / true → JIT 成功e2e/src/tests/usecase/advance/advance-13-external-password-allow-registration.test.js
外部パスワード認証の既存フロー(ポリシー追従後)advance-10-external-password-auth.test.js / advance-12-external-auth-config-effects.test.js / scenario/application/scenario-11-password-policy-full-flow.test.js

1.5 関連


2. response_resolve_configs の JSON スキーマを配列形式に統一(Issue #1500)

外部 HTTP 連携の http_request.response_resolve_configs(外部レスポンスを内部ステータスコードに解決する設定)について、authentication-configurations と identity-verification-configurations で JSON スキーマが非対称だった問題を修正した。配列形式 [...] を正準とし、authentication 側もこれに統一する。あわせて、authentication 側で発覚していた 2 つの併発バグ(実行時に無視される/GET に出ない)も解消した。

HttpRequestExecutionConfighttp_request の共有設定クラス)は authentication 以外に security-event-hook / federation / email sender 等の多数の設定に埋め込まれているため、本変更はそれら全設定タイプに作用する。

2.1 背景:3 つの不整合

#不整合症状
型の非対称authentication 側は {"configs":[...]}(オブジェクトラップ)必須、identity-verification 側は [...](配列)。同じ response_resolve_configs キーで構造が違い、authentication API に配列を POST すると 500
デッドフィールドauthentication 側は responseResolveConfigs() のオーバーライド欠落で executor が常に空を受け取り、設定しても実行時に何も起きない(どの形式で書いても無視)
toMap 漏れHttpRequestExecutionConfig.toMap()response_resolve_configs を出力せず、管理 API の GET に出ない(入れたのに見えない)

2.2 変更内容(Before / After)

観点BeforeAfter
入力(POST/PUT){"configs":[...]} のみ(配列は 500)配列 [...]。旧 {"configs":[...]} も後方互換で受理
GET レスポンス出力されない(②③)配列 [...](値が空なら省略)
DB 保存形式{"configs":[...]}配列 [...](後述の遅延移行で正規化)
実行時の適用無視(デッドフィールド)実際に適用(条件マッチで mapped_status_code に解決)

実装はドメイン設定クラスに JSON ライブラリのアノテーションを足さずJsonConverter への中央登録で実現(core 系モジュールは JSON ライブラリ非依存のため):

  • HttpResponseResolveConfigsDeserializer … 読み込み時に配列 / 旧ラッパー両形式を受理
  • HttpResponseResolveConfigsSerializer … 書き込み時は常に配列で出力

2.3 挙動変更(注意)

authentication 側で response_resolve_configs を設定済みの構成: 従来は無視されていた(②)解決ルールが、本バージョンから実際に適用される。たとえば「外部レスポンス body が失敗を示すとき mapped_status_code: 401 にマップ」を設定していた場合、これまで素通りしていた認証が今後は 401 になる。意図したルールかを確認すること。

管理 API レスポンスを解析している場合: response_resolve_configs が配列 [...] で返るようになる(従来は出力されないか、内部的にラップされていた)。{"configs": [...]} を前提にパースしているクライアントは配列に追従する。

2.4 既存データの扱い・移行

DB に旧 {"configs":[...]} 形式で保存済みのデータは、読み取りでそのまま動作し、設定の更新時に配列へ自動正規化される(遅延移行)

操作既存 wrapper 行の挙動
読み取り(GET / イベント発火等)正常に読める(旧版で発生していた MismatchedInputException による 500 は解消)
読み取り後の DB更新するまで wrapper のまま(読み取りでは書き換わらない)
更新(PUT / 再保存)JsonConverter.write 経由で DB も配列に書き換わる(空→[]、値あり→[{...}]
  • 能動的な SQL データ移行は行わない(寛容読み込み+遅延正規化で対応。既存の contacts String→String[] と同方針)。放置しても両形式が読めるため、急いだ移行は不要。
  • データ損失なし: 旧 wrapper 内の解決ルールはそのまま読み込まれ、配列で再保存される。
  • 空(解決ルールなし)の場合の表現に差がある: GET レスポンスでは省略、DB 保存(JsonConverter.write)では空配列 [](旧 {"configs":[]}[] に変わるだけで実害なし)。
  • 外部の設定リポジトリ / テンプレートは順次配列形式へ更新(移行期間中も両対応)。
  • DB スキーマ変更(マイグレーション)は無し。

2.5 動作確認

ケーステスト
配列 / 旧ラッパーの読み込み・書き込み正規化・往復HttpRequestExecutionConfigResponseResolveTest(platform unit)
authentication: 配列 POST(201) / GET 配列 / 実行時に mapped_status_code 適用(401)e2e/.../integration/authentication-interactors/integration-02-authentication-interactor-response-resolve-configs.test.js
security-event-hook: 旧ラッパー入力の受理 / GET 配列正規化 / イベント発火が 500 しないe2e/.../usecase/advance/advance-15-security-hook-response-resolve-legacy-format.test.js

2.6 関連

  • 外部サービス連携http_request / response_resolve_configs
  • HttpResponseResolver / HttpResponseResolveConfiglibs/idp-server-platform/.../http/)— 条件評価コンテキストは $.status_code / $.response_headers.* / $.response_body.*

3. パスワード 2 要素認証のユーザーバインド(Issue #1396)

password を 2 要素目(step_definitions[].requires_user: true)に使う構成で、パスワードの検証対象を 1 段目で確定したセッションの認証済みユーザーに固定するようハードニングした。認証識別子切り替え(CWE-287)対策の強化。

3.1 影響

  • 正規の認証フローへの影響なし:利用者が自分の資格情報で 2 要素認証を行う通常動作は従来どおり成功する。
  • 影響範囲は password を 2nd factor(requires_user: true)に使う構成のみ。1st factor の password・他の認証方式は対象外。
  • 設定変更・DB マイグレーション不要(アップグレードのみで適用)。
  • 本ハードニングの取り込みのため v0.12.0 への更新を推奨。

3.2 動作確認

ケースE2E テスト
2nd factor のユーザーバインド(正規成功 / 不一致拒否 / 1 段目未完了拒否)e2e/src/tests/usecase/mfa/mfa-22-password-second-factor-user-binding.test.js

4. SMS/Email 検証フェーズのエラーハンドリング修正(Issue #1395 / #1667 / PR #1668)

SMS/Email 認証の検証フェーズsms-authentication / email-authentication)におけるユーザー解決まわりの 2 つの問題を修正した。電話番号/メールにユニーク制約のないテナント、および Security Event を監視している運用に影響する。

4.1 too-many users → 400 invalid_request(#1395)

電話番号/メールにユニーク制約がないテナントで同一値に複数ユーザーが存在する状態で検証(resolveUserfindByPhone / findByEmail)すると、UserTooManyFoundResultException が未ハンドリングのまま 500 になっていた。Challenge 側(*-authentication-challenge)と同様に 400 invalid_request へ変換する。

観点BeforeAfter
HTTP ステータス500(未ハンドリング例外)400
レスポンスサーバエラー{"error":"invalid_request","error_description":"too many users found for phone number: ..."}(Email は "for email")

影響: identity_unique_key_typeEMAIL_OR_EXTERNAL_USER_ID 等で電話番号/メールにユニーク制約がなく、同一値の複数ユーザーが認証検証に到達しうるテナント。500 を監視・リトライ対象にしていた場合、クライアントエラー(4xx)扱いに変わる。設定変更・DB マイグレーション不要。

4.2 user_not_found の Security Event 型を是正(#1667)

検証フェーズの user_not_found パス(OTP は検証成功したがユーザーを特定できない=登録不許可 / 2nd factor without user)が、challenge(送信)系・別 subsystem のイベント型を発火していたのを是正した。

InteractorBeforeAfter
Email(email-authenticationsms_verification_challenge_failure(❌ Email フローなのに SMS イベント)email_verification_failure
SMS(sms-authenticationsms_verification_challenge_failure(subsystem は合うが "送信失敗" の意味でズレ)sms_verification_failure

影響: Security Event フック(Slack / Datadog / Webhook / SSF 等)や SIEM ダッシュボードで、これらの失敗をイベント型でフィルタ・集計している運用者。特に Email 認証失敗の監視で user_not_found ケースが SMS イベントとして記録され漏れていたのが、email_verification_failure に正される。既存テストでこれらのイベント名に依存していたものはなし。設定変更・DB マイグレーション不要。

4.3 動作確認

ケースE2E テスト
too-many users(同一電話に 2 ユーザー)→ 400 invalid_requeste2e/src/tests/usecase/mfa/mfa-27-sms-verification-too-many-users-400.test.js

5. 自己登録の重複判定を identity_unique_key_type 準拠に(Issue #1669)

自己登録 InitialRegistrationInteractor の重複ユーザー判定が、テナントの identity_unique_key_type を無視して email 固定findByEmail)になっていた問題を修正した。テナントのユニークキー方針が決める preferred_usernameapplyIdentityPolicy で email / phone / username / external_user_id から導出)で判定するよう変更し、永続化パスの UserVerifierfindByPreferredUsername)とロジックを一致させる。

5.1 Before / After

観点BeforeAfter
重複判定キーemail 固定(findByEmailpreferred_usernameidentity_unique_key_type 準拠)
判定タイミングapplyIdentityPolicy の前applyIdentityPolicy の後
EMAIL ユニークのテナントemail で重複判定preferred_username = email なので 従来どおり
非 EMAIL ユニーク(PHONE / USERNAME 等)email で誤って重複ブロックユニークキーで判定(同一 email・別ユニークキーの登録が可能
同一 email が既に複数存在する状態での登録findByEmail が複数ヒットし UserTooManyFoundResultException500preferred_username はユニークなので 500 リスクなし

5.2 影響

email をユニークキーにしないテナントidentity_unique_key_typePHONE / USERNAME / EXTERNAL_USER_ID 等)で、従来は同一 email の2人目以降の自己登録が 400 "user is conflict with username and password" で弾かれていたのが、ユニークキーが異なれば登録できるようになる。EMAIL ユニーク(既定 EMAIL_OR_EXTERNAL_USER_ID を含む、email 保持ユーザー)のテナントは挙動不変。設定変更・DB マイグレーション不要。

5.3 動作確認

ケースE2E テスト
PHONE ユニークで同一 email・別 phone の2ユーザー登録 → 成功、検証で too-many → 400e2e/src/tests/usecase/mfa/mfa-28-email-verification-too-many-users-400.test.js