据 Okta 官方博客,88% 的企业报告已确认或疑似的 AI Agent 安全事件,但只有 22% 的企业为其 Agent 绑定了身份。这个落差指向的问题是真实的:Agent 已经在代替用户更新工单、读取客户记录、调用内部 API,但几乎没有企业能回答一个基本问题——哪个 Agent 被允许代表哪个用户、访问哪个应用、何时可以被即时撤回。

AWS 机器学习博客同日发布的两篇系列文章(Part 1 与 Part 2)给出了一个完整的技术方案演示:用 Okta Cross App Access(XAA)在 IdP 层做授权决策,用 Amazon Bedrock AgentCore Identity 在 Agent 运行时兑换授权,再用 AgentCore Gateway 把散落在每个 MCP Server 里的鉴权逻辑收归单一入口。这套方案从「1 个 Agent + 2 个 MCP Server」的最小拓扑起步,逐步演进到支持多 Agent、多工具、多运行环境的企业级架构。

本文按这两篇博客的技术主线,逐层拆解这套身份治理方案的设计逻辑、实测数据与架构演进,并对比它与普通令牌交换、按用户 OAuth 同意的差别。

身份问题的起点:per-app OAuth 与服务账号在 Agent 场景下的失效

Part 1 开篇直接定义了问题:”An AI agent that updates a ticket or reads a customer record acts for a person, inside applications that person can reach. Your identity team then has to answer a question that per-app OAuth consent and shared service accounts leave open.”

这句话点出了两种传统授权模式在 Agent 场景下的具体缺陷。

per-app OAuth 授权的问题在于,它的设计假设是「用户主动授权一个应用访问自己的数据」。用户点击授权按钮,应用获得 access token,访问范围由 scope 限定。但在 Agentic AI 场景下,Agent 可能需要跨多个 SaaS 应用执行操作——更新 Jira 工单、读取 Salesforce 客户记录、修改 ServiceNow 配置。每个应用都有自己的 OAuth 授权流程,Agent 需要逐一获取授权。这产生了两个问题:第一,用户面对大量重复的授权弹窗;第二,每个应用独立管理授权,没有一个中心化的地方能回答「这个 Agent 总共能访问什么」。

共享服务账号的问题更严重。当开发者为了绕过 per-app OAuth 的复杂性,给 Agent 配置一个拥有广泛权限的服务账号时,Agent 的每一次操作都以这个服务账号的身份执行,而非以实际用户的身份执行。这意味着:审计日志里看不到「Agent 代表张三读取了客户 X 的记录」,只能看到「服务账号读取了客户 X 的记录」;权限无法按用户粒度收缩,服务账号能访问的一切,Agent 都能访问;当某个用户离职或权限变更时,服务账号的权限不会自动调整。

Okta 在其官方博客中引用了一个安全事件来说明这种风险模式。据该博客描述,一家”well-known developer platform”遭遇的安全事件中,没有漏洞、没有基础设施缺陷,仅仅是一个员工的企业账号与第三方 AI 工具之间的 OAuth 连接。当该 AI 工具被攻陷时,预先授予的信任关系成为攻击路径——直接通向公司的内部系统、API 密钥、令牌和环境变量。Okta 将此定性为”an identity failure”,具体而言是”how third-party apps and AI tools are granted access, what they can do, and for how long”的治理失败。

Part 1 将需要回答的问题精确表述为三个维度:which agent might act for which user in which application, and how you stop it。传统方案在这三个维度上都留下了空白。per-app OAuth 能回答「用户授权了哪个应用」,但无法回答「哪个 Agent 被授权代表用户使用这个应用」;服务账号能让 Agent 访问应用,但无法区分它代表的是哪个用户;两种方案都缺乏即时撤回机制——OAuth token 在过期前持续有效,服务账号的凭据通常是长期有效的。

Okta XAA 如何在 IdP 层做授权决策:注册、审批、策略评估与即时撤回

Okta Cross App Access(XAA)的设计思路是把授权决策从分散的应用层收归到 IdP 层。Part 1 描述的流程分为 3 个阶段:注册 Agent、管理员审批、运行时策略评估。

