Microsoft Entra External IDでアクセストークンにカスタムクレームを追加する方法(TokenIssuanceStart/カスタム クレーム プロバイダー)

Microsoft Entra External ID の TokenIssuanceStart(カスタム クレーム プロバイダー/カスタム認証拡張)で外部システムの値をトークンに追加したのに、ID トークンには入るのにアクセストークンには入らない――その原因は多くの場合「どのアプリ(=トークンの宛先)に拡張を割り当てたか」と「SPA がどのスコープでアクセストークンを取得しているか」です。この記事では、アクセストークンへカスタムクレームを載せる具体的な設定手順と、つまずきやすいポイントを整理します。

目次

まず押さえる:ID トークンとアクセストークンは「宛先」が違う

Microsoft Entra External ID(および Entra ID)のトークン拡張で混乱が起きる最大のポイントは、ID トークンとアクセストークンがそもそも別物だという点です。

ID トークンは「サインインしたユーザー情報をクライアント(SPA など)へ伝える」ためのトークンで、アクセストークンは「特定の Web API を呼ぶ権限を示す」ためのトークンです。つまり、アクセストークンは必ず “呼び出し先 API” 宛てに発行されます。

種類主な用途代表的な宛先(aud)クレームを足すときの考え方
ID トークンクライアントがユーザーを識別(ログイン状態の確立)クライアント アプリ(SPA)のアプリ ID「クライアントに見せたい属性」を足す
アクセストークンWeb API の保護(API へのアクセス権を提示)Web API(リソース アプリ)のアプリ ID URI / クライアント ID「API が判断に使う属性」を足す(認可・テナント判定など)

結論だけ先に言うと、TokenIssuanceStart のカスタム クレーム プロバイダーで追加したクレームをアクセストークンに載せることは可能です。ですが、そのためにはアクセストークンの宛先(aud)になる “Web API 側” を正しく用意し、そこに拡張を割り当てる必要があります。

「ID トークンには入るのにアクセストークンには入らない」典型パターン

現場で多いのは次のパターンです。

  • TokenIssuanceStart の拡張を作り、クライアント(SPA)のエンタープライズ アプリに割り当てた
  • SPA でログインすると、ID トークンには外部 API 由来のクレームが見える
  • しかし SPA から API を呼ぶためのアクセストークンには、そのクレームが見えない

このとき「アクセストークンにも同じように optionalClaims で指定すれば出せるのでは?」と考えがちですが、カスタム クレーム プロバイダー由来のクレームは optionalClaims の対象とは考え方が違うため、ここで詰まります。

重要:TokenIssuanceStart のカスタム クレーム プロバイダーは、“どの宛先のトークンを拡張するか”で割り当て先が変わります。アクセストークンに入れたいなら、クライアントではなく、アクセストークンの宛先となる Web API(リソース)側に割り当てます。

アクセストークン拡張の選択肢は大きく 2 つ

「アクセストークンに追加情報を含める」やり方は、実務上は次の 2 方式に整理すると分かりやすいです。

方式何をしているか向いているケース注意点
カスタム属性(拡張属性)+ optional claimsディレクトリに属性を保存し、トークン発行時に取り出して載せる外部 ID、会員番号など「頻繁に変わらない値」値を事前に同期・更新する仕組みが必要
カスタム クレーム プロバイダー(TokenIssuanceStart)トークン発行時に外部 REST API を呼び、返ってきた値を載せるサインイン時に「常に最新の外部情報」が欲しい、動的な判定をしたい外部 API の安定性・遅延がサインイン体験に影響

受理された回答で紹介されがちな「カスタム属性+ optional claims」は、アクセストークンにクレームを出す “定番” です。一方、今回の主題である TokenIssuanceStart の拡張は、割り当ての考え方(宛先がどちらか)を外すと ID トークンにしか入らないように見えてしまいます。

最優先で確認:そのアクセストークンの aud は本当に「自分の Web API」か?

