Azure API Managementで、異なるIdP間のOBO flowを実現できるのか?

このエントリは2026/03/27現在の情報に基づいています。将来の機能追加や変更に伴い、記載事項からの乖離が発生する可能性があります。

以前以下のようなエントリを書いたが、それを読んだ人から問い合わせをもらったのが、このエントリを書くきっかけ。

主の質問は以下の通り。

入口のJWT発行元と、OBOで取得したいJWTは別の発行元、しかも異なるIdPサービスもしくはソフトウェアという条件がある場合、Azure API Management (APIM) で、OIDC/OAuth 2.0のOBO (On-Behalf-Of flow) を実現することはできるか?できるとしたらどのような方法が考えられるか?

Microsoft Entraなど、1個のIdPだけで済むならともかく、SaaSや3rd party APIを使う場合には起こりえるので、調査しておくに超したことはなさそう。ただし、前回のエントリに記載した通り、APIMではOBO Flowをネイティブにはサポートしておらず、ポリシーを組み合わせて実現する必要があるので、注意が必要。今回の調査対象のIdPは以下とする。それぞれを組み合わせると12通り。

  • Microsoft Entra
  • Okta
  • KeyCloak
  • Auth0

Executive Summary

TargetのIdPによるToken Exchangeのサポート状況次第。

APIM を middle tier に置いただけで Entra / Okta / Auth0 / Keycloak 間の any-to-any な Cross-IdP OBO を 1 つの標準パターンとして実現できない。 APIM は JWT 検証、外部 token endpoint 呼び出し、管理済み OAuth 接続からの token 取得はできるが、汎用 Security Token Service (STS) として foreign token を別 IdP の token に変換する専用機能は持ち合わせていない。

したがって成否は APIM ではなく target IdP 側の受け口 で決まる。4 製品の中では、Auth0 target が最も柔軟で、Custom Token Exchange により external token を Action で検証して Auth0 token を返すことが可能。一方、Entra target は documented OBO が Entra 自身の access token を前提にしており、generic cross-IdP target としては使えない。

Okta target は same-tenant の custom authorization server world に閉じており、Keycloak target は supported な RFC 8693 経路が same-realm に限られるため、heterogeneous な流入を受けるには preview / draft / trust 設定に依存する。本当に any-to-any の Cross-IdP OBO を求めるなら、APIM の後段に custom broker / 独自 STS を置く設計が必要。

Architecture/System Overview

そもそもAPIMはSTS (Security Token Service) ではない

RFC 8693 は、resource server 自身が client の役割を担い、受け取った access token を downstream service 向けの別 token に交換する シナリオを明示している。つまり、「client -> middle tier -> downstream」という OBO / token exchange の考え方自体は標準の守備範囲。

