powered by TechFeed
表示モード
Deep Dive

AIエージェントが「誰の代わりに動くか」をOktaとAWS BedrockでID管理する設計 — トークン3段階チェーンで実現するユーザー委任制御

11月7日、AWSが「Govern AI agent access with Okta Cross App Access and Amazon Bedrock AgentCore – Part 1」と題した記事を公開した。記事の中心にあるのは「AIエージェントが誰の代わりに動いているか」という問題意識だ。チケット更新や顧客レコード参照を行うエージェントは、特定ユーザーの代理として動作する。しかし従来のOAuthコンセントや共有サービスアカウントでは、「どのエージェントが、どのユーザーの代わりに、どのアプリにアクセスできるか」を一元管理する手段がなかった。この記事が提示するのは、IDトークン・ID-JAG・アクセストークンの3段階トークンチェーンによってユーザー委任を厳密に制御するアーキテクチャだ。

11月7日、AWSが「Govern AI agent access with Okta Cross App Access and Amazon Bedrock AgentCore – Part 1」と題した記事を公開した。記事の中心にあるのは「AIエージェントが誰の代わりに動いているか」という問題意識だ。チケット更新や顧客レコード参照を行うエージェントは、特定ユーザーの代理として動作する。しかし従来のOAuthコンセントや共有サービスアカウントでは、「どのエージェントが、どのユーザーの代わりに、どのアプリにアクセスできるか」を一元管理する手段がなかった。この記事が提示するのは、IDトークン・ID-JAG・アクセストークンの3段階トークンチェーンによってユーザー委任を厳密に制御するアーキテクチャだ。


3本のトークンチェーン

このソリューションの核心は、1リクエストに3種類のトークンを連携させる設計にある。

  1. IDトークン:「このユーザーは誰か」をOIDCで証明
  2. ID-JAG(Identity Assertion JWT Authorization Grant):「このエージェントがこのユーザーの代理として行動することをOktaが認可した」という決定を表すトークン(RFC 8693に基づくトークン交換)。単回使用・5分有効
  3. アクセストークン:リソース(MCPサーバー)が受け付けるトークン。ユーザーとエージェント双方の名前とユーザーのグループ情報を含む

リクエストの流れは以下のとおりだ:

  1. ユーザーがWebフロントエンド(BFF)にOIDCでサインイン
  2. BFFがユーザーのIDトークンをOktaのorg認可サーバーでID-JAGに交換(エージェントとして認証)
  3. BFFがID-JAGをベアラートークンとしてAgentCore runtimeのエージェントを呼び出し
  4. runtimeがAgentCore Identityからワークロードアクセストークンを取得し、エージェントコードに渡す(このトークンはAWS内部にとどまる)
  5. エージェントがAgentCore Identityにアクセストークンを要求。AgentCore IdentityがID-JAGをリソース側のOkta認可サーバーでRFC 7523形式のJWT認可グラントとして引き換え
  6. エージェントがそのトークンでMCPサーバーを呼び出し

解決したい問題:従来アプローチの限界

アーキテクチャの背景にある問題意識を整理しておく。従来のアプローチ——アプリごとのOAuthコンセント、共有サービスアカウント——では「どのエージェントが、どのユーザーの代わりに、どのアプリケーションにアクセスできるか」を一元管理する手段がなかった。

この記事が提示するアーキテクチャは、OktaをガバナンスプレーN、Amazon Bedrock AgentCoreを実行プレーンとして分離した設計だ。

  • Okta Cross App Access(XAA):エージェントをOktaに登録し、管理者がアクセス可能なアプリを承認。ユーザーのアクセス権を剥奪してから約1秒以内に新規リクエストを拒否、発行済みトークンは5分以内に失効する
  • Amazon Bedrock AgentCore Identity:OktaのJWT認可グラントを引き受け、エージェントコードに認証情報を渡すことなく短命アクセストークンを生成。エンドツーエンド約30秒のリクエストに対し、AgentCore Identityが追加するレイテンシは約0.6秒

