tech#entra-id#microsoft-foundry#azure#authentication#multitenant

企業ごとの Entra ID で使うマルチテナント Foundry チャット

公開 👁 —
企業ごとの Entra ID で使うマルチテナント Foundry チャット

同じ企業グループでも、グループ会社ごとに別々の Microsoft Entra ID テナントで社員を管理していることがあります。今回は、そうした複数の利用会社が、サービス提供会社の運用する1つの AI チャットを共通で使うケースを想定しました。

利用者をサービス提供会社の Microsoft Entra ID テナントへゲストとして集約するのではなく、利用会社が管理する社員アカウントをそのまま使います。そのために、組織アカウント向けのマルチテナント App Registration と、利用会社ごとのテナントに作成される Enterprise Application を組み合わせました。利用者の MFA や Conditional Access、退職時のアカウント停止といった ID 管理は利用会社に残しつつ、サービス提供会社の API で利用を許可する Tenant ID を制限します。

この記事では、Microsoft Foundry のチャットデモで実際に採用した構成、テナントを追加する手順、API 側で必要な検証、HTTP 403 でつまずいたポイントをまとめます。Tenant ID、Application ID、リソース名などの環境固有情報は、すべてプレースホルダーへ置き換えています。

目次

先に結論

今回満たしたかった要件は、次の 4 つです。

この要件には、Microsoft Entra External ID の外部テナントではなく、通常の Workforce tenant を対象にしたマルチテナント App Registration が合っていました。

サービス提供会社は App Registration を1つ所有します。利用会社がアプリへ同意すると、その利用会社のテナントに Service Principal が作成され、Microsoft Entra 管理センターでは Enterprise Application として管理できます。利用者自身は利用会社のテナントに所属する Member ユーザーのままで、サービス提供会社のテナントへ複製されません。

ただし、マルチテナントに設定しただけでは、任意の組織からの利用を防げません。今回の構成では、次の 3 層で利用範囲を制御しました。

制御する場所制御する対象今回の実装
利用会社のホームテナント本人確認とサインイン条件利用会社の MFA、Conditional Access、アカウントライフサイクルを利用
サービス提供会社の API利用を許可する組織検証済みアクセストークンの tid を Tenant ID 許可リストと照合
利用会社の Enterprise Applicationテナント内の利用者必要に応じて割り当て必須を有効化し、ユーザーまたはグループを割り当て

ブラウザでも Tenant ID を検査しましたが、これは未許可ユーザーに早く案内するための UX 改善です。セキュリティ境界は API 側のトークン検証と Tenant ID 検査に置きました。

Foundry への接続は、App Service に割り当てた User-assigned Managed Identity と Azure RBAC で認証し、Private Endpoint で到達経路を制限しました。利用者へ Foundry の API キーや Azure RBAC を渡す必要はありません。

今回の要件と構成

今回作ったのは、サービス提供会社が1つのシステムとして運用し、複数の利用会社がそれぞれ自社の Microsoft Entra ID アカウントでサインインできる軽量な AI チャットです。

サービス提供会社の Entra ID にあるマルチテナント App Registration と、利用会社 A・B の Entra ID に作成される Enterprise Application を介して、利用会社が管理する社員が自社アカウントのまま共通 Web アプリを利用する構成図
利用者は利用会社の Entra ID に所属したままです。サービス提供会社の App Registration に対する利用関係が、利用会社ごとの Enterprise Application として作成されます。
項目採用した構成
利用者許可した利用会社の Workforce tenant に所属する社員
Web アプリAzure App Service for Linux 上の ASP.NET Core 10
フロントエンドSPA、MSAL Browser、Authorization Code Flow with PKCE
APISPA と同じ App Service で配信する POST /api/chat
認証組織アカウント向けのマルチテナント App Registration
API の委任スコープaccess_as_user
組織の認可API で tid を Tenant ID 許可リストと照合
モデルMicrosoft Foundry にデプロイした gpt-4o-mini
Foundry 認証User-assigned Managed Identity と Azure RBAC
Foundry ネットワークPublic Network Access を無効化し、Private Endpoint 経由に限定
監視Application Insights と Log Analytics

通信の流れは次のとおりです。

  1. 利用者が所属する利用会社の Microsoft Entra ID でサインインする
  2. 利用会社のテナントが MFA や Conditional Access を評価する
  3. SPA が Web API 向けのアクセストークンを取得する
  4. API が署名、issuer、audience、有効期限、access_as_user、tid を検証する
  5. App Service が Managed Identity で Foundry 用のトークンを取得する
  6. App Service の VNet 統合と Private Endpoint を経由してモデルを呼び出す