一方で APIM の公式ドキュメントが提供している building blocks は、次の 4つ。

  • 入口トークンの検証(validate-jwt
  • 外部IdPへのHTTP呼び出し(send-request
  • credential manager + get-authorization-context による管理済み OAuth 2.0 connection の token 取得
  • Managed Identity による Microsoft Entra トークン取得

APIM の役割は「入口 token を検証し、必要なら target 側の token endpoint へ代理呼び出しし、返ってきた token を downstream に載せる broker / orchestrator」であり、APIM 自身が foreign token を generic に解釈して別 IdP token を mint する documented STS として振る舞うわけではない。

APIMは、OIDC準拠のあらゆるIdPからのJWTを検証し、任意のトークンエンドポイントに対してHTTP POSTリクエストを発行できるため、実現可否は「APIM にできるか」ではなく、「OBO / token exchange の相手IdPが、その入口JWTを subject_token / assertion として受け付けるか」で決まる。

APIM の validate-jwt と send-request

validate-jwt は header / query / expression から JWT を取り出して検証し、openid-configaudiencesissuersrequired-claims を使って issuer / audience / claims を制限できる。 send-request は任意 URL に対して独立した HTTP request を送り、response を context variable に保持できる。したがって、APIM から target IdP の /token endpoint を POST する という broker パターンはそのまま構成可能。

APIMの機能できることできないこと
validate-jwt入口 token の issuer / audience / claims / signing key を検証できる。OIDC discovery endpoint (openid-config) も、個別 signing key も使える。token を別 issuer 向けに交換しない。
send-request任意の token endpoint / introspection endpoint / 外部 broker に POST し、返った JSON を policy で扱える。target IdP 側の grant を増やさない。
get-authorization-context事前構成した OAuth 2.0 connection から access token を取得・保持・refresh し、backend header に載せられる。任意 issuer の incoming JWT を generic に target IdP token へ変換しない。
credential managerOAuth 2.0 backend 接続の consent / token cache / refresh をコードなしで管理できる。generic cross-IdP per-request token exchange engine ではない。

APIMのCredential Managerが一般的な cross-IdP per-request token exchange の代替ではない理由

APIM Credential Manager は、接続済み OAuth 2.0 backend / SaaS API への delegated access を簡素化する機能であり、一般的な cross-IdP per-request token exchange の代替ではない。とくに get-authorization-context の identity-type=jwt は Microsoft Entra JWT を前提としている(期待される audience は https://azure-api.net/authorization-manager)ため、本件のようなEntra以外のIdPをentry IdPとした場合のtokenの汎用処理には向かない。

  • unattended scenario
    • APIM インスタンスの managed identity を使った事前構成済み接続で、各ユーザー入力に依存しない
  • attended / user-delegated scenario
    • Microsoft Entra user or group identity を前提
    • authorization code grant を使う接続に限定

つまり Credential Manager は 「APIM が管理する OAuth 2.0 connection に対する token acquisition / caching / refresh」 には強い一方、「Okta の incoming JWT を持ってきて Entra / Okta / Keycloak / Auth0 向け token にその場で交換する generic bridge」 ではない。想定しているユースケースからもわかる通り、SaaS バックエンド接続や Logic Apps 的な統合を含む、バックエンド OAuth 2.0 API のトークン ライフサイクル管理を簡素化することが目的。もしCredential ManagerをCross-IdP 委任に使うと、(unattended では)呼び出し元のアイデンティティが失われるか、(attended では)呼び出し元が最初から Microsoft Entra の ID を持っている必要がある。

理由詳細
静的かつ事前構成された接続であり、リクエストごとの交換ではないCredential Manager は、あらかじめ確立された接続に基づいて動作する。
attended(ユーザー委任) シナリオでは、
❶構成済みユーザーは一度だけサインインして同意すればよく、その後 API Management インスタンスが接続を作成し管理する
❷実行時には、get-authorization-context ポリシーが保存済みトークンを取得し、期限切れであれば更新する
これは、各受信リクエストの JWT を都度ターゲット IdP 用の新しいトークンに交換する OBO とは根本的に異なる。Credential Manager は、そのようなリクエスト単位の交換を行わない。
Unattended モードでは、リクエストごとのユーザー コンテキストがないunattended シナリオでは、API 呼び出しはユーザー コンテキストに関連しない事前構成済み接続を利用するため、すべてのユーザーに同じデータが返される。
つまり、1 つの保存済みトークンがすべての呼び出し元に使い回されてしまう。OBO は定義上、呼び出し元ユーザーのアイデンティティ をダウンストリーム トークンに伝播させる必要があるが、このモードではそれが実現できない。
Attended モードは Microsoft Entra の ID に限定されるAttended シナリオでは、Microsoft Entra ユーザーまたはグループ ID を代理して接続へのアクセスを有効化するのであって、Okta、Auth0、Keycloak で認証されたユーザーは、この委任メカニズムに直接参加できない。そのため、「入口トークンが非 Entra IdP から来る」という本件ユースケースの前提条件を満たせない。

ターゲットとしてのIdP ごとのトークン交換機能について

a) Microsoft Entra

Microsoft Entra の documented OBO は RFC 8693 の token-exchange grant ではなく、

  • grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
  • requested_token_use=on_behalf_of

を使うproprietary な middle-tier delegation。しかもassertionは、その OBO request を行う middle-tier app 自身を aud に持つ Entra access tokenでなければならない、という制約もある。さらに OBO は user principal 向けで、app-only token には使えない。

したがって、source が Okta / Auth0 / Keycloak などの foreign JWT である時点で、APIM がその token をそのまま Entra OBO に渡す documented path はない。 Entra docs には OBO で SAML assertion を返す nonstandard extension もあるが、これは Entra 独自 OBO の派生であって、heterogeneous な RFC 8693 target になったわけではない。

つまりforeign IdP token を APIM がそのまま Entra OBO に渡す generic cross-issuer path はない。APIM で成立するのは、入口 token も Entra 発行で、APIM / 中間 API 宛ての場合のみ。

Entra には OBO で SAML assertion を返す nonstandard extension もあるが、これは Entra 独自 OBO の派生であって RFC 8693 Token Exchange 対応ではない。

b) Okta

Okta は公式に OAuth 2.0 On-Behalf-Of Token Exchange を案内しており、API microservice が resource server と client の両方を兼ねる前提で、RFC 8693 ベースの token exchange を説明している。ただし documented topology は single custom authorization server または same Okta tenant 内の custom authorization server 同士 + Trusted servers に限られる。さらに service app initiated flow では offline_access と OpenID Connect scopes を利用できない。

ここで重要なのは、Okta docs が「foreign issuer token を generic に Okta token endpoint で受ける」構成を案内していない点。案内されているのは、あくまで Okta tenant 内の authorization server world で subject token を引き継ぐ構成のみである。

c) Auth0

