How do you authorize each Bedrock AgentCore tool call instead of trusting a broad IAM role?
An AgentCore agent signs in once. Then it makes many tool calls. The session can run up to eight hours. Say those calls all ride the same broad IAM role. Each call then carries the same standing power. A prompt injection that quietly hijacks the agent gets all of it. Per-call authorization breaks that. It makes each tool request prove itself.
The native AWS path looks like this. Front your current MCP tools with an AgentCore Gateway. It became the single controlled entry point for agent-to-tool flow. That happened when Amazon Bedrock AgentCore reached general availability on October 13, 2025. Attach an AgentCore Policy. It is Cedar-based. Per AWS's whats-new page, it went generally available on March 3, 2026. The gateway then intercepts and evaluates every agent-to-tool request at runtime. It checks each one against your policies. Before it lets the tool run. The principal and tags come from the JWT. The action is the MCP tool call itself. So the check happens per tool call, not once per session. Then derive user identity from the signed-in principal. Never from a client-supplied header.
Done right, that is per-call authorization at the gateway. AWS ships most of it out of the box. The rest of this piece covers the system underneath. And the two spots where it still leaves you exposed.
How AgentCore inbound auth actually works
AgentCore Runtime gives you just one inbound auth system per runtime. You choose it at config time. Not both at once. The runtime OAuth devguide lays out the two choices.
- IAM SigV4, the default. The caller signs the request with AWS credentials. You authorize with IAM. This is just where the broad-role habit sneaks in. Pointing everything at one role is the easy path.
- A JWT Bearer Token, via a
customJWTAuthorizer. You give it four things. AdiscoveryUrl, the OpenID Connect.well-knowndocument.allowedClients, matched against theclient_idclaim.allowedAudience, theaudclaim. AndallowedScopes, thescopeclaim. AgentCore then checks the token issuer. Then the signature. Then the expiry.
The JWT path is the one that hands you real claims. Claims to authorize on. It is also where AgentCore Policy reads its principal and tags from. Want per-call checks? The JWT authorizer is the inbound path that supports them. SigV4 alone leaves you authorizing on AWS credentials, not user claims.
The on-behalf-of model, and the Runtime-User-Id trap
This is where AgentCore is really strong. It also leaves a trap for the unwary.
An inbound JWT arrives. AgentCore exchanges it, via bedrock-agentcore:GetWorkloadAccessTokenForJWT, for a Workload Access Token. That token carries both the agent workload identity and the end-user identity. It then fetches third-party OAuth tokens. 3LO, think Google Drive. From the AgentCore Token Vault. Keyed by workload identity plus user id. This is AgentCore's dual inbound/outbound auth model at work. It is the right shape. The agent acts for a specific user. Not as a faceless service.
The other path is the problem. The X-Amzn-Bedrock-AgentCore-Runtime-User-Id header does not check one thing. It is hit via GetWorkloadAccessTokenForUserId. The user id, against a signed-in identity. AWS documents it as an opaque tag. AWS also tells you to lock down the IAM action bedrock-agentcore:InvokeAgentRuntimeForUser. Derive the user-id from the signed-in principal instead. That stops impersonation. Say an unverified header gets to pick the user. Your per-call policy then ends up trusting a claim nobody checked. Derive identity from the JWT. Leave the raw header out of the call.
AgentCore Policy is already per-call, so what's missing?
AWS's native path is per-tool-call. AgentCore Policy first landed in preview in December 2025. By AWS's account, it hit GA on March 3, 2026. It checks each request at the gateway against Cedar policies. Before the tool runs. Default-deny evaluation applies per request. It is worth being precise here. AgentCore is per-call too. Any claim otherwise gets the system wrong.
Cedar does real per-call work at the gateway. It answers one question. Is this principal, with these tags, allowed to invoke this tool right now? It uses the JWT claims and session tags at query time. It also covers admin and setup calls, with the same engine. Lake Formation session tags and Lambda interceptors scope the same way. Both work by IAM role and claims. They check at query time.
What none of those three does comes down to two things. This follows from how each one works. Not from any published limit. They check a request against policy, which they really do per call. But they do not put a scope ceiling on a passed-down token. The token still carries whatever the authorization server granted. Nothing downstream shrinks it. And they do not revoke an already-issued token before expiry. A hacked agent keeps its access until the token times out. Those are the two gaps the rest of this piece is about. Neither one contradicts that. Cedar decides at the gateway, on each call.
The two gaps: a scope ceiling and a next-call kill
Bedrock AgentCore per-call authorization is the layer here. We built DataShield Auth to add it, and describe it as design, not certification. SOC 2 is not there yet; that is planned, not attested. I would rather say that plainly than let you assume otherwise.
DataShield sits as a complement to AgentCore and IAM, not a replacement. It is not a prompt proxy, either. In deployment terms it sits post-gateway, at the dispatch layer. That is where a token-validated tool call gets routed. Routed to the tool it targets. It is not inline as a proxy in front of the model. And not a sidecar inside it. It issues scope-ceiling MCP tool tokens in the RFC 9068 JWT access-token format. It checks them via JWKS. Each tool call then runs through a dispatch pipeline. The steps run in a fixed order.
The pipeline starts with the scope ceiling. The passed-down token cannot exceed the ceiling. Not even when the upstream authorization server granted more. So on-behalf-of delegation carries a ceiling. OAuth scopes negotiated at issuance do not enforce that on their own. Next it checks the call against the caller's power tier. Then it re-checks revocation mid-session. A revoked agent is stopped at that call. Not only when the token later expires. The call is then metered as it dispatches. Finally it is written into a sealed, hash-chained audit record.
The revocation step matters most here. An agent starts misbehaving. Waiting for a short-lived token to expire leaves it live. Live and still active. A mid-session re-check stops it at its next try.
What the MCP spec and the RFCs actually require
None of this is DataShield inventing physics. The Model Context Protocol authorization spec (revision 2025-11-25) sets an MCP server as one thing. As an OAuth 2.1 resource server. It hands you hard rules. The resource-server model itself arrived back in revision 2025-06-18.
- Servers MUST check that access tokens were issued exactly for them. They use the
audaudience claim, per RFC 9068. - Servers MUST publish OAuth 2.0 Protected Resource Metadata (RFC 9728). That way a client can find the token issuer for that resource. Before it ever presents one.
- Clients MUST build RFC 8707 resource indicators. That binds each token to one specific server, via the
resourceparameter. - Token passthrough is forbidden. You do not forward the client's token upstream.
- Authorization MUST accompany each HTTP request. Even within one logical session.
- Say a request needs more scope than the presented token carries. The server then steps up. It returns a 403, with a
WWW-Authenticateheader. That lets the client negotiate the extra scope. The server does not just quietly widen the token in place. - Authorization servers SHOULD issue short-lived tokens.
The short-lived rule is worth dwelling on. A token can leak, or come out too broad. The spec's fix: make it short-lived. It has no way to cap a passed-down token below what was granted. No way to revoke an issued token before expiry, either. Step-up authorization handles the opposite problem: a token that is too narrow. It lets the client ask for more. But nothing in the spec lets a go-between hand back less scope. Not less than the authorization server granted. A short lifetime narrows the window of exposure. It does not shrink a token's scope. It does not stop a token on demand.
The spec's own security best practices push for fine-grained per-tool access control. Each tool is its own trust boundary. That is why a broad standing role invites the confused-deputy problem. For more on hardening the server, see MCP server security. Also see the sibling piece on per-call authorization for AI agents.
Threat model: what a hijacked agent actually does
It helps to keep in-the-wild incidents and proof-of-concept research apart. As of this writing, I know of no confirmed in-the-wild incident. No hacked Bedrock AgentCore agent has been shown abusing a passed-down token. The named events below are researcher demos. That makes them credible warnings, not proof of active abuse.
EchoLeak (CVE-2025-32711) was disclosed by Aim Labs, on June 11, 2025. Aim Labs called it the first zero-click AI flaw. Found in Microsoft 365 Copilot. A single crafted email needed no user action. It used a novel "LLM Scope Violation" trick. That trick made Copilot access and exfiltrate in-scope company data. It is a researcher disclosure, not a report of an in-the-wild breach.
Aim Labs also reported CurXecute (CVE-2025-54135). A remote-code-execution chain, in the Cursor IDE. By its authors' account, it is a proof-of-concept. For indirect prompt injection, via MCP auto-start. Untrusted content writes a .cursor/mcp.json file. Cursor runs it before the user accepts it. That leads to RCE. It is a demo, not a confirmed real-world attack.
Both map onto the OWASP Top 10 for Agentic Applications. OWASP published it on December 9, 2025. OWASP frames it as its first flagship list aimed at autonomous agents. Its top category is ASI01 Agent Goal Hijack. It covers attackers who hide new goals in documents, emails, or RAG results. The agent's goal is then quietly rewritten. The list seems to fold classic prompt injection into ASI01. So read the full taxonomy. ASI02 Tool Misuse. ASI03 Identity and Privilege Abuse. On down to ASI10 Rogue Agents. Do not lean on any one-line summary of where the line sits. The pattern in each case is the same. Broad standing power, plus untrusted input, leads to exfiltration. With no classic bug anywhere in sight.
Bedrock AgentCore authorization best practices: how to prevent tool-call overreach
These are the practices worth applying when you build authorization on AgentCore. This is an engineering threat model for the authorization control. Treat it as one control among several. Not a complete security program.
- Use the JWT authorizer, not SigV4, as your inbound path. When you want per-call checks. Set
allowedClients,allowedAudience, andallowedScopestightly. - Front each tool behind an AgentCore Gateway. Attach an AgentCore Policy so Cedar checks each tool call. Do not let agents reach tools out of band.
- Derive user identity from the signed-in principal. Lock down
bedrock-agentcore:InvokeAgentRuntimeForUser. Never trust theRuntime-User-Idheader as identity. Would rather buy this? OAuth-based agent identity does the same job. Per token, not per broad role. - Never pass tokens through. Exchange for a fresh, audience-scoped token per downstream call. Check the
audclaim server-side, per RFC 9068. - Bind tokens to their resource, with RFC 8707 resource indicators. A token stolen from one tool is then inert at the next.
- Keep access tokens short-lived. Then add what a short lifetime cannot give you. A scope ceiling on passed-down tokens. Plus a revocation path. It stops an agent at its next call, not only at expiry.
- Log each call into a tamper-evident record you can verify later. "Which call touched what, under whose authority" then has a cryptographic answer.
- Assume the model is hacked. Put the check point outside the agent. At the gateway and the dispatch layer. The model cannot touch either one.
Govern what the agent can reach, not only what it will do
Bedrock AgentCore per-call authorization governs what an agent is allowed to do. It says nothing about what the agent can reach. That is the part most teams skip.
That second question is where tokenization earns its keep. Tokenize sensitive fields at ingest. Say a hijacked agent slips its constraints anyway. It then finds tokens where the PII used to be. There is no raw data left to send to an attacker's domain. Pair that with per-call authorization and mid-session revocation. Then seal each call into a tamper-evident audit chain you can verify. Check it later. The data model behind that lives in the ontology. What counts as sensitive, and how it maps.
Prompt injection corrupts the agent's reasoning directly. So it is a weak bet. Do not stake everything on constraining what the agent decides to do. Constrain what it can reach instead. Authorize each call outside the model, at the level of the call. AWS gives you a strong per-call check at the gateway. Add a scope ceiling. A next-call kill. And tokens in place of raw fields underneath it. A hijacked agent then has few ways left to cause harm. Want to watch the pipeline run against your own tools? That is what a scoped walkthrough is for.
Watch: related explainers
Two AWS talks on AgentCore access control. Plus a reality check on MCP security.
Frequently asked questions
How do you authorize each Bedrock AgentCore tool call instead of trusting a broad IAM role?
Front your MCP tools with an AgentCore Gateway. Attach an AgentCore Policy: Cedar-based, GA since March 3, 2026, per AWS. The gateway catches each tool request. It checks it against policy, before the tool runs. Use the JWT inbound authorizer, not IAM SigV4. That way the call has real claims to work with. Derive the user identity from the signed-in principal. That makes the check per tool call, not once per session. A hijacked agent cannot spend one broad role across each call.
Does Amazon Bedrock AgentCore support per-tool-call authorization natively?
Yes. AgentCore Policy hit general availability in 2026 (AWS dates it to March 3). It checks each agent-to-tool request at the Gateway, against Cedar policies. Before allowing or denying the call. It uses the principal and tags from the JWT. The MCP tool call is the action. So the native AWS path is really per-tool-call. What it does not add: a scope ceiling on passed-down tokens. It also has no way to revoke a live token early.
What does AgentCore Policy not do that DataShield adds?
Two things. AgentCore Policy, Lambda interceptors, and Lake Formation tags all decide per call. Each scopes by IAM role and JWT claims, at query time. But none puts a scope ceiling on a passed-down token. Below what the authorization server granted. And none revokes an already-issued token before expiry. DataShield adds a scope ceiling on passed-down tokens. Plus mid-session revocation, which stops a hacked agent at its next call. It is a complement to AgentCore and IAM. Not a replacement.
Is the AgentCore Runtime-User-Id header safe to trust for identity?
No. The X-Amzn-Bedrock-AgentCore-Runtime-User-Id header, hit via GetWorkloadAccessTokenForUserId, is an opaque tag per AWS docs. It is not checked against a signed-in identity. AWS tells customers to restrict the IAM action bedrock-agentcore:InvokeAgentRuntimeForUser. Derive the user-id from the signed-in principal instead, to stop impersonation. Use the JWT exchange path (GetWorkloadAccessTokenForJWT) for identity. Not the raw header.
Has a Bedrock AgentCore agent been compromised in the wild?
Not that I have seen confirmed as of mid-2026. The well-known cases are researcher proofs-of-concept. Not in-the-wild breaches. EchoLeak (CVE-2025-32711, disclosed June 11, 2025) hit Microsoft 365 Copilot. CurXecute (CVE-2025-54135) hit the Cursor IDE. Aim Labs reported both. They show the same failure mode AgentCore agents share. Broad standing power, plus untrusted input, leads to data exfiltration. That is what OWASP ASI01 Agent Goal Hijack describes.