注册 Agent:在 Okta Universal Directory 中,AI Agent 被注册为 first-class identity。据 Okta 官方博客,Okta for AI Agents 支持从已配置的应用或 Okta Integration Network 导入已知 Agent,也支持将自建 Agent 纳入 Universal Directory 管理。目前已与 Salesforce Agentforce、Amazon Bedrock AgentCore、ServiceNow AI Platform 完成预建集成。Agent 在 Okta 中拥有独立身份,可以被分配人类 owner、应用现有身份控制策略、从单一数据源中检索。

管理员审批:管理员决定这个 Agent 可以到达哪些应用(”an administrator approves the applications it might reach”)。这一步在 IdP 层建立了 Agent 与目标应用之间的授权关系,而非在每个应用内部单独配置。

运行时策略评估:每次 Agent 请求代表某个用户访问某个应用时,Okta 都会执行策略检查。Part 1 明确描述了检查内容:”Okta checks that the agent is active, that the user is assigned to it, and that an administrator connected it to the target authorization server for the requested scopes.” 这意味着 3 个条件必须同时满足:Agent 处于活跃状态、用户被分配给该 Agent、管理员已将 Agent 连接到目标授权服务器的对应 scope。任何一个条件不满足,授权即被拒绝。

这套机制的治理效力体现在 Part 1 给出的实测数据上:管理员移除用户对 Agent 的分配后,Okta 在约 1 秒内拒绝该 Agent 对该用户的新请求,已签发令牌在 5 分钟内过期。

这组数据需要与一个重要限制一起理解。第一层是即时阻断:管理员在 Okta 中取消用户与 Agent 的关联后,Agent 在约 1 秒后就无法再为该用户获取新的 ID-JAG。但 Okta 在兑换 ID-JAG 为 access token 时不会再检查用户分配状态——Part 1 实测显示,移除用户 3 秒后,Okta 的 custom authorization server 仍为移除前签发的 ID-JAG 发出了 access token。第二层是存量令牌自动失效:已签发的 access token 在 5 分钟内过期。因此,在管理员执行撤回到所有存量令牌过期之间,存在一个最长约 5 分钟的窗口期,在此期间已兑换的令牌仍然有效。对一个 Agent 的撤回操作不影响另一个 Agent。Okta System Log 以 Agent 为 actor 记录每次授权。

需要指出的是,Part 1 的实测数据来自其演示拓扑(1 个 Agent、2 个 MCP Server),大规模 Universal Directory 下的策略评估延迟可能有所不同。

从技术协议层面看,Part 1 描述的令牌链涉及 3 种令牌,每种记录不同的决策:ID token 表示用户身份;Identity Assertion JWT Authorization Grant(ID-JAG)是 Okta 的授权决策;access token 是下游资源接受的凭据。ID-JAG 通过 RFC 8693 token exchange 获取,是单次使用的(single-use),有效期 5 分钟。最终的 access token 通过 RFC 7523 JWT authorization grant 获取,同样有效期 5 分钟,且同时包含用户和 Agent 的身份信息以及用户的组信息。

Okta 官方博客还描述了一项「kill switch」能力——Agent deactivation,可以即时撤销失控 Agent 的权限,并在所有连接系统中保留单一审计轨迹。这与 XAA 的策略评估机制互补:XAA 控制的是「哪个 Agent 能代表哪个用户」的细粒度授权,kill switch 控制的是「这个 Agent 是否还被允许存在」的粗粒度开关。

AgentCore Identity 如何在 Agent 运行时兑换授权:3 种令牌的传递链

Okta XAA 解决的是「谁被允许做什么」的决策问题,但决策本身不等于执行。Agent 代码需要拿到下游 MCP Server 能接受的 access token 才能实际调用工具。这个从 Okta 授权决策到可消费令牌的转换过程,由 Amazon Bedrock AgentCore Identity 完成。

Part 1 对 AgentCore Identity 的定位表述非常精确:”Okta is the governance plane: it decides which agent might act for which user, against which resource. AgentCore Identity is the execution plane: it holds the agent’s credentials, turns Okta’s decision into a token the resource accepts, and keeps both identities in that token.”

治理平面与执行平面的分离是这套架构的核心设计选择。Okta 不需要知道 Agent 运行在哪里、用什么框架构建;AgentCore Identity 不需要做授权决策,只需要忠实地将 Okta 的决策转换为下游可消费的令牌。

