tech#entra-id#external-id#ciam#azure#authentication

デモ利用者がメール OTP で登録できる認証基盤を Microsoft Entra External ID で構築した

公開 👁
デモ利用者がメール OTP で登録できる認証基盤を Microsoft Entra External ID で構築した

データを登録して後から確認するデモを考えていたとき、「登録した本人だけが自分のデータを見られる状態」が必要になりました。

この要件には認証が必要です。ただし、デモを使う人を管理者が毎回登録する運用にはしたくありません。利用者が自分で登録でき、パスワードを新しく作る必要もない構成にしたかったため、Microsoft Entra External ID の外部テナントとメールのワンタイムパスコード(OTP)を採用しました。

この記事では、実際に外部テナントを作り、React SPA と Azure Functions API を接続するまでに行った設定をまとめます。テナント ID、アプリケーション(クライアント)ID、Azure サブスクリプション、ホスト名などの環境固有情報は、すべてプレースホルダーへ置き換えています。

目次

先に結論

今回の構成では、デモ利用者のアカウントを社員用の Microsoft Entra ID テナントに混在させず、External ID の外部テナントへ分離できました。デモの利用者は、メールアドレスへ届く OTP を使って自分で登録できます。新しいパスワードを作って覚える必要もありません。

この構成では、フロントエンドと API を別々のアプリとして Microsoft Entra ID に登録します。SPA は Single Page Application の略で、今回の React で作ったブラウザアプリを指します。

アプリ登録役割
SPA認証画面を呼び出し、サインイン後の戻り先となるリダイレクト URI を定義する。利用者として Web API を呼ぶためのアクセストークンを取得する
Web APIアクセストークンの宛先となる API と、access_as_user などのアクセス許可を定義する。受け取ったトークンが自分宛てで、必要な権限を持つか検証する

つまり、External ID の認証画面を呼び出す主体は SPA です。Web API のアプリ登録は認証画面を呼ぶためではなく、SPA が要求する API の宛先とアクセス許可を Microsoft Entra ID に知らせるために使います。

構築に必要だった作業は、次の7つです。

  1. 作成元のディレクトリと Azure サブスクリプションを確認し、Tenant Creator を準備する
  2. 外部テナントを作成し、そのディレクトリへ切り替える
  3. Web API と SPA を別々にアプリ登録する
  4. API に委任されたアクセス許可のスコープを公開し、SPA へ追加する
  5. メール OTP を使うサインアップとサインインのユーザーフローを作成する
  6. SPA をユーザーフローへ関連付ける
  7. SPA でアクセストークンを取得し、API でトークンとデータの所有者を検証する

大事なのは、認証を追加しただけでは、本人のデータだけに制限できないことです。API では、検証済みアクセストークンに含まれる変更されない ID をデータの所有者として保存します。データの読み書きもその ID で絞り込むことで、初めて本人のデータだけを扱えます。

External ID の用途や、社員用テナント、B2B コラボレーションとの違いは、Microsoft Entra External ID 入門(2026年版)で整理しています。

今回の要件と構成

今回作ったのは、デモの利用者が自分で登録してサインインするための認証基盤です。取引先を社員用テナントへゲストとして招待する B2B コラボレーションではありません。

項目採用した構成
利用者デモへ自分で登録する一般利用者
認証基盤Microsoft Entra External ID の外部テナント
認証画面Microsoft がホストするサインイン画面
サインイン方法メール OTP
クライアントReact SPA、Authorization Code Flow + PKCE
APIAzure Functions の Web API
アクセス許可委任された access_as_user スコープ
所有者キーアクセストークンの oid クレーム
登録時の収集項目メールアドレスのみ
クライアントシークレットSPA と API のどちらにも作成しない

SPA と API は別々にアプリ登録しました。SPA はサインインした利用者として API を呼び出します。API は、自分宛てに発行されたアクセストークンだけを受け入れます。

構築前に決めたこと

ポータルを操作する前に、次の値と運用担当を決めました。

項目判断したこと
作成元ディレクトリサブスクリプションを所有し、テナント作成が許可されたディレクトリ
課金先外部テナントに関連付けるサブスクリプションとリソースグループ
テナント名管理画面で用途を識別できる名前
ドメイン名<TENANT_SUBDOMAIN>.onmicrosoft.com のサブドメイン
国/地域Japan。作成後に変更できない
Go-Local今回は利用しない
アプリ名SPA と API を識別できる別々の名前
リダイレクト URIローカル環境と本番環境で SPA が実際に使う URI
運用担当アプリ、ユーザーフロー、デモ利用者のアカウント、緊急復旧の担当者

2026年8月14日時点の公式手順では、外部テナントを作成するには、Microsoft Entra の組み込みディレクトリロールである Tenant Creator が必要です。これは Azure RBAC ロールではありません。ただし、公式手順では Tenant Creator を Azure サブスクリプションまたはリソースグループのスコープで割り当てます。

一方、Owner は Azure サブスクリプションなどのリソースを管理する Azure RBAC ロールです。2つは異なるロール体系のため、Azure サブスクリプションの Owner があっても、Tenant Creator がなければ外部テナントを作成できません。

