生成 AI エージェントをマルチテナントプロダクションアーキテクチャにデプロイする際、特定の ID 問題に直面します。エージェントがユーザーに代わってダウンストリーム API を呼び出すとき、その呼び出しとともに誰の ID が伝わるのか?エージェントのサービス ID として呼び出しを実行すると、すべてのダウンストリームシステムがエージェントを無条件に信頼する必要があるため、監査証跡が崩れます。ユーザーのトークンを変更せずに転送すると、すべてのダウンストリームツールが混乱したデプティ(confused deputy)になります。1 つのエージェントが多くのテナントに対応し、ツール呼び出し時にユーザーが存在しない場合、どちらの選択肢もスケーリングしません。
OAuth 2.0 トークン交換仕様(RFC 8693)はこの問題に対処し、Amazon Bedrock AgentCore Identity はそれをネイティブに資格情報プロバイダー付与タイプとしてサポートします。このブログ投稿は実装ガイドであり、Okta に対する完全なマルチテナント OBO 設定を説明し、各ホップでの JSON Web トークン(JWT)クレーム変換を示し、オーディエンスバインディングがテナント全体で拡張可能な多層防御をどのように生み出すかを実証します。
参照実装 TravelBot は、2 つのサンプルテナント(Acme と Globex)にサービスを提供するマルチテナント予約アシスタントです。この参照実装は、公開後に aws-samples/sample-obo-flow-poc リポジトリで利用可能になります。
OBO パターンは、エージェントが複数のダウンストリームサービスまたはテナントに対応し、インバウンドトークンのオーディエンスが単一のダウンストリーム API のオーディエンスと異なる場合に不可欠です。インバウンドオーディエンスがすでにダウンストリームサービスと一致するシングルテナントエージェントの場合、直接トークン転送で十分な場合があります。この投稿の残りの部分はマルチテナントケースに焦点を当てています。
AgentCore Identity でのオン・ビハーフ・オブ・トークン交換の導入
Amazon Bedrock AgentCore Identity は、OAuth 2.0 トークン交換(RFC 8693)をネイティブな資格情報プロバイダー付与タイプとしてサポートします。この機能により、AgentCore Gateway は、ダウンストリームツールを呼び出す前に、インバウンドユーザートークンを新しいオーディエンスバウンドトークンに透過的に交換でき、エージェント自身が交換を実装する必要はありません。
この機能により、次の利点が得られます:
- テナント境界を越えた ID 伝播:オーディエンスがテナントごとに変わっても、元の呼び出し元の ID は sub クレームを通じてエンドツーエンドで保持されます。
- 暗号的最小権限:各ダウンストリーム呼び出しは、aud クレームを介して 1 つのダウンストリームサービスにバインドされたトークンを運びます。あるテナントに発行されたトークンは別のテナントでは使用できません。
- エージェント側の交換ロジック不要:エージェントコードは単一のインバウンドトークンを取得し、ツールを呼び出します。AgentCore がすべての交換を実行します。
- 標準ベース:実装は RFC 8693 トークン交換付与に基づいています。同じ付与タイプと互換性のあるリクエスト形状をサポートする認可サーバーは、資格情報プロバイダーとして機能できます。
AI エージェントにおける混乱したデプティ問題
多くのテナントからのユーザーに対して「私の予約を表示」などのリクエストを処理するエージェントを考えます。3 つの実装選択肢があり、そのうち 1 つだけが正しいです。
- サービスアカウントのなりすまし:エージェントは自身として認証し、リクエストヘッダーまたはパスパラメーターでユーザー ID を主張します。すべてのダウンストリーム API はエージェントを無条件に信頼する必要があります。侵害されたエージェントは、任意のテナントに対して任意のユーザーとして動作できます。これは教科書的な混乱したデプティです。
- 直接ユーザートークン転送:エージェントはインバウンドユーザートークンを再利用してダウンストリーム API を呼び出します。これは、インバウンドトークンのオーディエンスがすでにダウンストリーム API と一致する場合にのみ機能します。マルチテナントシステムではめったに当てはまらず、エージェントがツールゲートウェイを前面に出す場合には決して当てはまりません。
- オン・ビハーフ・オブ・トークン交換:エージェントの認可ブローカーは、インバウンドサブジェクトトークンを、sub が元の呼び出し元、aud がダウンストリームサービス、署名がダウンストリーム API が信頼する認可サーバーからのものである新しいトークンに交換します。結果として、トークンは単一のダウンストリーム呼び出しに暗号的にスコープされ、単一のユーザーに代わって動作します。
OBO は、ユーザーの ID をエンドツーエンドで保持し、オーディエンス境界で最小権限を強制し、ダウンストリーム API がエージェントを信頼せずに独立して検証できるトークンを生成する唯一の選択肢です。RFC 8693 の実装には、エージェントランタイム、認可サーバー、ダウンストリーム API 間の調整が必要です。Amazon Bedrock AgentCore Gateway と AgentCore Identity はその調整の負担を取り除きます。Gateway はツール呼び出しをインターセプトし、ターゲットテナントを特定し、ダウンストリーム呼び出しが発行される前に Identity にテナントの認可サーバーとの交換を実行するよう指示します。
なりすましとオン・ビハーフ・オブの比較
OBO 交換では、インバウンドトークンの sub クレームは保持され、aud クレームはダウンストリームサービスに書き換えられます。交換のアクターは別のクレーム(RFC 8693 の act、Okta の cid)に記録されるため、ダウンストリーム API は単一のトークンから 2 つの質問に答えることができます:誰に代わって行動しているか?(sub クレーム)および誰が行動を実行しているか?(アクタークレーム)。認可の決定は sub に依存します。監査ログとレート制限の決定はアクターに依存します。
ソリューション概要
この投稿では、次の識別子が TravelBot 参照の特定の Okta リソースを参照します:
- TravelBot Provider:エージェントにインバウンド JWT を発行する Okta 認可サーバー。
- ACME Travel API:Acme テナントの OBO トークンを生成する Okta 認可サーバー。
- Globex Travel API:Globex テナントの OBO トークンを生成する Okta 認可サーバー。
- TravelBot Agent Client:エージェントのマシンツーマシンパスのための Okta API サービスアプリ。
- TravelBot User Client:3 レッグユーザーログインのための Okta OpenID Connect(OIDC)アプリ。
- AgentCore Delegate:AgentCore Identity がトークン交換を実行するために使用する Okta アプリの資格情報。
TravelBot 参照実装は、交換の両側で Okta を使用します。1 つの Okta 認可サーバー(TravelBot Provider)がエージェントを認証します。さらに 2 つ(ACME Travel API と Globex Travel API)が各テナントの OBO トークンを生成します。3 つすべてが Okta カスタム認可サーバーです。Okta の組み込み Org 認可サーバーはカスタムオーディエンスやスコープをサポートしておらず、このパターンには使用できません。
同じ役割は、同等の機能を持つ他の ID プロバイダー(IdP)によって果たされます。RFC 8693 は認可サーバーレイヤーで実装されているため、トークン交換付与をサポートする他の認可サーバーでも同じアーキテクチャが機能するはずです。AgentCore Identity が送信する正確なリクエスト形状(subject_token_type、audience、actor-token の存在、クライアント認証方法)は、customParameters マップを介して資格情報プロバイダーごとに設定されるため、異なる IdP に適応することはコード変更ではなく構成変更です。
アーキテクチャコンポーネント
アーキテクチャには 6 つのコンポーネントがあります:
- プロバイダー認可サーバー:エージェントが Gateway に提示するインバウンド JWT を発行します。TravelBot では、これは TravelBot Provider という Okta 認可サーバーです。
- AgentCore Gateway:インバウンド JWT をプロバイダーの JSON Web Key Set(JWKS)に対して検証し、ツール呼び出しを正しいテナントターゲットにルーティングし、OBO 交換を調整します。
- AgentCore Identity:デリゲートクライアント資格情報を保持し、テナント認可サーバーに対して RFC 8693 トークン交換を実行します。
- テナントごとの認可サーバー:各テナントのオーディエンスにスコープされた OBO トークンを生成します。TravelBot では、ACME Travel API と Globex Travel API は、それぞれ独自のオーディエンスとアクセスポリシーを持つ 2 つの独立した Okta 認可サーバーです。
- テナントごとの API サーフェス:テナントごとに 1 つの JWT 認可者を持つ Amazon API Gateway HTTP API。各認可者は発行者、オーディエンス、および必要なスコープを検証し、テナント間トークン再利用に対する最後の防御線を提供します。
- テナントビジネスロジック:検証された OBO トークンを受け取り、クレームを読み取ってテナント固有の決定を行う AWS Lambda 関数。per-user authorized_scopes クレームから書き込み権限を強制し、予約を sub クレームでパーティション化された Amazon DynamoDB に保存するため、ユーザーは自分のレコードのみを読み取ることができます。
リクエストフロー
ワークフローは次の手順で構成されます:
- ユーザーは 3 レッグ authorization_code ログインを通じてプロバイダー認可サーバーで認証し、エージェントは Gateway にオーディエンスバウンドされたインバウンド JWT を受け取ります。
- エージェントはモデルコンテキストプロトコル(MCP)を介してツールを呼び出し、インバウンド JWT をベアラートークンとして提示します。
- Gateway はプロバイダーの JWKS を取得し、JWT 署名を検証し、オーディエンスが travelbot-provider と一致することを確認します。
- Gateway は Acme ターゲットに関連付けられた資格情報プロバイダーを選択し、Identity に OBO トークンを要求します。
- Identity は Acme の認可サーバーに RFC 8693 トークン交換リクエストを送信します。サブジェクトトークンはインバウンド JWT で、要求されたオーディエンスは https://api.acme-travel.example です。
- Okta は Acme の認可サーバーがプロバイダーを信頼していることを確認し(信頼されたサーバー関係)、次に要求されたオーディエンス(Acme Travel API)に対して新しい JWT を生成します。この JWT では sub が元のユーザーに設定され、actor が AgentCore Delegate クライアント ID に設定されます。
- Gateway は新しいトークンを Acme 予約 API への呼び出しに注入します。
- ダウンストリーム API は OBO トークンのオーディエンスと署名を検証し、sub クレームに基づいて認可を実行します。
この構成により、各テナントの認可サーバーが独立してトークンを発行し、トークンが意図された API にのみ使用できるため、多層防御が提供されます。テナント間トークン再利用の試みは、オーディエンスまたは署名の検証に失敗することで拒否されます。