Web アプリだけをインターネットへ公開し、Foundry のデータプレーンはパブリックネットワークから到達できない構成にしました。

App Registration と Enterprise Application の役割

この構成を理解するうえで重要なのが、App Registration と Enterprise Application の違いです。

Microsoft の公式ドキュメントでは、Application object はアプリのグローバルな定義、Service Principal は各テナントにおけるローカルな実体と説明されています。Application object と Service Principal は 1 対多の関係です。

オブジェクト作成される場所役割
Application objectサービス提供会社の所有テナントリダイレクト URI、公開スコープ、対応するアカウント種別など、アプリ共通の定義を持つ
Service Principalアプリを利用する利用会社ごとのテナント同意、ユーザー割り当て、サインイン制御など、そのテナント固有の設定を持つ
Enterprise ApplicationMicrosoft Entra 管理センター上の表示テナント内の Service Principal を管理する画面

利用会社の管理者が同意すると、その利用会社のテナントに Service Principal が作られます。これによって利用会社は、自社の Enterprise Application に対して利用者の割り当てやサインインの無効化を行えます。

ここで作られるのはアプリの実体であり、社員ユーザーのコピーではありません。利用者は自社テナントの Member ユーザーとしてサインインし続けます。

Microsoft Entra ID を構成する

サービス提供会社の Azure 管理テナントに、アプリ登録を1つ作成しました。

設定値
Name利用者が識別しやすいアプリ名(例:Group Corp AI Chat Demo)
Supported account typesMultiple Entra ID tenants
signInAudienceAzureADMultipleOrgs
Redirect URI のプラットフォームSingle-page application (SPA)
SPA Redirect URIMSAL の redirectUri と完全一致する https://<APP_HOST>/
API Identifier URIapi://<APPLICATION_ID>
委任スコープaccess_as_user
Who can consent?Admins and users
API permissions自身の API の委任された access_as_user
MSAL authorityhttps://login.microsoftonline.com/organizations
クライアントシークレット作成しない

現行の Microsoft Entra 管理センターでは、アプリ登録時の Supported account types に Multiple Entra ID tenants を選択します。これはマニフェストの signInAudience では AzureADMultipleOrgs に対応します。個人用 Microsoft アカウントまで許可する Any Entra ID Tenant + Personal Microsoft accounts は、利用会社の組織アカウントだけを対象にする今回の要件では選びません。

登録画面で Redirect URI を入力する場合は、プラットフォームに Single-page application (SPA) を選びます。ここは省略して登録し、後から Authentication で追加しても構いません。どちらの場合も、末尾の / やパスを含めて、SPA が認証要求に指定する Redirect URI と完全に一致させます。

SPA が要求するスコープは次の形です。

api://<APPLICATION_ID>/access_as_user

Expose an API でスコープを公開した後、API permissions、Add a permission、My APIs の順に開き、同じアプリの Delegated permissions から access_as_user を追加します。これによって、アプリが基本動作に必要とするアクセス許可として access_as_user が登録され、利用会社の管理者が同意内容を確認できます。アクセス許可を後から変更した場合、すでに同意した利用者やテナントでも再同意が必要になることがあります。

SPA はブラウザで動作する public client であり、クライアントシークレットを安全に保持できません。Authorization Code Flow with PKCE を使い、サインインした利用者として API 向けのアクセストークンを取得しました。

organizations authority は、任意の Microsoft Entra 組織アカウントでサインインできる入口です。ここに接続しただけでは、利用を許可する利用会社の一覧は表現できません。

許可するテナントを二段階で検査する

今回の API は、Microsoft.Identity.Web を使ってアクセストークンを検証しました。

Tenant ID の許可リストは App Service の設定へ配列として渡しました。

AllowedTenantIds__0=<TENANT_ID_A>
AllowedTenantIds__1=<TENANT_ID_B>
AllowedTenantIds__2=<TENANT_ID_C>

API では、フレームワークが検証したアクセストークンの Tenant ID クレームを許可リストと照合し、未許可なら HTTP 403 を返します。

スコープは Microsoft.Identity.Web の標準機能を使って検証しました。

app.MapPost("/api/chat", ChatAsync)
    .RequireAuthorization()
    .RequireScope("access_as_user");

Microsoft identity platform の公式ドキュメントでは、マルチテナント API がトークンを検証するとき、tid と iss を結び付け、署名鍵の issuer も確認する必要があると説明されています。単に JWT をデコードし、クレームの文字列だけを信頼してはいけません。