「アクセストークンにクレームが入らない」とき、まず最初にやるべきことは、トークンをデコードしてaud(Audience)を確認することです。

  • aud が Microsoft Graph(または SharePoint など Microsoft 提供 API)になっている
  • aud が自分で登録した Web API(Expose an API したアプリ)になっている

この違いは決定的です。一般に、Microsoft が提供する API 宛てのアクセストークンに、任意のカスタムクレームを差し込むことは期待どおりに動きません。TokenIssuanceStart の拡張で制御できるのは、原則として自分で登録したリソース(自分の Web API)宛てに発行されるトークンだと理解しておくのが安全です。

逆に言うと、SPA が取得しているアクセストークンが Graph 宛てだった場合、どれだけ Web API 側の設定を頑張ってもクレームは出ません。まず “宛先” を正すのが近道です。

実践:SPA → Web API のアクセストークンにカスタムクレームを入れる手順

ここからは、TokenIssuanceStart のカスタム クレーム プロバイダーで取得した外部情報を、SPA が Web API を呼ぶためのアクセストークンに含めるための、実務で使える流れをまとめます。

Web API 側:Expose an API を設定して「宛先」を作る

アクセストークンは “API 宛て” に発行されるので、まずは Web API 用のアプリ登録で API を公開します。

  • Web API 用にアプリ登録を用意する(既存があればそれを使う)
  • 「Expose an API」で Application ID URI を設定する(例:api://{api-client-id})
  • スコープを作る(例:user_impersonation)

ここが “アクセストークンの aud を自分の API にする” ための土台です。以後、SPA はこの API スコープを指定してトークンを取りに行きます。

SPA 側:Graph ではなく「自分の API スコープ」を要求する

SPA(MSAL など)でトークンを取得するとき、要求するスコープが Graph になっていると、発行されるアクセストークンの aud も Graph になります。必ず、自分の API のスコープを指定します。

// 例:MSAL(概念例)
// 実際の authority / knownAuthorities / clientId などは環境に合わせて設定します。

const request = {
  scopes: ["api://{APIのクライアントID}/user_impersonation"]
};

// まずはサイレント取得(キャッシュ)→ 失敗したら対話取得、が一般的
let result;
try {
  result = await msalInstance.acquireTokenSilent(request);
} catch (e) {
  result = await msalInstance.acquireTokenPopup(request);
}

const accessToken = result.accessToken;
// この accessToken を Authorization: Bearer で Web API に送る

この時点で、取得したアクセストークンの aud が api://{APIのクライアントID}(または API の識別子)になっているかを確認してください。ここがズレていると、以降の設定をしても期待する結果になりません。

割り当て先が本丸:カスタム クレーム プロバイダーは「Web API の Enterprise application」へ

TokenIssuanceStart のカスタム クレーム プロバイダーは、トークンの宛先(aud)に基づいて割り当てるのが要点です。

  • ID トークンに入れたい → クライアント(SPA)側に割り当てる
  • アクセストークンに入れたい → リソース(Web API)側に割り当てる

具体的には、Entra 管理センターで Enterprise applications(エンタープライズ アプリケーション)から Web API のサービスプリンシパル(=エンタープライズ アプリ)を開き、シングル サインオンの設定(属性とクレーム周り)にある詳細設定からカスタム クレーム プロバイダーを割り当てます。

ここを SPA 側に割り当てたままだと、「ID トークンにだけ入る」という現象が起きやすくなります。

クレーム マッピング:外部 API の戻り値をトークンのクレーム名へ結び付ける

外部 REST API が返す属性を、トークンに載せる “クレーム名” にマッピングします。設計上は、次の 2 点を意識すると後で楽になります。

  • クレーム名は短く、意味がブレない命名にする(例:crm_id、tier、tenant_key)
  • 個人情報そのものを詰め込まず、API 側で参照できる “キー” を載せる(例:誕生日そのものではなく会員ID)
外部 API の属性(例)トークンに載せるクレーム名(例)用途設計メモ
externalUserIdcrm_idCRM の顧客識別子で認可・紐付け最小限のキーだけ載せる
membershipTiertier機能制限(Free/Pro など)値の取り得る範囲を固定する
tenantSegmentsegmentテナント別のルーティングログに出ても問題ない粒度に

Web API アプリの manifest:マップされたクレームを受け入れる設定を揃える

アクセストークン側にクレームを反映する場合、Web API(リソース アプリ)の設定として、マップされたクレームを受け入れるための項目を揃える必要があります。代表的には次のような設定が論点になります。

  • acceptMappedClaims:マッピングされたクレームを許可する
  • アクセストークンのバージョン:v2 トークンを前提にする(ポータル UI / manifest のキー名は環境で見え方が異なる場合があります)

manifest 例(重要部分のみ・概念例):

{
  "acceptMappedClaims": true,
  "accessTokenAcceptedVersion": 2
}

環境やアプリ種別によっては、関連するキー名として requestedAccessTokenVersion が出てくるケースもあります。UI の更新で表現が変わることがあるため、目的は次の 2 つだと押さえてください。

  • アクセストークンを v2 前提で扱う
  • クレーム マッピング(カスタム追加)を許可する

外部 REST API:TokenIssuanceStart で返す値は「速く・小さく・安定して」

TokenIssuanceStart では、トークン発行のタイミングで外部 REST API が呼ばれ、そのレスポンスがクレームとして取り込まれます。ここで重要なのは、外部 API がログイン体験の一部になるということです。

  • レスポンスはできるだけ小さくする(JWT が肥大化すると扱いづらくなります)
  • 処理時間は短く(遅延はそのまま認証の遅延になります)
  • 可用性を上げる(障害時にサインイン全体へ影響し得ます)

外部 API のレスポンス例(概念例):

{
  "claims": {
    "crm_id": "C12345678",
    "tier": "pro"
  }
}

実際のリクエスト/レスポンス形式は、拡張の作り方(ポータル/Graph)やドキュメントにより細部が変わり得ます。ですが、設計の勘所は一貫しており、「トークンに入れたい最小限のキーだけ返す」のが安全です。

「カスタム属性+ optional claims」方式でアクセストークンに出す手順(静的な値向け)

外部情報が “毎回リアルタイムで変わる” のでなければ、ディレクトリに保存して optional claims で出す方が運用は安定します。受理された回答の方針も、現場では合理的です。

カスタム属性(拡張属性)を作る

Entra External ID テナントでユーザー属性(拡張属性)を作成し、外部システム ID などを保存できるようにします。拡張属性は一般に extension_... の形式で参照されます(実際のプレフィックスはテナント/アプリに依存します)。

optionalClaims でアクセストークンに含める

対象アプリ登録の Token configuration もしくは manifest で、アクセストークンに含めたい拡張属性を optional claims として要求します。

{
  "optionalClaims": {
    "accessToken": [
      {
        "name": "extension_yourCustomAttribute",
        "source": null,
        "essential": false,
        "additionalProperties": []
      }
    ]
  }
}

この方式は「値がディレクトリに存在する」ことが前提なので、外部システム側の変更をどう同期するか(プロビジョニング、バッチ、Web フックなど)が設計ポイントになります。

両方式の使い分け:迷ったときの判断基準

どちらを選ぶべきか迷う場合は、次の表で判断すると設計がブレにくくなります。

判断軸カスタム属性+ optional claimsカスタム クレーム プロバイダー(TokenIssuanceStart)
値の更新頻度低いほど向く高いほど向く
外部 API 依存基本なし(同期時のみ)常にあり(発行タイミングで呼ばれる)
ログイン体験への影響小さい外部 API の遅延/障害が影響し得る
トークンの一貫性ディレクトリの値に一致発行時点の外部 API 応答に一致
実装難易度低〜中中〜高(外部 API の運用が前提)

現実的には、次のようなハイブリッドもよく採用されます。

  • ほぼ固定の値(会員IDなど)はディレクトリに保持し optional claims で出す
  • サインイン時にだけ必要な判定(特典やセグメントなど)は TokenIssuanceStart で動的に付与する

アクセストークンにクレームが入らないときの切り分けチェックリスト

最後に、つまずきやすいポイントを症状別にまとめます。設定をいじる前に、まずはトークンをデコードして事実確認をすると早いです。

症状よくある原因対処
ID トークンには入るがアクセストークンには入らない拡張の割り当て先が SPA 側になっているWeb API(リソース)側の Enterprise application に割り当て直す
アクセストークンの aud が自分の API ではないSPA が Graph など別リソースのスコープを要求しているMSAL の scopes を api://{api-client-id}/... に変更する
設定を変えたのにトークンが変わらないトークンがキャッシュされているサイレント取得だけでなく対話取得を試す/スコープを見直す
外部 API を呼んでいる形跡がない拡張が割り当たっていない/対象のフローで発火していない割り当て先(Enterprise application)と対象ユーザー/アプリ範囲を再確認
外部 API が時々失敗してログインが不安定外部 API の可用性・タイムアウト・負荷タイムアウト短縮、キャッシュ、スケール、監視、フェイル設計を入れる

運用で差が出る:セキュリティと設計のコツ

アクセストークンにクレームを増やすと、API 側の認可がシンプルになり、外部参照も減らせます。一方で、トークンはログや解析ツールに出回りやすい性質もあるため、次の点を守ると安全です。

  • トークンに個人情報を詰め込まない:どうしても必要なら最小限にし、できれば参照キー(ID)にする
  • クレーム名・値の仕様を固定する:API と SPA の両方で使う場合、変更は破壊的になりやすい
  • 外部 API は “認証基盤の一部” として扱う:監視、アラート、スケール、障害時の影響範囲を設計する
  • サイズを意識する:JWT が肥大化するとヘッダーサイズ制限やログ肥大の原因になる

まとめ:アクセストークンへ載せる鍵は「宛先(aud)と割り当て先」

  • TokenIssuanceStart のカスタム クレーム プロバイダーでも、アクセストークンにクレームを追加することは可能
  • ただし、アクセストークンは “Web API 宛て” に発行されるため、Web API(リソース アプリ)側に拡張を割り当てるのが基本
  • SPA 側は、必ず自分の API スコープを要求して、アクセストークンの aud を自分の API にする
  • 値が静的なら「カスタム属性+ optional claims」、動的なら「カスタム クレーム プロバイダー」と、性質で使い分けると運用が安定

参考(ドキュメント名で探すときの手掛かり)

本文の設定箇所は Microsoft Learn 上で次のキーワードで辿れます。URL を管理したい場合は、下記を控えておくと便利です。

Microsoft Learn: How to get a custom claim inside the access token - Microsoft Q&A
https://learn.microsoft.com/en-ie/answers/questions/2116520/how-to-get-a-custom-claim-inside-the-access-token

Microsoft Learn: Add attributes to token claims - Microsoft Entra External ID
[https://learn.microsoft.com/en-us/entra/external-id/customers/how-to-add-attributes-to-token](https://learn.microsoft.com/en-us/entra/external-id/customers/how-to-add-attributes-to-token)

Microsoft Learn: Configure optional claims - Microsoft identity platform
[https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims)

Microsoft Learn: Custom claims provider: Configure a token issuance event - Microsoft identity platform
[https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-tokenissuancestart-configuration](https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-tokenissuancestart-configuration)

Stack Overflow: How do I add claims from my custom claims provider to Entra External ID/Azure AD access tokens?
[https://stackoverflow.com/questions/77060652/how-do-i-add-claims-from-my-custom-claims-provider-to-entra-external-id-azure-ad](https://stackoverflow.com/questions/77060652/how-do-i-add-claims-from-my-custom-claims-provider-to-entra-external-id-azure-ad)

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次