使用 Amazon Bedrock AgentCore Gateway 为多租户代理实现代理令牌交换
本文详细介绍了如何利用 Amazon Bedrock AgentCore Gateway 和 Identity 实现 OAuth 2.0 令牌交换(RFC 8693),以解决多租户代理中下游 API 调用时的身份传播问题和 confused deputy 问题。通过参考实现 TravelBot,展示了在 Okta 环境下的完整设置、JWT 声明转换以及如何通过受众绑定实现深度防御。
在将生成式 AI 代理部署到多租户生产架构中时,您会面临一个特定的身份问题:当代理代表用户调用下游 API 时,谁的身份随调用传递?以代理的服务身份运行调用会破坏审计追踪,因为每个下游系统都必须无条件信任该代理。直接转发用户的令牌而不做更改,则会使得每个下游工具成为混乱的代理(confused deputy)。当单个代理面对多个租户且用户在工具调用时并不在场时,这两种选择都无法扩展。
OAuth 2.0 令牌交换规范(RFC 8693)正是为了解决这个问题,Amazon Bedrock AgentCore Identity 原生支持将其作为凭证提供者授权类型。这篇博文是实施指南,它完整地演示了针对 Okta 的多租户 OBO 设置,展示了每个跳转点的 JSON Web 令牌(JWT)声明转换,并说明了受众绑定如何产生可跨租户扩展的深度防御。
参考实现 TravelBot 是一个多租户预订助手,服务于两个示例租户(Acme 和 Globex)。该参考实现将在发布后于 aws-samples/sample-obo-flow-poc 仓库中提供。
OBO 模式在代理面对多个下游服务或租户且入站令牌的受众与任何单个下游 API 的受众不同时至关重要。对于单租户代理,如果入站受众已经匹配下游服务,则直接转发令牌可能就足够了。本文其余部分聚焦于多租户场景。
AgentCore Identity 中的代理令牌交换介绍
Amazon Bedrock AgentCore Identity 支持 OAuth 2.0 令牌交换(RFC 8693)作为原生凭证提供者授权类型。凭借此能力,AgentCore Gateway 可以在调用下游工具之前透明地将入站用户令牌交换为新的受众绑定令牌,而无需代理本身实施交换。
此能力提供以下好处:
- 身份跨租户边界传播:即使受众随租户变化,原始调用者的身份也能通过 sub 声明端到端保留。
- 密码学最小权限:每个下游调用携带一个通过 aud 声明绑定到单个下游服务的令牌。为一个租户颁发的令牌不能用于另一个租户。
- 无代理端交换逻辑:代理代码获取单个入站令牌并调用工具,AgentCore 执行每次交换。
- 基于标准:实施基于 RFC 8693 令牌交换授权。支持相同授权类型和兼容请求形状的授权服务器可以作为凭证提供者。
AI 代理中的混乱代理问题
考虑一个处理来自多个租户用户的“显示我的预订”等请求的代理。有三种实现选择,只有一种是正确的:
- 服务账户模拟:代理以自身身份认证,并在请求头或路径参数中声明用户身份。每个下游 API 都必须无条件信任代理。被攻破的代理可以针对任何租户模拟任何用户。这是典型的混乱代理。
- 直接用户令牌转发:代理重用入站用户令牌调用下游 API。这仅当入站令牌的受众已经匹配下游 API 时才有效。在多数系统中很少成立。
- 代理令牌交换:代理的授权中介将入站主题令牌交换为新令牌,其 sub 为原始调用者,aud 为下游 API,签名来自下游 API 信任的授权服务器。结果是一个加密作用域到单个下游调用、代表单个用户的令牌。
OBO 是唯一能端到端保留用户身份、在受众边界强制执行最小权限、并产生下游 API 可独立验证且无需信任代理的令牌的选择。实施 RFC 8693 需要代理运行时、授权服务器和下游 API 之间的协调。Amazon Bedrock AgentCore Gateway 和 AgentCore Identity 消除了这种协调负担。Gateway 拦截工具调用,识别目标租户,并在下游调用发出前指示 Identity 针对租户的授权服务器执行交换。
模拟与代理令牌交换对比
在 OBO 交换中,入站令牌的 sub 声明被保留,而 aud 声明被重写为下游服务。交换的执行者记录在单独声明中(RFC 8693 中的 act,Okta 中的 cid),因此下游 API 可以通过单个令牌回答两个问题:代表谁执行操作?(sub 声明)以及谁在执行操作?(actor 声明)。授权决策应基于 sub,审计日志和速率限制决策应基于 actor。
解决方案概述
本文使用以下标识符引用 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:用于三方登录的 Okta OIDC 应用。
- AgentCore Delegate:AgentCore Identity 用来执行令牌交换的 Okta 应用凭证。
TravelBot 参考实现使用 Okta 作为交换双方。一个 Okta 授权服务器(TravelBot Provider)认证代理,另外两个(ACME Travel API 和 Globex Travel API)为每个租户铸造 OBO 令牌。所有三个都是 Okta 自定义授权服务器。Okta 内置的组织授权服务器不支持自定义受众或作用域,无法服务此模式。
相同的角色可以由其他身份提供商扮演。由于 RFC 8693 是在授权服务器层实现的,支持令牌交换授权的其他授权服务器可以应用相同架构。AgentCore Identity 发送的请求形状(subject_token_type, audience, actor-token 存在性以及客户端认证方法)通过 customParameters 映射按凭证提供者设置,因此适应不同 IdP 是配置更改而非代码更改。
架构组件
架构包含六个组件:
- 提供者授权服务器:发出入站 JWT,在 TravelBot 中是 TravelBot Provider。
- AgentCore Gateway:验证入站 JWT,将工具调用路由到正确租户目标,并编排 OBO 交换。
- AgentCore Identity:持有委托客户端凭证并针对租户授权服务器执行 RFC 8693 令牌交换。
- 每个租户的授权服务器:为每个租户的受众铸造 OBO 令牌。在 TravelBot 中,ACME Travel API 和 Globex Travel API 是两个独立的 Okta 授权服务器,各有其受众和访问策略。
- 每个租户的 API 表面:一个 Amazon API Gateway HTTP API,每个租户有一个 JWT 授权者。每个授权者验证发行者、受众和所需作用域,提供防止跨租户令牌重用的最后防线。
- 租户业务逻辑:一个 AWS Lambda 函数,接收验证后的 OBO 令牌并读取声明以做出租户特定决策。它从 per-user authorized_scopes 声明强制执行写权限,并将预订存储在按 sub 声明分区的 Amazon DynamoDB 中,因此用户只能读取自己的记录。
请求流程
工作流包括以下步骤:
- 用户通过三方 authorization_code 登录在提供者授权服务器处认证,代理收到入站 JWT,受众绑定到 Gateway。
- 代理通过模型上下文协议(MCP)调用工具,将入站 JWT 作为 bearer 令牌呈现。
- 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,其中 sub 设置为原始用户,actor 设置为 AgentCore Delegate 客户端 ID。
- Gateway 将新令牌注入到对 Acme 预订 API 的调用中。
- 下游 API 验证 OBO 令牌的受众和签名,然后基于 sub 声明执行授权。
这种配置确保每个租户的授权服务器独立颁发令牌,且令牌只能用于其预期的 API,从而提供深度防御。任何跨租户令牌重用尝试都会因受众或签名验证失败而被拒绝。