SPA でも、MSAL のサインイン結果に含まれる account.tenantId を確認しました。未許可組織ならアクティブアカウントを設定せず、チャット入力欄を有効にしません。ただし、ブラウザ側の処理は変更できるため、API 側の検査を省略する理由にはなりません。

トークン発行前に未許可組織を止める必要がある場合は、サインイン前に利用会社を選択させ、許可した Tenant ID 固有の authority へ誘導する設計も検討できます。

利用会社をオンボーディングする

新しい利用会社を追加するときは、次の手順で進めました。

  1. 利用会社の Tenant ID を確認する
  2. Tenant ID を Web アプリの AllowedTenantIds へ追加する
  3. 対象テナントの管理者が access_as_user へ同意する
  4. 対象テナントに Enterprise Application が作成されたことを確認する
  5. 許可した利用会社のアカウントでサインインし、API 呼び出しを確認する
  6. 未許可テナントからの API 呼び出しが HTTP 403 になることを確認する

管理者同意 URL は次の形式です。

https://login.microsoftonline.com/<TENANT_ID>/v2.0/adminconsent
  ?client_id=<APPLICATION_ID>
  &scope=api%3A%2F%2F<APPLICATION_ID>%2Faccess_as_user
  &redirect_uri=<URL_ENCODED_REDIRECT_URI>

実際の URL では改行を除きます。管理者同意は強い操作なので、利用会社の管理者に要求するスコープと用途を確認してもらいます。Microsoft の公式ドキュメントでも、テナント全体の管理者同意は、要求されるアクセス許可を慎重にレビューしてから行うよう案内されています。

利用者を利用会社ごとに限定する

Tenant ID の許可リストだけでは、許可した利用会社の中で誰が利用できるかまでは絞りません。特定の社員やグループだけに限定する場合は、利用会社ごとのテナントにある Enterprise Application で設定します。

  1. Microsoft Entra 管理センターで Entra ID、Enterprise applications を開く
  2. 対象アプリの Properties を開く
  3. Assignment required? を Yes にする
  4. Users and groups から利用者またはグループを割り当てる
  5. 割り当てたユーザーと未割り当てユーザーの両方で結果を確認する

割り当て必須を有効にすると、割り当てられていない利用者はサインインやアクセストークンの取得を拒否されます。また、公式ドキュメントでは、この設定を有効にしたアプリではユーザー同意が許可されないため、テナント全体の管理者同意が必要になると説明されています。

アプリ独自の役割まで API で強制したい場合は、App Registration にアプリロールを定義し、アクセストークンの roles クレームを検証します。その場合は、利用会社ごとの Enterprise Application で利用者やグループへのロール割り当ても必要です。

今回のデモではテナント横断の動作確認を優先し、API の必須条件は access_as_user と Tenant ID 許可リストにしました。利用者単位の制御は利用会社ごとの Enterprise Application で追加できる形です。

Foundry をキーなし、閉域で呼び出す

エンドユーザーの認証と、App Service から Foundry への認証は分けて考えます。

エンドユーザーのアクセストークンは Web API を呼ぶためのものです。Foundry の呼び出しには転送せず、App Service に割り当てた User-assigned Managed Identity を使いました。

  1. App Service に User-assigned Managed Identity を割り当てる
  2. Foundry リソースでローカルキー認証を無効化する
  3. Managed Identity に Cognitive Services OpenAI User ロールを割り当てる
  4. アプリから DefaultAzureCredential で Microsoft Entra トークンを取得する
  5. Foundry の Public Network Access を無効化する
  6. App Service を専用サブネットへ VNet 統合する
  7. 別サブネットに Foundry の Private Endpoint を作成する
  8. 必要な Private DNS Zone と VNet Link を構成する

Microsoft の公式ドキュメントでは、Foundry Models へのキーレス認証に Microsoft Entra ID と Azure RBAC を使い、互換性を確認したうえでキー認証を無効化する方法が案内されています。また、App Service の VNet 統合はアプリから VNet への送信アクセスを提供します。

Managed Identity は「どのワークロードが呼べるか」、Private Endpoint は「どのネットワーク経路から到達できるか」を制御します。片方だけで代用せず、認証とネットワークの両方を組み合わせました。

この構成では、エンドユーザーへ次の情報や権限を渡しません。

External ID を採用しなかった理由

Microsoft Entra External ID の外部テナントも検討しましたが、今回の利用者は一般消費者ではなく、利用会社の Workforce tenant に所属する社員です。