Part 1 描述的完整令牌传递链如下:

  1. 用户通过 OpenID Connect 登录 Web 前端(BFF),获得 ID token。
  2. BFF 将用户的 ID token 通过 RFC 8693 token exchange 换取 ID-JAG,此时 BFF 以 Agent 的身份向 Okta 的 org authorization server 认证。Okta 执行前述 3 项检查后签发 ID-JAG。
  3. BFF 将 ID-JAG 作为 bearer token 调用运行在 AgentCore Runtime 上的 Agent。Runtime 的 JWT authorizer 只接受 Okta 为该 Agent 签发的 ID-JAG。
  4. Runtime 从 AgentCore Identity 获取一个 workload access token,将 Agent 的 workload identity 与用户绑定。这个 token 是 AWS 内部绑定令牌,Okta 和下游资源都看不到。
  5. Agent 请求 AgentCore Identity 获取 access token。AgentCore Identity 将 ID-JAG 在 Okta 的 custom authorization server 上通过 RFC 7523 JWT authorization grant 兑换,以 Agent 身份认证。Okta 返回 5 分钟有效期的 access token,其中包含用户和 Agent 双重身份以及用户的组信息。
  6. Agent 用该 access token 调用 MCP Server。MCP Server 检查令牌是否由注册的 Agent 获取、用户的组和 scope 是否允许该工具调用。

这条链中有一个关键的安全属性:Agent 代码本身不接触凭据。Part 1 明确写道 AgentCore Identity “redeemed it for a short-lived access token with credentials your agent code doesn’t see, and keeps that token in its token vault”。Agent 代码只通过 AgentCore Identity 的 API 获取已经兑换好的 access token,Agent 自身的 client secret 和 Okta 的交互细节对 Agent 代码不可见。这消除了一类常见的安全风险:Agent 代码被攻陷后泄露长期凭据。

在性能方面,Part 1 给出了实测数据:AgentCore Identity 在一个端到端约 30 秒的请求中增加了约 0.6 秒的延迟。对于一个涉及 LLM 推理、多次工具调用的 Agent 请求来说,0.6 秒的身份兑换开销在可接受范围内。进一步看 token vault 的缓存效果:每次请求中第一次调用兑换 ID-JAG 约 0.5 秒,但当 Agent 在同一请求中调用第二个 MCP Server 时,token vault 直接返回同一令牌,中位数 58 毫秒,不再重复兑换单次使用的 ID-JAG。

Part 1 实测踩到的坑

Part 1 的实测过程暴露了几个在文档中不易察觉的配置陷阱,对实际部署有直接参考价值。

Runtime 授权器的 allowedClients 配置。Runtime 的 JWT authorizer 如果不设 allowedClients,另一个 Agent 的 ID-JAG 会以 HTTP 200 被放行,直到兑换环节才失败;设了 allowedClients 之后则直接返回 HTTP 401。这意味着缺少这项配置时,非法请求会深入到令牌兑换阶段才被拦截,增加了攻击面。Okta 把两次交换(ID token → ID-JAG、ID-JAG → access token)都绑定到 ID-JAG 里的 client,所以每个 Okta Agent 需要自己的 credential provider——拿一个 Agent 的 ID-JAG 以另一个 Agent 身份兑换会被 Okta 拒绝。

用户权限走 groups claim 而非 scope。Part 1 的测试设计中,resource connection 给所有用户同样的 scope(3 个测试用户 Alice、Bob、Carol 都拿到 xaa:read 和 xaa:write),每个用户的实际权限放在 groups claim 里:Alice 属于 xaa-readers 组,Bob 属于 xaa-writers 组,Carol 没有组。MCP Server 只接受注册 Agent 获取的令牌,且写操作要求组和 scope 同时具备。这两项检查各堵住了实测中发现的一个漏洞:当写检查只看 scope 而允许没有 groups claim 的令牌时,不属于任何组的 Carol 也能创建任务;在加 _require_agent 检查之前,一个发给服务应用、没有 act claim 的普通 token-exchange 令牌替 Bob 创建了任务。这两个实测发现说明,scope 和 Agent 身份检查缺一不可。

从 PoC 到企业:AgentCore Gateway 引入后的拓扑变化