Auth0 は Custom Token Exchange (CTE) を /oauth/token で提供しており、docs 上も RFC 8693 ベースの exchange と位置づけている。CTE request は Custom Token Exchange Profile に対応づけられ、Profile は 1 つの subject_token_type と 1 つの Action を 1 対 1 で関連づける。Action 側で subject_token を decode / validate / authorize し、最終的に user を set すると、Auth0 が access token / ID token / refresh token を返す。

しかも Auth0のドキュメントには、external authentication provider の ID token を subject token にする 例と、別 audience 向け Auth0 token を得る 例が明示されていて、4製品の中で、target 側が external token を programmable に受けることを最も明確にドキュメント化している。

一方で、これは plug-and-play ではない点に注意が必要。その他、以下のような制約がある。

  • CTE を使う application
    • first-party かつ OIDC-conformant である必要がある
  • CTE
    • Early Access
    • actor_token / actor claim による delegation は未対応
    • target API では Allow Skipping User Consent が必要
    • custom profile の subject_token_type は unique URI であり、urn:ietf などの予約 namespace は使えない

つまり source token を受けるロジックと trust 判断は Action 実装が負う。

Auth0 は Custom Token Exchange (以下、CTE)を /oauth/token で提供している(Enterprise および B2B Pro 向け Early Access)。この機能は「RFC 8693 に従い、アプリケーションが既存トークンを Auth0 トークンへ交換できるようにする」もので、想定ユースケースとしては、「別 audience 向け Auth0 トークンの取得」「外部 identity provider との統合」「Auth0 への移行」が挙げられている。

Auth0 では Custom Token Exchange Profile が subject_token_type と Action を結びつけ、Action が subject_token の decode / validate / authorize と user mapping を実行したうえで Auth0 access token / ID token / refresh token を発行する。

Auth0 の特徴は、foreign token を受けられる以下の3つのcanonical use caseをdocumented pathとして提示している。

  1. legacy refresh token からの migration
  2. external authentication provider の ID token を subject token に使う path
  3. Auth0 token を別 audience に交換する path

ただし CTE には以下の制約・制限がある。

  • Early Access
  • MFA API メソッド(challengeWith() / EnrollWith())は未対応
  • import mode ON の Custom DB Connections は setUserByConnection() では未対応
  • actor_tokenactor claim などの委任情報は未対応
  • サードパーティおよび non-OIDC conformant クライアントは未対応
  • 非対話型フローでは同意を取得できないため、ターゲット API で Allow Skipping User Consent の有効化が必要

d) Keycloak

Keycloak の RFC 8693 対応は partial support。公式の対応表でも、RFC 8693 は「Token exchange V2 only supports the internal to internal use-case」とされており、standard token exchange は same realm の internal-to-internal に限られる。公式 token exchange guide も、V2 が fully supported path、V1 が preview / deprecated path であり、V2 は use-case (1) だけ、すなわち internal token to internal token に限定されると明記している。

Keycloak はトークン交換を 2 系統で実装している。

  1. Legacy Token Exchange V1 (preview / deprecated)
    • 外部 IdP の既存 token をそのまま Keycloak token に交換する経路
    • external token to Keycloak token を含む 4 つの use case を preview として扱う
  2. Standard Token Exchange V2(RFC 8693 の部分対応、正式サポート)と JWT Authorization Grant(RFC 7521 / 7523, preview)を組み合わせる
    • 別の OIDC server との trust relationship を前提に JWT assertion を grant として送り、cross-domain / cross-realm access token を取得する

その他、Standard Token Exchange V2(RFC 8693 の部分対応、正式サポート)と JWT Authorization Grant(RFC 7521 / 7523, preview)の組み合わせも可能だが、このしくみ自体はまだドラフト仕様であり、議論中。

JWT Authorization Grant and Identity Chaining in Keycloak 26.5 – Keycloak
https://www.keycloak.org/2026/01/jwt-authorization-grant
OAuth Identity and Authorization Chaining Across Domains
https://github.com/keycloak/keycloak/blob/main/docs/guides/securing-apps/oauth-identity-authorization-chaining-across-domains.adoc
OAuth Identity and Authorization Chaining Across Domains
https://www.ietf.org/archive/id/draft-ietf-oauth-identity-chaining-05.html