国/地域は後から変更できません。また、Japan を選ぶと、有料の Go-Local データ所在地オプションが表示されます。正式導入前に、組織の要件と公式のデータ所在地情報を確認します。

外部テナントを作成する

外部テナントは Microsoft Entra 管理センターから作成しました。

  1. 作成元ディレクトリの管理者アカウントで Microsoft Entra 管理センターへサインインする
  2. Entra ID概要テナントの管理を開く
  3. 作成を選び、テナントの種類で 外部を選択する
  4. Azure サブスクリプションを使用を選択する
  5. テナント名、ドメイン名、国/地域を入力する
  6. サブスクリプションとリソースグループを選択する
  7. 確認と作成で内容を確認して作成する
Microsoft Entra 管理センターのテナント作成画面で外部を選択した状態
テナントの構成で「外部」を選択する

公式ドキュメントでは、作成に最大30分かかる場合があると案内されています。作成後は、設定ディレクトリとサブスクリプションから新しい外部テナントへ切り替えます。

テナントの概要で、後からアプリ設定に使う値を控えます。

Tenant name    : <EXTERNAL_TENANT_NAME>
Primary domain : <TENANT_SUBDOMAIN>.onmicrosoft.com
Tenant ID      : <EXTERNAL_TENANT_ID>
Microsoft Entra 管理センターに表示された外部テナントの概要
外部テナントの概要でテナント ID とプライマリドメインを確認する

テナント ID やクライアント ID はシークレットではありませんが、環境の特定につながる値です。公開資料では実値を使いません。また、クライアントシークレット、アクセストークン、OTP、利用者のメールアドレスは記録しません。

Web API をアプリ登録する

先に、SPA から呼び出される Web API を登録しました。

  1. 外部テナントへ切り替えた状態で、Entra IDアプリの登録新規登録を開く
  2. API の用途が分かる名前を入力する
  3. この外部テナント内のアカウントだけを対象として登録する
  4. API の公開で Application ID URI を api://<API_CLIENT_ID> にする
  5. スコープの追加から、利用者として API を呼び出すための委任スコープを作成する
Microsoft Entra 管理センターのアプリケーション登録画面
外部テナント内のアカウントだけを対象にアプリを登録する

今回のスコープは次のようにしました。

項目
Scope nameaccess_as_user
Who can consentAdmins and users
Admin consent display nameAccess the demo API
User consent display nameAccess the demo API
StateEnabled

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

api://<API_CLIENT_ID>/access_as_user

今回は、API が SPA から受け取ったアクセストークンを検証するだけなので、API のアプリ登録にクライアントシークレットは作成していません。

クライアントシークレットは、サーバー側のアプリが Microsoft Entra ID に対して「このアプリ自身からの要求である」と証明し、トークンを取得するときに使う資格情報です。たとえば、利用者がサインインしないバッチ処理やバックエンド間通信で client credentials flow を使う場合、サーバー側の Web アプリが認証処理を行う場合、API が利用者の代理で別の API を呼び出す On-Behalf-Of flow などで必要になります。

ただし、Azure 上で動くアプリでは、保存や定期更新が必要なクライアントシークレットより、マネージド ID や証明書などの漏えいしにくい方法を優先します。

SPA をアプリ登録する

次に、API を呼び出す SPA を登録しました。

  1. アプリの登録新規登録を開く
  2. SPA の用途が分かる名前を入力する
  3. この外部テナント内のアカウントだけを対象として登録する
  4. 認証プラットフォームの追加シングルページ アプリケーションを選択する
  5. ローカル環境と本番環境のリダイレクト URI を登録する
http://localhost:<LOCAL_PORT>
https://<APP_HOST>/auth/callback

リダイレクト URI は、スキーム、ホスト、ポート、パス、末尾のスラッシュまで、SPA が送る redirectUri と一致させます。SPA はブラウザへ配信されるため、シークレットを安全に保持できません。コードへ埋め込んでも利用者から確認できてしまうため、SPA にはクライアントシークレットを作成しません。

続いて、API のアクセス許可アクセス許可の追加所属する組織で使用している APIから先ほどの API を選び、委任されたアクセス許可の access_as_user を追加します。Microsoft Graph を利用しない場合は、既定で追加された User.Read が必要かどうかも確認します。

サインアップとサインインのユーザーフローを作成する

メール OTP を利用するため、最初に External Identitiesすべての ID プロバイダーを開き、テナント全体で Email One-time Passcode が有効になっていることを確認しました。

Microsoft Entra 管理センターの External Identities 画面でユーザー フローを選択する場所
External Identities のメニューから ID プロバイダーとユーザー フローを設定する
  1. External Identitiesユーザーフロー新しいユーザーフローを開く
  2. ユーザーフローの名前を入力する
  3. ID プロバイダーで Email AccountsEmail one-time passcodeを選択する
  4. 利用者から収集する属性は Email Addressだけを選択して作成する
  5. 作成したユーザーフローの 使用アプリケーションを開く
  6. アプリケーションの追加から SPA を選択する