Part 1 的设计把令牌交换放在 Agent 的角色里,并在每个 MCP Server 里重复授权检查逻辑。Part 2 把这两件事移到 AgentCore Gateway 后面,用 Cedar 策略统一管理,加上运行在 Amazon EKS 上的第 2 个 Agent,并在 CloudWatch 里把 Okta System Log 与 AgentCore traces 关联起来。Part 2 对 Part 1 拓扑的判断是:”The mechanism scales. The topology doesn’t.”

Part 2 的架构变化可以从其描述的步骤中精确还原:

  1. 用户登录,BFF 将 ID token 换取 ID-JAG,与 Part 1 相同。
  2. BFF 调用运行在 AgentCore Runtime 上的 Agent A,携带 ID-JAG。
  3. Agent A 将 ID-JAG 作为 bearer token 转发给其 Gateway 的 MCP session。Gateway 的 inbound authorizer 只接受为 Agent A 签发的 ID-JAG。Agent A 改为把 ID-JAG 转发给 Gateway 后,它的执行角色不再持有任何令牌交换或密钥权限。
  4. AgentCore 中的 Policy 组件为每个工具列表和每个工具调用评估 Cedar 策略,在 Gateway 调用目标之前执行。
  5. 对于 Cedar 策略允许的调用,Gateway 通过 AgentCore Identity 在 Okta 的 custom authorization server 上兑换 ID-JAG,获取目标资源的 access token。Cedar 策略拒绝的调用不会触发 ID-JAG 兑换。一个单次使用的 ID-JAG 可以服务一个 session 里的所有工具调用。
  6. MCP Server 检查用户的组和 scope,与 Part 1 相同,并将每个决策记录为 authz.check span。
  7. 每一跳都通过 OpenTelemetry 将 span 发送到 AgentCore Observability,CloudWatch Transaction Search 收集这些 span,Okta 通过 Amazon EventBridge 将其 System Log 流式传输到 CloudWatch。

这个架构变化的核心在于鉴权逻辑的位置从 MCP Server 内部移到了 Gateway 层。在 Part 1 的拓扑中,每个 MCP Server 都需要自己实现令牌验证和授权检查逻辑。当 MCP Server 数量从 2 个增长到数百个时,这意味着数百份重复的鉴权代码需要维护、测试和审计。Gateway 作为单一入口后,鉴权逻辑只需要在一个地方实现和维护。

Cedar 策略的设计细节。Cedar 采用 permit-only 策略、默认拒绝。每个 target 使用静态工具 schema,Cedar 策略引用的工具名才能固定——实测中,按名字引用工具的策略无法挂到动态列出的 target 上。Cedar 读缺失的 tag 会出错并被跳过——被跳过的 permit 什么也不授予,但被跳过的 forbid 什么也拦不住,所以每个 tag 要用 hasTag 保护。target 的 scope 必须与 ID-JAG 的一致。

Agent B 在 EKS 上的验证。Part 2 引入的第 2 个 Agent(Agent B,报表 Agent)运行在 Amazon EKS 上而非 AgentCore Runtime 上,只获得 xaa:read scope,有自己的 Gateway 和 credential provider。在 EKS 上没有 AgentCore Runtime 的授权器挡在前面,Agent B 自己校验 ID-JAG 的签名、issuer、audience、令牌类型、过期和 client_id。实测结果:Agent A 拒绝 Alice 和 Carol 的写操作、为 Bob 创建了任务;Agent B 告诉 Bob 它没有创建任务的工具——同一用户通过不同 Agent 拿到不同权限,由 Okta 决定。这证明了 Gateway 架构的运行时无关性——无论 Agent 运行在 AgentCore Runtime 还是 EKS 上,只要它拥有 Okta 身份并通过 Gateway 访问工具,同一套治理机制就能生效。

据 Okta 与 AWS 扩展合作的新闻稿,双方正在探索的联合用例包括:将 XAA 协议构建到 Amazon Bedrock AgentCore 中、在 AgentCore Gateway 中使用 Okta 作为 Policy Decision Point(PDP)配合 Cedar 策略语言实现关系型访问控制(ReBAC)、以及通过 Auth0 for AI Agents 实现 Human in the Loop 审批流程。据 Okta 新闻稿,如今 Amazon Bedrock AgentCore 上几乎每 10 秒就有一个新 Agent 部署。Okta 通过 AWS Marketplace 的累计销售额已超过 20 亿美元,距离超过 10 亿美元不到 2 年。