したがって Keycloakをtargetにする場合、supported な RFC 8693 targetというより、standard V2 だけでは足りず、legacy preview か JWT authorization grant trust を選ぶ必要がある点には注意が必要。

Cross-IdP OBO 実現可否マトリクス

ターゲットが Microsoft Entra または Okta の組み合わせはすべて不可能。ターゲットが Auth0 または Keycloak の組み合わせは、条件付きで可能。

Target IdPToken exchangeのサポートforeign issuer token を heterogeneous に受けられるかAPIM を置いた実務 verdict
Microsoft Entra IDProprietary OBO (jwt-bearer + requested_token_use=on_behalf_of)NoNo
OktaRFC 8693 token exchangesame-tenant custom AS world のみNo for heterogeneous cross-IdP
Auth0RFC 8693-based Custom Token Exchange + ActionYes, programmable (Action / Profile 前提)Yes, conditional
KeycloakStandard token exchange V2 / Legacy V1 / JWT Authorization GrantPartial / path-specificDepends

この表をそのまま pairwise に読み替えると、次の理解で十分。

  • Entra / Okta / Auth0 / Keycloak -> EntraNo
  • Entra / Okta / Auth0 / Keycloak -> OktaNo(ただし source も Okta same-tenant world なら Yes)
  • Entra / Okta / Auth0 / Keycloak -> Auth0Yes, conditional(Action / Profile 実装前提)
  • Entra / Okta / Auth0 / Keycloak -> KeycloakDepends

APIM を middle tier に置いた実装パターン

1) target IdP の native / programmable exchange を APIM が broker する

これはよくあるやつ。APIM は source IdP の JWT を validate-jwt で検証し、send-request で target IdP の /token endpoint に必要パラメータを投げ、返ってきた token を backend 呼び出しに載せる。このパターンの成立性は target ごとに異なる。

  • Entraassertion が Entra access token である必要があるため heterogeneous では不可
  • Okta: same-tenant 限定
  • Auth0: Action で external token を受けられるため条件付きで可
  • Keycloak: path-specific
<inbound>
  <validate-jwt header-name="Authorization" require-scheme="Bearer">
    <openid-config url="https://source-idp.example/.well-known/openid-configuration" />
    <audiences>
      <audience>api://middle-tier-apim</audience>
    </audiences>
  </validate-jwt>

  <set-variable name="entryToken"
                value="@(context.Request.Headers.GetValueOrDefault(&quot;Authorization&quot;, &quot;&quot;).Split(' ').Last())" />

  <send-request mode="new" response-variable-name="targetToken" timeout="20" ignore-error="false">
    <set-url>https://target-idp.example/oauth/token</set-url>
    <set-method>POST</set-method>
    <set-header name="Content-Type" exists-action="override">
      <value>application/x-www-form-urlencoded</value>
    </set-header>
    <set-body>@($"grant_type=...&subject_token={Uri.EscapeDataString((string)context.Variables["entryToken"])}&...")</set-body>
  </send-request>

  <set-header name="Authorization" exists-action="override">
    <value>@("Bearer " + (string)((IResponse)context.Variables["targetToken"]).Body.As<JObject>()["access_token"])</value>
  </set-header>
</inbound>

2) Credential Manager を使う

これは backend access を managed にするうえでは有効だが、Cross-IdP OBO の主回答にはならない。 credential manager は connection に対する token の consent / acquisition / refresh / cache を簡略化する仕組みであり、get-authorization-context はその connection の AccessToken を取り出すものだからである。

しかも attended / user-delegated scenario は Microsoft Entra user or group identity を前提にしており、runtime で identity-type="jwt" を使う場合の identity も Microsoft Entra JWT 。それゆえ、source が Entra / Okta / Auth0 / Keycloak の任意 JWT で、その都度別 IdP token に交換したい という問いに対する generic answer にはなり得ない。

c) custom broker / 独自 STS を APIM の後ろに置く

もし要件が「4 製品間で any-to-any の Cross-IdP OBO をできるだけ統一的に扱いたい」であれば、最終的には custom broker を置くしかない。APIM は validate-jwt で入口 token を検証し、send-request で Azure Functions / Container Apps / 独自 STS に渡す。その broker 側で source token validation、user mapping、target IdP ごとの token acquisition を実装する必要がある。