判断軸マルチテナント App RegistrationExternal ID の外部テナント
主な利用者他社を含む Microsoft Entra 組織アカウント自社アプリを使う顧客や一般利用者
ユーザーの所在利用会社の Workforce tenant に残るExternal tenant 内に顧客ユーザーを作成する
利用会社の MFA や Conditional Access利用会社のホームテナントで運用しやすいExternal tenant の顧客向けポリシーとして別設計する
利用会社ごとの利用者制御利用会社の Enterprise Application で割り当てるExternal tenant 内で一元管理する
今回の要件との相性利用会社の ID をそのまま使う要件に合う別ディレクトリへユーザーを作るため合わない

External ID が劣っているわけではなく、対象者と ID の管理主体が異なります。セルフサービスで顧客を登録するアプリなら External ID、既存の組織アカウントを利用会社の管理下で使うならマルチテナント App Registration、という切り分けにしました。

External ID の用途と実装は、Microsoft Entra External ID 入門(2026年版)とメール OTP で登録できる認証基盤を構築した記事で詳しくまとめています。

実装時につまずいたポイント

認証後に HTTP 403 になる

サインインと JWT 検証は成功しているのに、チャット API へ入る前に HTTP 403 になりました。原因は 2 つありました。

デモでは必須条件を access_as_user と Tenant ID 許可リストに整理し、スコープ検証には Microsoft.Identity.Web の RequireScope を使いました。Tenant ID は、実際にフレームワークが生成した Claim 一覧を確認してから取得方法を決めました。

設定へ追加した Tenant ID が反映されない

許可リストを App Service の設定へ追加しても、稼働中のアプリが古い一覧を返し続けることがありました。

配列全体を一括で保存して App Service を再起動し、実行時の GET /api/config で読み込まれた Tenant ID を確認して解消しました。設定値そのものだけでなく、アプリが実際に読み込んだ値を確認できる診断 API が役立ちました。

診断 API で Tenant ID を返す場合は、認証済みの管理者だけに限定する、値をマスクする、または検証後に無効化するなど、環境情報の露出を抑える必要があります。

Foundry とモデルのデプロイが競合する

Bicep を再適用した際、Foundry アカウントが更新中のままモデル子リソースの更新が始まり、AccountProvisioningStateInvalid になりました。

モデルデプロイを Foundry project の完了後へ明示的に依存させ、アカウントとモデルの更新を直列化して解消しました。

リージョンをモデルの可用性だけで決められない

当初選んだリージョンでは、対象サブスクリプションの App Service Total Regional VMs クォータが 0 でした。モデルをデプロイできても Web アプリを配置できないため、App Service とモデルの両方を利用できるリージョンへ変更しました。

デプロイ前に、モデルのリージョンとクォータだけでなく、App Service Plan のリージョンクォータも確認します。

動作確認

今回のデモでは、次の項目を確認しました。

特に、許可した利用者が成功するテストだけでなく、未認証、未許可テナント、未割り当てユーザーが拒否されることも確認します。

本番化で追加検討すること

今回のデモでは、RAG、ファイルアップロード、会話履歴の永続化を対象外にしました。本番へ進める場合は、少なくとも次を追加で検討します。

複数の利用会社を受け入れる構成では、追加手順だけでなく、契約終了や組織再編時のオフボーディング手順も最初から用意しておく必要があります。

まとめ

サービス提供会社と利用会社の Microsoft Entra ID テナントが異なっていても、マルチテナント App Registration を使えば、利用者をサービス提供会社のテナントへ複製せずに、複数の利用会社へ1つのシステムを提供できます。

設計上の要点は次の 5 つです。

  1. App Registration はサービス提供会社が所有し、利用会社ごとに Service Principal が Enterprise Application として作られる
  2. 利用者は利用会社の Member ユーザーのままで、利用会社の MFA、Conditional Access、アカウント管理を利用する
  3. organizations authority だけに頼らず、API で検証済みトークンの tid を許可リストと照合する
  4. 個別ユーザーの制限は利用会社の Enterprise Application で割り当て必須を有効化する
  5. Foundry は Managed Identity による認証と Private Endpoint によるネットワーク制御を組み合わせる

「外部ユーザーだから External ID」と決めるのではなく、既存の組織 ID を誰が管理し続けるべきかで方式を選ぶと、今回のように複数のグループ会社で1つのシステムを共通利用する場合でも、サービス提供会社と利用会社の責任境界を整理しやすくなります。

公式情報源