Gateway 的 Inbound Authorizer 与两种授权路径的差异

Part 2 对 Gateway inbound authorizer 的技术细节给出了具体描述。其 issuer 是 Okta 的 org authorization server,audience 是 custom authorization server,allowedClients 将其锁定到单一 Agent。这意味着即使攻击者获取了某个 ID-JAG,如果该 ID-JAG 不是为这个特定 Agent 签发的,Gateway 也会拒绝。

这种设计与 Part 1 中 Runtime 的 JWT authorizer 逻辑一致,但位置不同。在 Part 1 中,Runtime 的 authorizer 保护的是 Agent 本身——确保只有合法的 ID-JAG 能触发 Agent 执行。在 Part 2 中,Gateway 的 inbound authorizer 保护的是工具层——确保只有合法 Agent 的合法 ID-JAG 能通过 Gateway 访问 MCP Server。两层 authorizer 形成纵深防御。

XAA 与按用户 OAuth 同意的适用条件不同。Part 2 明确指出,XAA 只适用于目标应用的授权服务器接受 ID-JAG 的情况,此时由 Okta 管理员决定哪个 Agent 可以代表哪些用户,用户看不到同意弹窗。有自己授权服务器的应用(例如 GitHub)仍靠每个用户自己的 OAuth 同意。AWS 在实测中部署了同意门户和一个 GitHub target:Alice 连接了 GitHub 测试账号,Bob 没有连接。两条路径的撤回方式也不同:XAA 下 Okta 的控制会阻止下一次 ID-JAG 签发,已签发令牌约 5 分钟内过期;同意路径下 Okta 只控制登录,已存储的授权在用户被停用后依然存在,直到手动断开。Part 2 建议「按目标应用选择,而不是整个企业只选一次」。

审计与可观测性:从分散日志到统一追踪

Part 2 架构中的可观测性设计值得单独讨论。每一跳都通过 OpenTelemetry 发送 span 到 AgentCore Observability,CloudWatch Transaction Search 收集这些 span。同时,Okta 通过 Amazon EventBridge 将其 System Log 流式传输到 CloudWatch,实测事件在 Okta 发布后约 1 秒到达。

Part 2 中 MCP Server 将每个授权决策记录为 authz.check span,这些 span 被统一收集到 CloudWatch。用 CloudWatch Omni 一条查询就能把 ID-JAG 授权和它导致的决策连起来。Part 2 给出了一个具体的查询示例:Okta 为 Alice 给任务 Agent 授予 xaa:read 和 xaa:write,16.7 秒后 MCP Server 拒绝了她的 create_task(因为 Alice 属于 xaa-readers 组,没有写权限),随后允许了 list_tasks。

性能方面,Part 2 给出了 24 小时 span 的统计:Gateway A 一次工具调用的身份与策略 span 中位数合计约 0.2 秒,MCP Server 自己的 authz.check span 不到 1 毫秒;Gateway B 的 GetResourceOauth2Token 中位数 408 毫秒。

这种端到端的追踪能力回答了「谁在什么时间授权了哪个 Agent 代表哪个用户访问了什么数据,以及每一步的授权决策依据是什么」。

XAA 与普通令牌交换的差别

Part 1 在同一 Okta 授权服务器和用户上对比了 XAA 与普通 RFC 8693 token exchange。结论是:”Plain token exchange carried the user’s identity, but not the user’s authority”——普通令牌交换带上了用户身份,但没有带上用户的权限。

这个差别在 Part 1 的实测中有具体体现。在加 _require_agent 检查之前,一个发给服务应用、没有 act claim 的普通 token-exchange 令牌替 Bob 创建了任务。Okta 指南里交换后的令牌应带 act claim,但 Part 1 的测试没有收到。这意味着普通令牌交换在实测中既缺少用户权限信息(groups claim),也缺少 Agent 身份标识(act claim),无法支撑「哪个 Agent 代表哪个用户」的治理需求。

