When you expose a tool to agents, the interesting decision is not the tool. It is who is allowed to call it. Amazon Bedrock AgentCore Gateway turns that decision into a single field — authorizerType — and the value you pick determines whether the tool is a private capability for your own agents or a multi-tenant service anyone can reach with a token.
I run two gateways in front of AgentCore tools. One is authorized by IAM: my agents sign each request with an AWS profile, and there is nothing in the config to leak. The other is authorized by a JWT from an external identity provider: callers mint an OAuth token and send it as a bearer header. Same Gateway primitive, opposite trust models. This post is about how to choose between them — and the third option that sits in the middle.
The Gateway is an authorizer with a tool behind it
AgentCore Gateway exposes tools over MCP. WebSearch, Lambda functions, and other connectors all attach as targets, and an agent discovers them with a standard tools/list call. That part is uniform. What differs per gateway is the gate in front of it.
A Gateway has exactly one authorizerType, set at creation, and it only accepts two values:
AWS_IAM— the caller must present a SigV4-signed request from an AWS principal.CUSTOM_JWT— the caller must present a JWT the Gateway validates against an external identity provider (issuer, audience, expiry, scopes).
Everything else — the MCP protocol, the connector, the tool schema — is downstream of that choice. So “how do I wire web search into my agent” is really “which of these two gates do I want, and how does the caller get through it.”
Pattern 1 — IAM + SigV4: the caller is you
With AWS_IAM, authorization collapses to a single question: does the caller hold valid AWS credentials for the account that owns the gateway? SigV4 answers it on every request. There is no token endpoint, no client secret, no refresh loop.
The caller resolves credentials from the standard AWS chain — environment variables, then a named profile, then an instance or container role — and signs the request. In code that is the only load-bearing part:
import botocore.session, botocore.auth, botocore.awsrequest
creds = botocore.session.Session().get_credentials().get_frozen_credentials()
req = botocore.awsrequest.AWSRequest(method="POST", url=gateway_url, data=body, headers=headers)
botocore.auth.SigV4Auth(creds, "bedrock-agentcore", region).add_auth(req)
Notice what get_credentials() does not take: no API key, no secret, no token. On my machine the tool config sets a profile name and a region, and botocore does the rest. There is nothing in that config to leak, nothing to rotate, and nothing to expire.
The trade-off is the reach. “Auth” here means “has an IAM identity in my account.” That makes it perfect for callers who already live inside your AWS boundary — your own laptop with a profile, a Lambda with an execution role, an ECS task with a task role. It makes it the wrong choice the moment you want to hand the tool to someone who is not in your account, because the only way to let them in is to give them an IAM principal, which is a far heavier grant than “here is a token.”
Pattern 2 — IAM + cross-account role assumption: the caller is another AWS account
If the caller is on AWS but in a different account — a partner, a separate org account — you can stay in IAM-land without adding OAuth. Keep the gateway on AWS_IAM, and have the external caller sts:AssumeRole into a role in your account that is permitted to invoke the gateway. They sign with the assumed role’s temporary credentials; the Gateway sees a valid principal from your account.
This extends IAM auth across an account boundary without introducing an identity provider. You manage one trust policy; they manage one assume-role hop. Still no bearer tokens, still no secret distribution.
The cost is that both sides must be on AWS and comfortable with cross-account role plumbing. It does not help you reach a caller who has no AWS identity at all — a SaaS backend, a partner’s non-AWS service, an individual you want to grant access to by handing them a credential.
Pattern 3 — JWT + OAuth: the caller is everyone else
CUSTOM_JWT is the multi-tenant answer. The Gateway validates a JWT against an external identity provider — checking issuer, audience, expiry, and scopes — and the caller obtains that token however the IdP allows. For a machine-to-machine caller that is the client_credentials grant: exchange a client ID and secret for a short-lived access token, cache it, refresh on expiry, and send it as Authorization: Bearer <token>.
# machine-to-machine token, then bearer header on every call
token = post(f"https://{idp_domain}/oauth/token", json={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"audience": audience,
"scope": "mcp:invoke",
}).json()["access_token"]
forward(message, headers={"Authorization": f"Bearer {token}"})
Now the caller needs no AWS identity at all. Anyone you issue an IdP client to can reach the tool, and you can scope what they are allowed to do through the token’s claims. This is the model to pick when the tool is meant to be shared — an internal platform tool, a service you expose to partners, anything with more than one tenant.
The cost is real, and it is everything IAM did not make you do. You now own an identity-provider relationship, client-secret issuance and storage, token lifecycle and refresh, and scope design. The bearer secret has to live somewhere on the caller’s side and be protected. That is the “extra auth layer” — it buys you reach, and you pay for it in operational surface.
The decision, in one table
| IAM + SigV4 | IAM + cross-account role | JWT + OAuth | |
|---|---|---|---|
authorizerType | AWS_IAM | AWS_IAM | CUSTOM_JWT |
| Caller identity | AWS creds in your account | Assumed role from another AWS account | Token from your IdP |
| Secret in caller config | None | None | Client secret |
| Token lifecycle | None | STS handles it | You manage refresh |
| Who can call it | You and things in your AWS boundary | You + trusted AWS accounts | Anyone you issue a client to |
| You operate | Nothing extra | One trust policy | An IdP, secrets, scopes |
| Best for | Single-owner tools | AWS-to-AWS B2B | Multi-tenant / external |
The pattern is not “IAM is simpler, so use IAM.” It is: push authorization to the layer that already knows your caller. If the caller is you or your own AWS workloads, that layer is IAM, and SigV4 gives you auth with nothing to rotate. If the caller is a separate AWS account, it is still IAM, through role assumption. If the caller is anyone else, that layer is an identity provider, and you take on the token machinery because reach is the thing you actually need.
What’s missing
Two gaps worth naming. The choice is made at gateway creation and is not a runtime toggle — a gateway is either IAM or JWT, so serving both an internal AWS caller and an external tenant means standing up two gateways in front of the same tool, not flipping a flag. And validating the token is not the same as scoping the caller: CUSTOM_JWT proves the token is genuine, but going from this is a valid token to this caller may call this tool, with these arguments is a separate layer. AgentCore does provide that layer — a Cedar policy engine you attach to the gateway, default-deny, where the principal is the caller’s identity, the action is the tool name, and conditions can read the token’s claims or even the tool’s own arguments. What is on you is authoring those policies and issuing per-user tokens rather than a single machine credential; the enforcement mechanism is there, unwired until you write the rules.
The open thread I have not resolved is the seam between the two. When a tool needs to be reachable by both my own agents and an external caller, I run two gateways — but that means two authorizers, two configs, and two things to keep in sync for one underlying capability. I do not yet have a clean pattern for a single tool that is IAM-authorized for me and JWT-authorized for everyone else without duplicating the front door. If that seam has a tidy answer, I have not found it.
So what
Before you wire a tool into your agents, decide who is allowed to call it, because that decision picks your authorizer and everything downstream follows. If the answer is “only me and my own workloads,” IAM with SigV4 is auth with no secrets and nothing to rotate — take it. The moment the answer becomes “and other people,” you are signing up for an identity provider and a token lifecycle, and that cost should be a deliberate choice, not something you discover after you have already shipped the tool with the wrong gate in front of it.
Part of a series working through Amazon Bedrock AgentCore by building on it. Start with The AgentCore Map for the full picture. This post covered who may reach a tool; the next one, Who May Call What, covers what a caller may do once the token is valid — per-user authorization with Cedar. See also Two Things I Almost Called AgentCore Gaps.