実装上の重要ポイント

Okta側の設定

カスタム認可サーバー(audience: api://task-manager)にxaa:read・xaa:writeスコープを作成し、アクセストークンにグループクレームを付与する。ID-JAGリクエストにopenidやprofileなどのOIDCスコープを含めるとinvalid_scopeで拒否されるため、カスタムスコープのみを指定する必要がある。

POST /oauth2/v1/token HTTP/1.1
Host: YOUR-ORG.okta.com
Authorization: Basic <the agent's client ID and secret>
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&subject_token=<the user's ID token>
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&audience=https://YOUR-ORG.okta.com/oauth2/YOUR-CUSTOM-AS-ID
&scope=xaa:read xaa:write

AgentCore Identityのクレデンシャルプロバイダー設定

control = boto3.client("bedrock-agentcore-control")
provider = control.create_oauth2_credential_provider(
    name="okta-xaa-obo",
    credentialProviderVendor="CustomOauth2",
    oauth2ProviderConfigInput={"customOauth2ProviderConfig": {
        "oauthDiscovery": {"discoveryUrl": f"{OKTA_ORG}/oauth2/{CUSTOM_AS_ID}/.well-known/openid-configuration"},
        "clientId": AGENT_CLIENT_ID,
        "clientSecret": AGENT_CLIENT_SECRET,
        "clientAuthenticationMethod": "CLIENT_SECRET_BASIC",
        "onBehalfOfTokenExchangeConfig": {"grantType": "JWT_AUTHORIZATION_GRANT"},
    }},
)

Oktaは発行したID-JAGのclient_idとクレデンシャルプロバイダーのclient_idを照合するため、Oktaエージェントごとに個別のプロバイダーが必要だ。別エージェントのID-JAGを別プロバイダーで引き換えようとすると「The 'client_id' in the JWT Bearer Grant must match the 'client_id' used to authenticate the client」で拒否される。

ワークロードアクセストークンの取得

記事中では実装上の重要な注意点として、ワークロードアクセストークンはリクエストヘッダーではなく**BedrockAgentCoreContextから取得する**必要があることが強調されている。

from bedrock_agentcore.runtime import BedrockAgentCoreContext

identity = boto3.client("bedrock-agentcore")

def okta_access_token() -> str:
    workload_token = BedrockAgentCoreContext.get_workload_access_token()
    return identity.get_resource_oauth2_token(
        workloadIdentityToken=workload_token,
        resourceCredentialProviderName="okta-xaa-obo",
        scopes=["xaa:read", "xaa:write"],
        oauth2Flow="ON_BEHALF_OF_TOKEN_EXCHANGE",
    )["accessToken"]

AgentCore Identityはアクセストークンをトークンヴォールトにキャッシュするため、同一リクエスト内の2回目の呼び出しは中央値58ミリ秒で返る。ID-JAGが単回使用であっても、2つ目のMCPサーバー向けに再度引き換える必要はない。


前提条件と所要時間

項目 使用環境
AWSアカウント(Bedrock AgentCore + Claude) US West (Oregon)
Cross App AccessおよびAPI Access Managementを有効にしたOktaオルグ Okta Integrator Free Planオルグ
Python / AWS CLI / boto3 / AgentCore CLI・SDK Python 3.11、AWS CLI v2、boto3 1.43、AgentCore CLI 0.31.1、SDK 1.24.1

ウォークスルーの所要時間は約1時間。ベースとなるサンプルコードはGitHubのAgentCoreサンプルリポジトリの08-okta-xaa-delegation-chainとして公開されている。

なお本記事はPart 1であり、Part 2ではAmazon Bedrock AgentCore GatewayとCedarポリシー、Amazon EKS上の第2エージェント、Amazon CloudWatchによる監査証跡を扱う予定だ。

詳細はGovern AI agent access with Okta Cross App Access and Amazon Bedrock AgentCore – Part 1を参照していただきたい。