两者解决的问题层次不同。XAA 回答的是治理层面的问题:管理员在 IdP 中配置策略,决定 Agent 与应用之间的授权关系,并能即时撤回。普通令牌交换解决的是协议层面的问题:令牌如何在调用链中安全传递,同时保留用户身份信息。Part 2 结尾把「通过 Gateway 做普通令牌交换」的读者指向 AWS 另一篇文章《Implement on-behalf-of token exchange for multi-tenant agents with Amazon Bedrock AgentCore Gateway》。

Shadow Agent 发现:治理的前提是可见性

Okta 官方博客中描述的另一项能力与 Part 1/Part 2 的授权治理形成互补:Shadow AI Agent Discovery。该功能通过在托管的 Chrome 浏览器中监控新的 OAuth consent grant 来检测未管理的 Agent,用用户上下文丰富发现结果,并在集中视图中呈现未管理的 Agent。

这解决了授权治理的前置问题:在你能治理 Agent 之前,你首先需要知道你的环境中有哪些 Agent。Okta 官方博客将此概括为安全领导者需要回答的第一个问题:”Where are my agents?” 如果员工在未经 IT 部门批准的情况下将企业账号连接到第三方 AI 工具(正如前述的安全事件案例),Shadow Agent Discovery 能够检测到这种 OAuth consent grant 并将其呈现给安全团队。

这种「发现 → 注册 → 授权 → 审计 → 撤回」的完整生命周期管理,是 Okta 将 AI Agent 定位为 first-class identity 的具体体现。Agent 不再是挂在某个用户账号下的附属品,而是拥有独立身份、独立策略、独立审计轨迹的实体。

架构选择的行业启示

Part 1 和 Part 2 展示的架构有一个贯穿始终的设计原则:治理决策与执行分离,且治理决策在 IdP 层完成。Okta 做决策,AgentCore Identity 做执行,Gateway 做集中入口,Cedar 做策略声明,CloudWatch 做统一审计。每个组件有明确的职责边界。

这种分离带来的直接好处是:当企业需要更换 Agent 框架(从 AgentCore Runtime 迁移到 EKS,正如 Part 2 中 Agent B 所示)、增加新的 MCP Server、或调整授权策略时,变更的范围是局部的而非全局的。更换运行时不影响 Okta 的授权策略;增加 MCP Server 只需要在 Gateway 和 Cedar 策略中添加配置;调整授权策略只需要修改 Okta 中的 Agent-用户分配关系。

笔者判断,这套「IdP 层治理 + 运行时兑换 + Gateway 集中入口」的三层架构,可能成为 Agentic AI 企业部署中身份治理的参考模式。原因有两个:第一,它复用了企业已有的 IdP 基础设施(Okta Universal Directory、策略引擎),降低了采用门槛;第二,它通过标准协议(RFC 8693、RFC 7523、OpenID Connect)而非私有 API 实现,具备跨供应商的可移植性。

据 Part 1 描述,作者团队服务媒体与娱乐公司(制片追踪、片名与素材元数据、版权与本地化、观众/玩家支持等系统各有授权模型和审计方式),问题最早在那里遇到;令牌链本身不限行业,金融、医疗、公共部门可放进同样的控制。Part 1 实操约 1 小时,Part 2 新增部分约 90 分钟,EKS 控制面每小时 0.10 美元;代码为非生产用途的示例代码。

这套方案目前展示的仍然是与 Okta + AWS 生态深度绑定的实现。对于使用其他 IdP 的企业(Okta 官方博客称 Okta 可与主流 IdP 联合),如何实现同等的治理能力,仍然是一个开放问题。Okta 与 AWS 的多年期战略合作协议(SCA)和 AWS Marketplace 上超过 20 亿美元的累计销售额,说明双方在商业层面已经深度绑定,但技术层面的开放性——特别是 XAA 作为开放协议(Okta 新闻稿中称之为”the open Cross App Access (XAA) protocol”)的跨平台采用——仍有待观察。


主题分类:企业AI落地

参考资料

  1. Govern AI agent access with Okta Cross App Access and Amazon Bedrock AgentCore – Part 1 — AWS Machine Learning Blog, 2026-11-06
  2. Okta for AI Agents: General Availability announcement and product overview — Okta Official Blog, 2026-11-06
  3. Okta expands collaboration with AWS with multi-year commitment to advance agentic AI security — Okta Newsroom, 2026-11-05