これは技術的には成立するが、native target-side OBO ではなく、組織内で trust と responsibility を持つ独自 broker に過ぎない。したがって APIM の OBO 機能というより、APIM を前段に置いた identity translation service の設計をすることになる。

実務上の推奨

要件が「4 製品間をまたいで user context を保った downstream token を得たい」だけなら、まず target issuer を固定して考えることを推奨する。 target issuer が決まると、可否はかなり明確になる。

Target検討・判断備考
EntraCross-IdP OBO ではなく federation / app architecture の見直しを検討すべきforeign JWT を Entra OBO に接続する documented path はない
OktaOkta tenant 内の custom authorization server 構成に寄せられるか検討すべき寄せられない場合はAPIMだけではどうしようもない
Auth04 製品の中では最も現実的。
Auth0 CTE を hub にし、Auth0 Action に trust translation を寄せる。
APIM は validation + routing + token endpoint 呼び出しを担当
Keycloaksupported path を優先するなら same-realm V2
heterogeneous に寄せるなら JWT Authorization Grant / chaining
draft を許容できるかを先に決めるべき

まとめ

  • APIM を middle tier に置いた状態で、Entra・Okta・Auth0・Keycloak 間の Cross-IdP OBO を universal に構成することはできない。
  • APIM は STS ではなく、validate-jwt と send-request、および managed OAuth connection を扱う credential manager を持つ broker / orchestrator なので、実現可否は target IdP が source token を subject_token / assertion として受け付けるかで決まる。

実務整理は次のとおり。

  • Entra target: 不可。Entra OBO は Entra access token を前提にした proprietary flow。
  • Okta target: heterogeneous では不可。Okta の token exchange は same-tenant custom authorization server world に閉じている。
  • Auth0 target: 条件付きで可。Custom Token Exchange により external token を Action で検証し、Auth0 token を発行できる。
  • Keycloak target: 条件付き。supported な V2 は same-realm only で、heterogeneous に寄せるには legacy V1 または JWT Authorization Grant / chaining が必要。

APIM で本当に構成できるのは「target IdP ごとの native / programmable exchange を broker する」方式であり、4 製品を any-to-any でつなぐなら、APIM の後ろに custom broker / 独自 STS を置く設計が必要。

Resources

RFC 8693: OAuth 2.0 Token Exchange
https://www.rfc-editor.org/rfc/rfc8693
Azure API Management policy reference – validate-jwt | Microsoft Learn
https://learn.microsoft.com/en-us/azure/api-management/validate-jwt-policy
Azure API Management policy reference – send-request | Microsoft Learn
https://learn.microsoft.com/en-us/azure/api-management/send-request-policy
Azure API Management policy reference – get-authorization-context | Microsoft Learn
https://learn.microsoft.com/en-us/azure/api-management/get-authorization-context-policy
About Credential Manager in Azure API Management | Microsoft Learn
https://learn.microsoft.com/en-us/azure/api-management/credentials-overview
Manage Connections for End Users – Azure API Management | Microsoft Learn
https://learn.microsoft.com/en-us/azure/api-management/credentials-how-to-user-delegated
Microsoft identity platform and OAuth2.0 On-Behalf-Of flow – Microsoft identity platform | Microsoft Learn
https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow
Set up OAuth 2.0 On-Behalf-Of Token Exchange | Okta Developer
https://developer.okta.com/docs/guides/set-up-token-exchange/-/main/
Custom Token Exchange – Auth0 Docs
https://auth0.com/docs/authenticate/custom-token-exchange
Configure Custom Token Exchange – Auth0 Docs
https://auth0.com/docs/authenticate/custom-token-exchange/configure-custom-token-exchange
Example Use Cases – Auth0 Docs
https://auth0.com/docs/authenticate/custom-token-exchange/cte-example-use-cases
Specifications implemented – Keycloak
https://www.keycloak.org/securing-apps/specifications
Configuring and using token exchange – Keycloak
https://www.keycloak.org/securing-apps/token-exchange
JWT Authorization Grant – Keycloak
https://www.keycloak.org/securing-apps/jwt-authorization-grant
OAuth Identity and Authorization Chaining Across Domains at main · keycloak/keycloak
https://github.com/keycloak/keycloak/blob/main/docs/guides/securing-apps/oauth-identity-authorization-chaining-across-domains.adoc
JWT Authorization Grant and Identity Chaining in Keycloak 26.5 – Keycloak
https://www.keycloak.org/2026/01/jwt-authorization-grant
OAuth Identity and Authorization Chaining Across Domains
https://www.ietf.org/archive/id/draft-ietf-oauth-identity-chaining-05.html

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください