AgentCore 代理用户身份调用 ServiceNow
教程介绍配置 Bedrock AgentCore Gateway 与 Microsoft Entra ID 的 OBO 令牌交换,让 Agent 保留调用用户身份访问 ServiceNow REST API。网关原生完成交换,无需拦截器 Lambda 或自定义代码。
AI 深度解读
让 AI Agent 帮用户查工单,难点不只是把 ServiceNow API 接成工具,还得让下游知道「这次是谁在查」。如果所有请求都用同一个应用身份,用户原有的权限边界和审计记录就可能失真。
AWS 这篇文章给出的方案是:由 AgentCore Gateway 配合 Microsoft Entra ID,替用户交换一张面向 ServiceNow 的令牌,再让 ServiceNow 按该用户的权限处理请求。它的价值是把身份委托放进网关配置,省去自行维护令牌交换代码。
为什么登录成功,还不能直接调用 ServiceNow
这条链路里有两个不同的身份入口:Agent 应用和 ServiceNow API。用户拿到的 JSON Web Token(JWT)面向前者,ServiceNow 接受的令牌则必须面向后者。
区别在于 audience,也就是令牌的目标接收方。原文举例,入站令牌对应 Agent 的 client ID:
3ff88e17-XXXX-XXXX-XXXX-XXXXXXXXXXXX
ServiceNow 要求的却是 API connector 的 client ID:
5d4a7a11-XXXX-XXXX-XXXX-XXXXXXXXXXXX
用户已经登录,不代表这张令牌可以拿去调用任意 API。直接转发,接收方对不上;改用共享应用凭证,又会丢掉发起操作的用户身份。
On-Behalf-Of(OBO,代表用户)流程解决的就是这个问题。Gateway 把入站令牌、Agent 应用的客户端凭证、目标 scope,以及 requested_token_use:on_behalf_of 交给 Entra。Entra 验证委托授权后,签发面向 ServiceNow API、保留原用户身份声明的新令牌。
交换的是令牌的目标接收方,用户身份沿着请求链继续传递。
接着,ServiceNow 根据令牌里的 preferred_username 找到本地用户,再应用该用户的访问权限。整个链路需要同时打通「谁在调用」和「这个人能访问什么」。
真正要对齐的是三处配置
第一处是 Entra 的应用关系。需要两个 application registration,分别代表 Agent 和 ServiceNow API connector。
API connector 通过 Expose an API 暴露 user_impersonation scope;Agent 应用创建 client secret,并获得调用该 connector 的 API permissions。Gateway 使用的是 Agent 应用的 client ID 和 client secret,不能把两个注册的用途混在一起。
第二处是 ServiceNow 的用户映射。在 System OAuth > Inbound Integrations 创建第三方 OIDC 身份提供方集成,填写 API connector 的 client ID,再配置 Entra 租户的 OIDC metadata URL。
原文假定 preferred_username 中放的是用户邮箱,因此将它映射到 ServiceNow 的 Email 字段。这里有明确前提:声明内容必须能匹配到已有用户。令牌有效,不等于用户一定能解析成功。
ServiceNow 还要创建 auth scope,例如 APIConnector,并把范围限制在 Table API 或 Agent 实际需要的接口。Entra 的 user_impersonation 和这里的 auth scope 分属不同配置环节,不能只配一个就认为授权结束。
原文还提到 JTI 校验:如果 Entra 令牌没有 jti,可以清除 JTI verification;同时建议考虑将 Entra 的 uti 映射成 jti,用于额外的重放防护和拒绝列表。文章没有展开映射如何落地,不能把关闭校验当成完整方案。
第三处才是 Gateway。先通过 Build > Identity > Add Outbound Auth > Add OAuth client 创建出站认证,选择 Customized provider,填写 Agent 凭证和 Entra discovery URL,并启用 JWT authorization grant。
创建 Gateway 时必须选择 MCP version 2025-11-25。按原文说明,用更早 MCP 版本创建的 Gateway 无法启用原生 OBO。入站 JWT 授权则配置 Agent 的 Application ID。
最后,在 REST target 的出站认证里选择 OAuth 和前面创建的配置,启用 Token exchange grant,填入:
Scope: api://5d4a7a11-xxxx-xxxx-xxxx-xxxxxxxxxxxx/user_impersonation
Custom parameter (key-value pair): requested_token_use:on_behalf_of
其中 requested_token_use:on_behalf_of 是关键参数。原文明确指出,缺少它,Entra 不会执行所需的 OBO 交换,ServiceNow 调用会出现认证错误。
Agent 代码可以很薄,但示例还不是完整应用
Gateway target 使用 OpenAPI 描述 ServiceNow REST API。原文规范采用 OpenAPI 3.0.0,接口描述版本为 1.0.0,服务器示例是 https://devxxx.service-now.com/api/now/table,通过 GET /incident 查询工单。
接口的 operationId 是 getIncidents,可传 sysparm_limit、sysparm_query、sysparm_fields;其中条数默认 10,过滤条件示例为 active=true。这些参数分别控制数量、筛选和返回字段,适合把查询能力收窄成明确的工具契约。
下面这段原文代码展示了调用端如何携带用户令牌接入 MCP,以及让 Agent 使用预先配置的工单工具:
import sys
import strands
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from mcp.client.streamable_http import streamablehttp_client
token = sys.argv[1] if len(sys.argv) > 1 else sys.exit("Usage: python snow_agent.py <TOKEN>")
MCP_URL = "<GATEWAY_URL>"
mcp_client = MCPClient(lambda: streamablehttp_client(url=MCP_URL, headers={"Authorization": f"Bearer {token}"}))
agent = strands.Agent(
model=BedrockModel(model_id="global.anthropic.claude-sonnet-4-6", streaming=False),
system_prompt=(
"You have a tool called 'target-snow___getIncidents'. "
"When asked about incidents, call it immediately with no parameters. "
"Never ask the user for credentials or instance details --- the tool is pre-configured."
),
tools=[mcp_client],
)
while (user_input := input("\nYou: ")).lower() not in ("quit", "exit", "q"):
print(f"\nAgent: {agent(user_input)}")
重点看 headers={"Authorization": f"Bearer {token}"}:调用端传入用户令牌,交换逻辑由 Gateway 承担。target-snow___getIncidents 也需要与实际暴露的工具名对应。
示例使用 global.anthropic.claude-sonnet-4-6,并设置 streaming=False。但它从命令行读取现成 token,没有展示用户登录、令牌刷新和完整的错误处理;提示词还要求无参数调用,因此没有演示前面几个查询参数的使用。
所以,原文的「无需自定义代码」应理解为无需自行编写 OBO 交换代码,不能理解为整个 Agent 应用无需开发。
我的判断
我认同把身份委托放在网关层处理。相比每个 Agent 自己交换令牌,或维护专门的 AWS Lambda interceptor,这种做法能把重复逻辑收拢到 target 配置,业务代码也更容易保持简洁。
但这是一篇配置教程。作者给出了配置步骤和最小调用示例,没有给出性能、失败率、维护成本的对照数据,也没有展示不同权限用户的实际返回结果。因此,「省去交换代码」有明确依据,「整体更快、更稳」则不能由文章推出。
它还依赖一组条件:团队能管理 Entra 应用注册,ServiceNow 已有正确的用户和权限,并且租户声明能可靠映射到本地账号。前置权限也不低:需要 AWS 的 Gateway 使用权限、Entra 至少 Application Developer 角色,以及 ServiceNow 管理权限。
保留用户身份,是执行用户权限的前提,配置本身不能代替权限验证。
原文建议搭配 Amazon Bedrock Guardrails 过滤有害内容、校验输入输出,但内容校验和下游访问控制解决的是不同问题。也别把提示词里的「不要询问凭证」当成权限约束,真正生效的边界仍在身份系统和 ServiceNow。
能拿来做什么
- 如果你正在做企业工单助手,先画清用户令牌、Agent 应用和目标 API 的对应关系,再决定工具怎么接;重点检查每一跳的 audience,以及最终由哪个系统执行用户权限。
- 如果你负责前端登录后的 Node 调用链,可以借鉴这里的职责分工:调用端携带用户令牌,网关负责目标 API 的令牌交换。具体代码仍需按现有身份体系实现,原文没有提供 Node 版本。
- 联调 ServiceNow 时,用权限不同的真实测试账号验证返回范围;遇到认证错误,依次检查
preferred_username映射、ServiceNow auth scope 和 Agent 的 Entra API permissions。 - 做概念验证后,删除测试用 Gateway、targets、outbound auth 配置,以及专门创建的 Entra 注册和 ServiceNow 集成,避免遗留费用与无用配置。