Okta Cross App AccessとAmazon Bedrock AgentCoreで実現する、ユーザー委任型AIエージェント認可


ADVERTISEMENT

IdPの認可判断をAWS上の実行制御へつなぐ三段階トークン設計

AWSの技術記事では、ユーザーに代わってチケットを更新したり顧客情報を参照したりするAIエージェントに対し、Okta Cross App Access(XAA)とAmazon Bedrock AgentCoreを組み合わせてアクセスを統制する構成を紹介している。中心となる考え方は、誰が、どのエージェントを通じて、どのアプリケーションに対して、何を実行できるかをIdPで決定し、その判断をクラウド上の実行時に検証・執行することだ。

Oktaがガバナンスプレーンとして、エージェントの有効性、ユーザーの割り当て、対象リソースへの接続、要求スコープを評価する。一方、AgentCore Identityは実行プレーンとして、エージェントの資格情報を保持し、Oktaの委任判断をリソースが受け入れるアクセストークンへ交換する。AgentCore runtimeは受け取った委任許可を検証し、エージェント実行環境に渡す。これにより、エージェントコードにOktaのクライアントシークレットやトークンエンドポイント呼び出しを直接持ち込まずに済む。

リクエストでは、用途の異なる三つのトークンが連鎖する。

  1. IDトークン:サインインしたユーザーを表す。ユーザーはエージェントにリンクされたOktaアプリを通じて認証する。

  2. ID-JAG(Identity Assertion JWT Authorization Grant):BFFがユーザーのIDトークンをOkta組織認可サーバーでトークン交換し、エージェントとして取得する単回利用の委任許可。RFC 8693のトークン交換を使い、有効期限は5分。

  3. アクセストークン:AgentCore IdentityがID-JAGをリソース用のOktaカスタム認可サーバーでJWT Bearer Grant(RFC 7523)により交換し、MCPサーバーが受け入れるトークン。有効期限は5分で、ユーザーとエージェント双方の識別情報に加え、ユーザーのグループ情報を持つ。

これとは別に、AgentCore runtimeがワークロードIDと受信トークンのissuer・subjectを結び付けるAWS内部のワークロードアクセストークンを発行する。このトークンはAWS内部で使われ、Oktaやリソースには見えないため、外部リソースが受け取る三つのトークンには含まれない。

この分離には意味がある。ID-JAGはOktaの判断を示す一時的な委任許可であり、アクセストークンは実際のリソース呼び出しに使う認可情報である。さらに、AWS内部のワークロードトークンが実行環境とユーザーを結び付ける。つまり、ユーザー認証、エージェントの委任、AWS上の実行主体、リソース認可を単一の長寿命資格情報に押し込めず、それぞれ異なる検証境界に置いている。

実装上の境界条件と、検証で明らかになった失敗パターン

Okta側では、対象リソース用のカスタム認可サーバーにapi://task-managerのaudienceとxaa:read、xaa:writeのスコープを設定する。アクセストークンには、xaa-で始まるグループを対象とするgroups claimを追加する。AIエージェントをOktaに登録し、リンクされたOpenID Connectアプリを作成したうえで、エージェントと認可サーバーを必要なスコープに限定して接続する。さらに、JWT Bearer Grantを許可するアクセスポリシーと、5分のアクセストークン有効期間を設定する。

トークン交換では、OIDCのopenidやprofileなどを要求せず、少なくとも一つのリソース固有カスタムスコープを指定する必要がある。記事の検証では、OIDCスコープを含むID-JAG要求はinvalid_scopeで拒否された。また、2段目の交換では、要求スコープをID-JAGに含まれるスコープと完全一致させなければならない。狭いスコープへの変更も、ID-JAGにないスコープの追加も拒否される。この制約は、委任許可を後段で拡張・縮小するのではなく、最初の認可判断をそのまま維持する設計と整合する。

AgentCore IdentityのOAuth2 credential providerは、Oktaエージェントごとに作成する。Oktaは両方の交換を同じクライアントに結び付けるため、別エージェントのID-JAGを別クライアントとして交換しようとすると、client_idの不一致で拒否される。クライアントシークレットはAgentCore Identityが管理するAWS Secrets Managerのシークレットに保存でき、既存のSecrets Managerシークレットを参照する構成も可能だ。記事では、共有シークレットを避ける選択肢としてPrivate Key JWT認証にも言及している。

実行環境では、AgentCore runtimeのカスタムJWTオーソライザーを使い、Oktaのissuer、対象認可サーバーのaudience、エージェントのclient ID、許可スコープを制限する。特にallowedClientsは、ID-JAGのclient_id claimを対象エージェントに固定する検査となる。これを省くと、別エージェントのID-JAGがランタイムをHTTP 200で通過し、後段のトークン交換まで失敗が遅れる。設定した場合はランタイムがHTTP 401で拒否するため、認可境界を可能な限り入口に近づけられる。

AgentCore Python SDKでは、ランタイムが取得したワークロードアクセストークンはエントリポイントのリクエストヘッダーではなく、BedrockAgentCoreContextから取得する。記事の初期実装はヘッダーから探した結果、トークンが見つからず、21件すべてで別のワークロードIDを使う追加取得にフォールバックした。正しいコンテキストからトークンを読み、GetResourceOauth2Tokenを呼び出す構成では、AgentCore Identityがトークンをトークンボルトに保持し、同一リクエスト内の後続呼び出しで再利用できる。