1つのユーザーフローには複数のアプリを関連付けられます。一方、1つのアプリを関連付けられるユーザーフローは1つだけです。

アプリ一覧にある b2c-extensions-app は、外部テナントのカスタム属性を保持するために自動作成されるアプリです。今回カスタム属性を使わなくても削除しません。

アプリへ認証設定を反映する

OIDC の接続先は推測せず、外部テナントの OpenID configuration から issuerjwks_uri を取得します。

https://<TENANT_SUBDOMAIN>.ciamlogin.com/<EXTERNAL_TENANT_ID>/v2.0/.well-known/openid-configuration

アプリへ設定する値は次のとおりです。

authority : https://<TENANT_SUBDOMAIN>.ciamlogin.com/<EXTERNAL_TENANT_ID>
clientId  : <SPA_CLIENT_ID>
scope     : api://<API_CLIENT_ID>/access_as_user
audience  : <API_CLIENT_ID>
issuer    : <OPENID_CONFIGURATION_ISSUER>
jwksUri   : <OPENID_CONFIGURATION_JWKS_URI>

SPA は Authorization Code Flow + PKCE でアクセストークンを取得します。API は OpenID configuration から得た署名鍵と発行者情報を使い、少なくとも署名と isstidaudexpnbfscp を検証します。

API へ送るのは、その API 向けに発行されたアクセストークンです。ID トークンは、SPA が利用者のサインイン状態を確認するためのものです。API のアクセス制御には使いません。

本人のデータだけを返す

サインインに成功しただけでは、利用者 A が利用者 B のデータを読めないことまでは保証されません。API 側でも、誰のデータなのかを確認する必要があります。

今回は、検証済みアクセストークンの oid クレームを所有者 ID として保存しました。

ownerId = verifiedAccessToken.oid

データを登録するときは、リクエスト本文で送られた所有者 ID を信用しません。検証済みトークンから取得した oid をデータに保存します。取得、更新、削除でも、指定されたデータ ID だけで検索せず、必ず ownerId を検索条件へ追加します。

resourceId = <REQUEST_RESOURCE_ID>
ownerId    = verifiedAccessToken.oid

メールアドレスは変更される可能性があるため、所有者キーには使いません。oid はテナント内で一意の値です。複数のテナントからトークンを受け入れる場合は、テナントを表す tid と組み合わせて扱います。今回は、1つの外部テナントから発行されたトークンだけを API で許可します。

動作確認

正常系では、次の流れを確認しました。

  1. 未登録のテスト用メールアドレスでサインアップを開始する
  2. 受信した OTP で登録し、SPA へ戻れることを確認する
  3. サインアウト後、同じアカウントでサインインできることを確認する
  4. SPA が access_as_user スコープのアクセストークンを取得できることを確認する
  5. API がトークンを検証し、本人のデータだけを返すことを確認する
  6. サインアウト後に認証キャッシュが削除され、保護された画面へ戻れないことを確認する

拒否系もあわせて確認します。

つまずいたポイント

外部テナントの作成ボタンが無効になる

今回、Azure RBAC の Owner があっても、外部テナントを作成できませんでした。Microsoft Entra の組み込みディレクトリロールである Tenant Creator は、Owner とは別に必要です。確認すべきなのは、次の4点です。

  1. ポータルで現在選択しているディレクトリ
  2. 作成元ディレクトリでテナント作成が許可されているか
  3. Microsoft Entra ロールの Tenant Creator が割り当てられているか
  4. Tenant Creator のスコープと、対象のサブスクリプションまたはリソースグループへのアクセス

複数のディレクトリへ参加しているアカウントでは、権限だけでなく、画面右上でどのディレクトリを選択しているかも確認します。

redirect URI mismatch になる

SPA のオリジンと /auth/callback を混在させず、認証ライブラリの redirectUri とアプリ登録の値を文字単位で比較します。localhost のポート、HTTP / HTTPS、末尾のスラッシュも確認します。

API がアクセストークンを受け取っても 401 になる

次の順序で確認します。

トークンの中身や個人情報を、公開記事、Issue、作業ログへ貼り付けないようにします。

作業後に確認すること

PoC の動作確認後も、認証基盤として運用するための作業が残ります。

デモ API には、ディレクトリ内の利用者を削除できる Microsoft Graph のアクセス許可を付与していません。デモ利用者のアカウント削除は、管理者が行う別の手順に分けました。

まとめ

デモで登録した本人だけがデータを見られるようにするため、Microsoft Entra External ID の外部テナント、メール OTP のユーザーフロー、SPA と API のアプリ登録を構成しました。

External ID を使うと、利用者のセルフ登録とサインインをアプリから分離できます。ただし、サインインに成功しただけでは、本人のデータだけを返すことはできません。API でトークンを検証し、oid を所有者キーとしてすべてのデータ操作を制限する必要があります。

構築時は、現在選択しているディレクトリ、Tenant Creator、API のスコープ、SPA のアクセス許可、ユーザーフローとの関連付けを順番に確認すると、設定漏れを見つけやすくなります。

公式情報源