Azure AD B2C の提供終了が見えてきた今、「メールドメインごとに自動で適切な IdP へリダイレクトさせたい」という要件は、多くの SaaS / 企業ポータルで現実の課題になっています。本記事では、Microsoft Entra External ID(以下 External ID)で B2C のカスタムポリシー相当の「ドメイン別 IdP 自動振り分け」をどう実現するか、現時点での機能とベストプラクティスを整理します。
Azure AD B2C から Microsoft Entra External ID への流れ
Azure AD B2C は既存テナントについては少なくとも 2030 年まではサポートされるものの、2025 年 5 月 1 日以降、新規購入はできなくなりました。 一方で、顧客・パートナー向け ID 基盤としては次世代の Microsoft Entra External ID(CIAM / B2B 含む)が推奨される立ち位置になっています。
External ID は B2C と同様に「外部のユーザー(消費者・ビジネス顧客・パートナー)」向けの ID 管理を担いますが、設計思想がかなり異なります。B2C が XML ベースのカスタムポリシーで任意のユーザージャーニーを組めたのに対し、External ID は「シンプルなユーザーフロー」+「必要に応じた拡張(API コネクタやフェデレーション)」でカバーするアプローチです。
この違いが、そのまま「ドメイン別 IdP リダイレクト」パターンの可否にも影響します。
旧 Azure AD B2C での「ドメイン別 IdP リダイレクト」パターンをおさらい
B2C のカスタムポリシーを使うと、典型的には次のようなフローを構成できていました。
- サインイン冒頭の画面で、ユーザーに メールアドレス を入力させる。
- ポリシー内のオーケストレーションステップで
@ドメインを抽出し、ドメインに応じて条件分岐。 - マッピングされた 外部 IdP(Azure AD / ADFS / SAML / OIDC / 社内 IdP など) へ自動リダイレクト。
- IdP での認証完了後、B2C がトークンを発行し、アプリ側のサインイン完了。
この方式のポイントは、ユーザーが IdP を選ばなくても、メールアドレスの @ドメインから自動判定してくれる ことです。大規模マルチテナント SaaS やグループ企業ポータルでは定番の設計でした。
結論の整理:External ID は B2C カスタムポリシーの完全互換ではない
まず結論から整理します。
- External ID には、B2C の Identity Experience Framework(カスタムポリシー)に相当する汎用的な「条件分岐エンジン」はありません。
- ただし、external tenant(CIAM)のサインイン画面に限っては、SAML/WS-Fed IdP 向けに「メールアドレスのドメインと IdP の対応付け」に基づく自動リダイレクトがサポートされています。
- OIDC IdP や複雑な条件(契約プラン、テナント ID、クエリパラメータなど)でルーティングしたい場合には、アプリ側で Home Realm Discovery (HRD) を実装するのが実質的な標準解です。
つまり、「External ID だけで B2C カスタムポリシーと同等の何でもアリな条件分岐を組めるわけではない」が、シンプルなドメイン別ルーティングなら一部ネイティブ機能も使える、というのが 2025 年時点の現実解です。
External ID のサインインモデルをざっくり整理
External ID(external tenant / CIAM)のサインイン方式は、公式ドキュメント上は大きく次のように分類されています。
| カテゴリ | 例 | 特徴 |
|---|---|---|
| ローカルアカウント | メール+パスワード / メール+ワンタイムコード | External ID テナント内にアカウントを保持。最もシンプル |
| ソーシャル IdP | Google / Facebook / Apple など | 外部のコンシューマ ID をそのまま活用 |
| カスタム OIDC IdP | 自社 IdP、Auth0、Cognito など | OIDC でのフェデレーション。 External ID 側は OIDC クライアントとして動作 |
| カスタム SAML / WS-Fed IdP | AD FS、Okta(SAML)、他社 SSO など | サインイン画面で IdP ボタンを表示し、選択された IdP へリダイレクト |
External ID の「ユーザーフロー」は、これらの IdP をどのように並べるかを GUI ベースで構成するイメージで、途中に任意の条件分岐ロジックを挟むことはできません。
External tenant でネイティブにできるドメイン別リダイレクト
カスタム SAML/WS-Fed IdP の「メールドメイン マッチ → 自動リダイレクト」
2025 年時点のドキュメントでは、external tenant でカスタム SAML / WS-Fed IdP を構成した場合、次のような挙動が明記されています。
- 各 SAML / WS-Fed IdP ごとに 「ドメイン名」 を関連付けられる。
- サインインページでユーザーがメールアドレスを入力すると、そのドメインがどこかの IdP に紐づいているかを評価する。
- 一致する IdP があれば、その IdP へ自動的にリダイレクトされる。
これは、B2C カスタムポリシーで行っていた「メールドメインから IdP を引き当てる」動作にかなり近いものです。ただし、以下のような制約があります。
- 対象はカスタム SAML / WS-Fed IdP のみ(OIDC やソーシャル IdP はこのメールドメイン判定の対象外)。
- External ID の「サインイン画面内」で発動するため、ユーザーフローや画面構成を細かく変えることはできない。
- 「このドメインは A 社テナントか B 社テナントかをさらに見分けたい」といった、ドメイン以外の条件での分岐はできない。
メールアドレスのドメインごとに 1:1 で SAML / WS-Fed IdP に飛ばしたいだけであれば、External ID のこの機能だけで要件を満たせるケースもあります。
domain_hint による「URL ベースの IdP 直行リンク」
さらに External ID では、サインイン URL に domain_hint パラメータを付与することで、特定の IdP へ最初から遷移させる「ドメイン加速(Domain acceleration)」もサポートされています。
- SAML / WS-Fed IdP:フェデレーション設定で定義したドメイン名を
domain_hintに指定。 - Google / Facebook / Apple:
domain_hint=google/facebook/appleを指定。 - カスタム OIDC IdP:Issuer URI のドメイン部分を
domain_hintに指定。
これを使えば、
https://contoso.example.com/login→ 自動で Contoso 用 IdP に飛ばすhttps://fabrikam.example.com/login→ Fabrikam 用 IdP に飛ばす
といった「テナント別 URL から適切な IdP へ直行」も比較的簡単に実現できます。
それでも B2C カスタムポリシー完全互換にならない理由
ここまで見ると、「External ID でもメールドメインで IdP を自動選択できるなら、もう B2C と同じでは?」と思いたくなりますが、実際には次のようなギャップが残ります。
| 観点 | Azure AD B2C(カスタムポリシー) | Microsoft Entra External ID |
|---|---|---|
| 条件分岐の柔軟性 | 任意の条件(クレーム、クエリ、REST API の結果)で分岐可能 | 基本は固定のユーザーフロー。分岐は限定的 |
| UI カスタマイズ | HTML / CSS / JavaScript をフルカスタム可能 | ブランドロゴ・色・背景などの範囲に限定 |
| IdP 選択 | 動的に IdP を出し分け、非表示にするなど自由 | ユーザーフローに追加した IdP ボタン / メールドメイン判定の範囲 |
| 複雑なユーザージャーニー | ステップの挿入・再利用が自由(KYC、同意取得など) | ユーザーフロー+API コネクタで一部カバー |
公式の Q&A でも、External ID は B2C カスタムポリシーをそのまま置き換えるものではなく、「よくあるシナリオを簡単に構成できること」にフォーカスしていると明言されています。
そのため、複数の条件で柔軟に IdP ルーティングしたい、あるいは アプリ単位でまったく異なるフローを組みたい といった要件では、今でも「アプリ層 HRD」を組み合わせるのが現実解になります。
推奨アーキテクチャ:アプリ層 HRD(Home Realm Discovery)
アプリ層 HRD の基本アイデア
アプリ層 HRD の発想はシンプルです。
- 自分たちのアプリケーション(SPA / Web アプリ / ネイティブアプリ)が 最初のサインイン画面 を持つ。
- そこでメールアドレス or テナントキーを入力させ、@ドメイン → IdP(もしくは External ID テナント) をアプリ側で決定する。
- 決定した IdP / External ID に対して、適切な OIDC / SAML の /authorize / 認証リクエストを自ら投げる。
- リダイレクトバック後に state / nonce / PKCE を検証し、トークンを受け取ってアプリセッションを確立する。
Azure 側で提供されている Home Realm Discovery ポリシーは、どちらかというと「Entra が IdP を選ぶ」ためのものですが、アプリ層 HRD は「アプリが IdP を選ぶ」と理解すると整理しやすくなります。
アプリ層 HRD でのマッピング設計
まずは「どのドメインをどの IdP / テナントに紐づけるか」を表現するデータモデルを決めます。シンプルな例としては、次のようなテーブルを用意します。
| 列名 | 例 | 説明 |
|---|---|---|
| domain | contoso.com | メールドメイン(正規化済み) |
| idpType | ExternalID_OIDC / SAML / AzureAD など | 送信先 IdP の種類 |
| idpAuthority | https://login.contoso.com/… | OIDC Issuer、SAML エンドポイントなど |
| externalTenantId | 1111-2222-… | External ID テナント ID(必要な場合) |
| defaultPolicy | signupsignin1 | External ID のユーザーフロー名など |
| isEnabled | true/false | 無効化フラグ(契約終了などに対応) |
このテーブルを、アプリ起動時にメモリキャッシュへロードしておけば、サインイン時には単純な map[emailDomain] の参照で IdP を決定できます。
疑似コードによるフロー例
例えば Node.js / TypeScript 風に書くと、次のようなイメージです。
app.post("/signin", async (req, res) => {
const email = (req.body.email as string).trim();
const domain = email.split("@")[1]?.toLowerCase();
const idpConfig = await findIdpByDomain(domain);
if (!idpConfig) {
// 既定 IdP(自社アカウントや External ID のローカルアカウントなど)へ
return redirectToDefaultIdp(res, email);
}
const state = generateRandomString();
const nonce = generateRandomString();
const codeVerifier = generatePkceVerifier();
const codeChallenge = toCodeChallenge(codeVerifier);
saveAuthContextToSession(req, { state, nonce, codeVerifier, idpConfig });
const authorizeUrl = buildAuthorizeUrl(idpConfig, {
state,
nonce,
login_hint: email,
// External ID / Entra 用なら domain_hint を付与するのも有効
domain_hint: domain,
});
return res.redirect(authorizeUrl);
});
コールバック側では state と nonce を検証し、PKCE(code_verifier)付きでトークンエンドポイントにコードを投げて、ID トークンとアクセストークンを取得します。
セキュリティ上の必須チェックポイント
アプリ層 HRD を採用する場合、次のポイントを最低限押さえておく必要があります。
- オープンリダイレクトを避ける:送り先の IdP / テナントは必ずホワイトリスト(上記マッピングテーブル)からのみ選ぶ。
- PKCE を必須化:
response_type=code+code_challenge/code_verifierを必ず使用。 stateとnonceの検証:CSRF / リプレイ攻撃対策として必須。- エラー時のフォールバック:存在しないドメイン・一時的な IdP 障害時には、既定 IdP やサポート案内ページに安全に誘導する。
構成パターン:複数テナント+直接フェデレーションの併用
パターン概要
より大規模なマルチテナント SaaS の場合、
- 顧客ごと / グループ会社ごとに 別の External ID テナント を用意する
- または、External ID(もしくは Entra ID)と SAML/WS-Fed IdP の直接フェデレーション を構成する
といったパターンがよく採用されます。
この場合も、実際の「どのテナント / IdP に送るか」を決めるのはアプリ層 HRD です。External ID 側は、あくまで「そのテナント配下の顧客ユーザー管理」と「必要なら外部 IdP とのフェデレーション」を行うだけに絞られます。
パターン比較
| パターン | メリット | デメリット |
|---|---|---|
| 単一 External ID テナント+アプリ層 HRD | 運用するテナントが 1 つで済む。設定が集中管理しやすい。 | 顧客ごとの完全なテナント分離が必要な場合には不向き。 |
| 顧客ごと External ID テナント+アプリ層 HRD | テナント単位のデータ・設定分離が明確。法規制への対応もしやすい。 | テナント数が増えるほど運用・監査が複雑になる。 |
| Entra ID B2B / Direct Federation 併用 | 既存の企業 Entra ID と連携しやすく、ゲストユーザーとして一元管理可能。 | External ID(CIAM)ではなく B2B コラボレーション寄りのモデルになる。 |
External ID で「ドメイン別 IdP リダイレクト」を構成するチェックリスト
実際に設計する際に確認しておきたいポイントをチェックリスト形式でまとめます。
| 項目 | 観点 | 具体策の例 |
|---|---|---|
| ドメインマッピング管理 | スケーラビリティ / 運用性 | DB または構成管理サービスにマスタを集約し、管理画面 or IaC から更新できるようにする。 |
| 未登録ドメインの扱い | UX / セキュリティ | 既定 IdP へ誘導するか、「アカウントが見つかりません」画面でサポート問い合わせへ誘導。 |
| サブドメイン / エイリアス | エッジケース | foo.contoso.com を contoso.com に丸めるか、サブドメイン単位で別マッピングにするかを事前に決める。 |
| 国際化ドメイン | IDN 対応 | Punycode を正規化し、DB 上では ASCII に統一して比較する。 |
| 監査ログ | トラブルシュート / コンプライアンス | 「入力メール」「判定ドメイン」「選択 IdP」「結果(成功 / 失敗)」を監査ログとして保存する。 |
| アラート | 運用 | 特定の IdP へのリダイレクト失敗が一定時間で閾値を超えたら、アラートを上げる。 |
簡易フロー(擬似手順+ URL 例)
アプリ層 HRD+External ID(external tenant)を組み合わせた場合の典型的な OIDC フロー例を、もう一度整理します。
/signin(自アプリ)でメール入力フォームを表示。- POST されたメールアドレスから
domain = email.split("@")[1].toLowerCase()を取得。 - マスタ(DB / 設定ファイル)から
idp = map[domain]を検索。 - 見つかったら、その IdP / External ID テナントの
/authorizeへリダイレクト URL を生成。 - リダイレクト前に
state/nonce/code_verifierをサーバーサイドで保存。 - コールバックで
stateを検証し、PKCE 付きでトークンエンドポイントへコードを送信。 - ID トークンの署名・
nonceを検証したうえで、アプリケーションセッションを確立。 - マッピングが見つからなかった場合は、既定 IdP または IdP 選択画面へ出し分け。
External tenant のユーザーフローを使う場合の OIDC リクエストは、概ね次のようになります(例)。
GET https://<custom-domain-or-tenant>.ciamlogin.com/<tenant-id>/oauth2/v2.0/authorize
?client_id=<client-id>
&response_type=code
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fauth%2Fcallback
&scope=openid%20profile
&state=<random-state>
&nonce=<random-nonce>
&code_challenge=<pkce-challenge>
&code_challenge_method=S256
&[email protected]
&domain_hint=contoso.com // 必要に応じて付与
domain_hint は必須ではありませんが、External ID 側で SAML / ソーシャル / OIDC IdP のドメイン加速・Issuer 加速を使う場合には非常に有効です。
External ID ネイティブ機能 vs アプリ層 HRD の使い分け
ここまでの内容を、「どのパターンで何を使うか」という視点で整理します。
| 要件 | External ID ネイティブ | アプリ層 HRD |
|---|---|---|
| メールドメインごとに SAML IdP に自動送信したい | external tenant のカスタム SAML/WS-Fed IdP+メールドメインマッピングで実現可能 | より細かい制御が必要な場合に併用 |
| OIDC IdP を複数切り替えたい | domain_hint 付き URL を発行すれば一部加速は可能 | 基本はこちらで実装(メールドメイン→Issuer のマッピング) |
| グループ会社 / 顧客ごとにテナントを分けたい | 複数 External ID テナント+ユーザーフローで構成 | どのテナントへ飛ばすかをアプリ層で決定 |
| 複雑な条件(契約プラン、権限、パラメータ)で IdP を切り替えたい | 外部 API コネクタなどの工夫はできるが、自由度は限定的 | アプリ層 HRD 一択。任意のビジネスロジックを実装可能 |
UX・パフォーマンス観点での工夫ポイント
認証フローはユーザーが最初に触る部分なので、UX も軽視できません。いくつか実戦的な工夫ポイントを挙げておきます。
- 最近使ったドメインの記憶:Cookie / LocalStorage に最後にサインインしたドメインを記録し、次回は自動でそのドメインを補完してあげる。
- ドメインのオートコンプリート:
@以降を入力している際に、マスタにあるドメイン候補をサジェストする。 - SP Initiated SSO と IdP Initiated の共存:基本は SP Initiated(アプリ発)で HRD を行いつつ、顧客のポータルからの IdP Initiated SSO も許可する構成を検討する。
- 多言語 / ブランディング:External ID の Company branding で、「どのアプリから来ても一貫したブランド」に見えるようにロゴ・色を揃える。
「今 B2C カスタムポリシーで動いている」場合の戦略
最後に、すでに Azure AD B2C のカスタムポリシーでドメイン別 IdP ルーティングを実現している場合の現実的な選択肢を整理します。
- 短期:B2C 継続運用
既存テナントは 2030 年まではサポートされるため、すぐにやめる必要はありません。既存フローが安定しているなら、監視と小さな改善にとどめつつ、移行計画を立てるフェーズと割り切るのも有効です。 - 中期:External ID への段階的移行
まずは一部の新規アプリや限定テナントを External ID+アプリ層 HRD で構成し、運用ノウハウを貯めてから本番系を移行するのが現実的です。 - 長期:アプリ層 HRD を前提としたアーキテクチャに寄せる
将来の要件変更(新しい IdP の追加や契約モデル変更)を見据えると、認証フローの頭出しをアプリ側が握っていた方が変更コストを抑えやすくなります。
なお、External ID 側で「B2C カスタムポリシーと同等の柔軟なオーケストレーション」を求める声は多いため、必要であれば Microsoft のフィードバック ポータルに要望を出しておくと良いでしょう。実際、ドメイン別 IdP ルーティングについても Q&A で議論されており、多くが同様のニーズを持っていることがわかります。
まとめ:External ID 時代の「ドメイン別 IdP リダイレクト」の考え方
本記事のポイントを最後に整理します。
- External ID は B2C のカスタムポリシーと違い、任意の条件分岐エンジンを内蔵しているわけではない。
- ただし、external tenant における カスタム SAML / WS-Fed IdP+メールドメインマッピング と
domain_hintによるドメイン加速 により、シンプルなドメイン別 IdP 自動振り分けはネイティブでもある程度実現可能になっている。 - より高度なルーティング(複数プロトコル / 複数テナント / ビジネス条件ベース)を実現するには、アプリ層 HRD を実装して、アプリ自身が「どの IdP / テナントへ送るか」を決定する設計が有力。
- 既存の B2C カスタムポリシー環境をすぐに捨てる必要はないが、External ID+アプリ層 HRD を前提にしたアーキテクチャへ徐々に寄せていくのが、2025 年時点での現実的な移行シナリオと言える。
これから External ID への移行や新規構築を検討している方は、
- 「どこまでを External ID のネイティブ機能に任せるか」
- 「どこからをアプリ層 HRD として実装するか」
を最初に切り分けて設計することで、後から大きな作り直しを避けつつ、ドメイン別 IdP リダイレクト要件を満たしていくことができます。

コメント