リソース側の認可では、スコープとグループを別々の条件として確認する。リソース接続に設定したスコープは同じエージェントを使うユーザーに共通であり、ユーザーごとの権限差はgroups claimで表す。記事の例では、書き込みにxaa:writeとxaa-writersグループの両方を要求した。グループclaimがないユーザーを適切に拒否すること、さらにactおよびcid claimを用いてOkta XAA経由で登録済みエージェントが取得したトークンに限定することが重要だ。ランタイムのJWTオーソライザーが前段にある場合、MCPサーバーは検証済みclaimを参照できるが、その保護がないサーバーは署名、issuer、audience、有効期限を自ら検証する必要がある。

実測結果、失効の挙動、運用上の含意

検証では、ユーザーごとの権限差がツール実行に反映された。xaa-writersに属するBobはタスクを作成できた一方、読者グループのAliceは書き込みを拒否された。対象グループに属さずgroups claimもないCarolも作成を拒否され、三者ともタスク一覧の参照は許可された。Bobの後にAliceがリクエストしても、Bobの権限が引き継がれることはなかった。

誤用・不正なトークンも、検証境界ごとに異なる主体が拒否した。別エージェントのID-JAG、期限切れが近いトークン、不正形式のBearerトークンはAgentCore runtimeが拒否する。単回利用のID-JAGの再利用、スコープの不一致はOktaのカスタム認可サーバーが拒否する。接続に含まれないスコープはOktaの組織認可サーバーでinvalid_scopeとなり、通常のトークン交換で発行されたエージェント委任情報のないトークンはMCPサーバーが拒否する。アクセストークンのclaimはOktaで直接交換したものと一致し、subはユーザー、cidとactはエージェント、scpは接続スコープ、groupsはユーザーの所属グループを示した。有効期間は300秒だった。

記事のベンチマークは、テストクライアントからの5回実行の中央値である。サインイン(スクリプト化したAuthorization Code Flow)は1,205ミリ秒、IDトークンからID-JAGへの交換は216ミリ秒、GetWorkloadAccessTokenForJWTは135ミリ秒、OktaのJWT Bearer Grantを含むGetResourceOauth2Tokenは508ミリ秒だった。AgentCore Identityによる追加時間は、約30秒かかるエンドツーエンドのリクエストに対して約0.6秒と報告されている。10リクエストの検証では、同一リクエスト内の2つ目のMCPサーバー呼び出しは、単回利用ID-JAGを再交換せず、トークンボルトから中央値58ミリ秒で同じアクセストークンを返した。一方、新しいID-JAGを使う次のリクエストでは新しいアクセストークンが取得され、最新のOkta判断が反映される。

運用上、管理者の操作は「新規リクエスト」と「発行済みトークン」に異なるタイミングで効く。ユーザーのエージェント割り当て解除後、新しい要求は約1秒で拒否され、すでに発行済みのトークンは最大5分間有効となる。リソース接続の削除は新規要求を直ちにinvalid_targetで拒否するが、発行済みトークンは失効せず、有効期限まで最大5分利用可能である。したがって、管理操作の反映速度だけでなく、短命トークンの残存リスクも含めて失効設計を評価する必要がある。

なお、記事のチュートリアルはAWSのサンプルを土台にした概念的な実装パターンで、掲載コードや設定はそのままデプロイできる完全なソリューションではない。記事自身が非本番用サンプルとして位置付けており、本番導入前に組織のセキュリティ、法務、規制・コンプライアンス要件との照合を求めている。また、前提条件には米国西部(オレゴン)のAWS環境とAmazon Bedrock上のAnthropic Claudeモデルが含まれる。記事のPart 2では、同じガバナンスをAgentCore Gateway、Cedarポリシー、Amazon EKS上の別エージェント、Amazon CloudWatchの監査証跡へ展開する構成を扱うとしている。

開発者・エンジニア視点での考察

  1. 認可情報の「発行」と「執行」を別コンポーネントに割り当てる。 IdPが委任許可を発行し、ランタイムが入口でエージェントを検証し、リソースがユーザー権限を判断する構成にすると、単一の認証チェックに依存しない防御層を作れる。実装レビューでは、各境界で何を検証しているかを、issuer・audience・client ID・scope・group・actor claimの単位で明文化したい。

  2. キャッシュの範囲をリクエスト境界に合わせる。 同一リクエスト内でアクセストークンを再利用すれば、2回目の取得を中央値58ミリ秒に抑え、単回利用ID-JAGの再使用も避けられる。一方、次のリクエストでは新しいID-JAGから交換し直す設計により、権限変更の反映を保てる。トークンボルトの利用にあたっては、共有キャッシュの利便性だけでなく、キャッシュキーとユーザー・エージェント・スコープの結び付きもテスト対象にすべきだ。

  3. 否定ケースをAPI契約と回帰テストに含める。 別エージェントのID-JAG、期限切れトークン、グループclaim欠落、scope不一致、通常のサービスアプリ用トークンなどは、成功経路とは独立した必須テストケースになる。特に「グループ情報がない場合に許可しない」ことと「エージェントの識別claimがないトークンを受け付けない」ことは、権限昇格を防ぐための明示的な拒否条件としてコード化し、実際にどの層が拒否したかも観測可能にしておくべきである。

Source / 元記事

この記事について

著者
AIBloom AI編集部
初回公開
最終更新

この記事は、公開されているニュース、論文、公式発表、RSSフィードなどをもとに、AIが要約・補足調査・考察を行って作成しています。

元記事の完全な翻訳・逐語的な要約ではなく、AIによる背景説明や開発者向けの考察を含みます。

重要な技術仕様・価格・提供状況などは、必ず元記事または公式情報をご確認ください。

About AIBloom

ADVERTISEMENT