I implemented IAM authentication from AgentCore Runtime to AgentCore Gateway using MCP Proxy for AWS

I implemented IAM authentication from AgentCore Runtime to AgentCore Gateway using MCP Proxy for AWS

2026.01.18

This page has been translated by machine translation. View original

Introduction

Hello, I'm Jinno from the Consulting Department, who loves La Mu supermarket (@yjinno).

How do you handle Inbound Auth authentication when calling a Gateway from Amazon Bedrock AgentCore Runtime?

When AgentCore was in Preview, I thought M2M authentication was the way to go,
but now that Gateway also supports IAM authentication, I wondered if IAM authentication might be simpler in some cases... which prompted me to write this article.

Prerequisites

Environment

I used the following versions for this implementation:

Item Version
Terraform >= 1.5.0
AWS Provider >= 6.25.0
Python 3.12
bedrock-agentcore >= 1.2.0
strands-agents >= 1.22.0
mcp-proxy-for-aws >= 1.1.5

Architecture Overview

Here's the architecture we'll be building:
Gateway calls will use IAM authentication, and Runtime calls will also use IAM authentication for simplicity.

CleanShot 2026-01-18 at 15.04.56@2x

For the complete Terraform code, please refer to the repository below:

https://github.com/yuu551/runtime-to-gateway-for-iam

Terminology

Since AgentCore has two layers of authentication for Runtime and Gateway, let's clarify the terminology first:

  • Runtime Inbound Auth
    • Authentication (IAM or JWT) when clients (people/applications) call AgentCore Runtime
  • Gateway Inbound Auth
    • Authentication (IAM (SigV4) or JWT) when Runtime calls AgentCore Gateway
  • Gateway Outbound Auth
    • Authentication (SigV4, etc.) when Gateway calls targets (e.g., Lambda/external API)

The main focus of this article is Runtime → Gateway calls (= Gateway Inbound Auth).
Runtime Inbound Auth is set to IAM to simplify verification.

Authentication Methods for Runtime→Gateway

M2M Authentication

Initially, I was considering M2M (Machine-to-Machine) authentication for Runtime→Gateway.

However, using M2M authentication requires:

  • Setting up a Cognito User Pool
  • Creating a Machine-to-Machine application client
  • Managing client secrets
  • Implementing token acquisition and refresh

Creating Cognito and acquiring tokens takes a bit more effort.
Of course, token acquisition can be simplified by utilizing the SDK.

import asyncio
from bedrock_agentcore.identity.auth import requires_access_token, requires_api_key

@requires_access_token(
    provider_name= "sample-provider",
    scopes= [],
    auth_flow= 'M2M',
)
async def need_token_2LO_async(*, access_token: str):

IAM Authentication

On the other hand, IAM authentication offers these advantages:

  • No Cognito needed
  • No secret management required (completed with IAM roles)
  • Just add bedrock-agentcore:InvokeGateway permission to Runtime's IAM role
  • SigV4 signatures automated with mcp-proxy-for-aws

Since AgentCore Runtime already has an IAM role, it's nice that we just need to add permissions to the Gateway without building additional authentication infrastructure.

The Gateway configuration is also simple - just specify AWS_IAM for the authorizer_type:

gateway.tf
resource "aws_bedrockagentcore_gateway" "main" {
  name     = "${var.project_name}-gateway"
  role_arn = aws_iam_role.gateway.arn

  protocol_type = "MCP"

  # Inbound authentication: Specify IAM authentication
  authorizer_type = "AWS_IAM"

  # ...omitted
}

For the Runtime side, add the bedrock-agentcore:InvokeGateway permission:

runtime.tf
resource "aws_iam_role_policy" "agent_runtime_gateway" {
  name = "${var.project_name}-agent-runtime-gateway-policy"
  role = aws_iam_role.agent_runtime.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "InvokeGateway"
        Effect = "Allow"
        Action = [
          "bedrock-agentcore:InvokeGateway"
        ]
        Resource = aws_bedrockagentcore_gateway.main.gateway_arn
      }
    ]
  })
}

While MCP clients normally expect token authentication when connecting to a Gateway,
AgentCore Gateway requires IAM (SigV4) authentication. You might wonder about signature processing, but fortunately, the useful MCP Proxy for AWS library bridges this gap!
We'll look at the detailed implementation later.

https://github.com/aws/mcp-proxy-for-aws

When to Use IAM vs M2M Authentication

So far I've explained the advantages of IAM authentication, but you might wonder when to use M2M authentication.

If you have multiple agents and need fine-grained authorization control at the tool level for each agent, M2M authentication might be worth considering. Let's look at the differences in detail.

IAM Authentication Constraints

IAM authentication allows access control at the Gateway level.

{
  "Action": "bedrock-agentcore:InvokeGateway",
  "Resource": "arn:aws:bedrock-agentcore:us-east-1:123456789012:gateway/my-gateway"
}

You can't control access at the tool level, like allowing a Runtime to use only specific tools within a Gateway.
So if you need different agents to execute different sets of tools, you'll likely need to separate Gateways.
Something like this:

CleanShot 2026-01-18 at 15.11.54@2x

If you're using very few tools, you might not need a Gateway at all, or if you only have general-purpose tools that all agents can use, a single Gateway might be sufficient.

What M2M Authentication + Interceptor/Policy Can Do

On the other hand, combining M2M JWT authentication with Gateway Interceptors/Cedar Policies enables more granular authorization control.
Consider a scenario with multiple AI agents where you want to restrict which tools each agent can use:

CleanShot 2026-01-18 at 20.07.48@2x

Such control can be achieved by verifying JWT scopes or attributes through Interceptors or Cedar Policies.

Choosing an Authentication Method

Pattern Use Case Tool-level Authorization
IAM Authentication Simple setup, all tools accessible Not possible
M2M JWT + Interceptor/Policy Multi-agent, tool-level authorization needed Possible

I believe IAM authentication is sufficient for simple setups with a single agent.
If you have multiple agents and need to grant each different access permissions to tools within a single Gateway, consider M2M authentication.

I'd like to explore and introduce methods for separating permissions per agent in a future article.

Notes on M2M Authentication

There are some considerations when adopting custom attributes with M2M authentication:

https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html

If you want to control authorization based on user permissions, you'll need to implement user delegation-based permission control. This discussion is limited to cases where the authorization subject is the agent.

Implementation with Strands Agents

Agent Implementation

Let's look at the implementation code.
This is a simple agent that connects to the Gateway and calls tools.

agent/agent.py
import os
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from mcp_proxy_for_aws.client import aws_iam_streamablehttp_client

GATEWAY_URL = os.getenv("GATEWAY_URL", "")
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
MODEL_ID = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-haiku-4-5-20251001-v1:0")

app = BedrockAgentCoreApp()
model = BedrockModel(model_id=MODEL_ID, region_name=AWS_REGION)

@app.entrypoint
def invoke(payload: dict) -> dict:
    message = payload.get("prompt") or payload.get("message", "")
    if not message:
        return {"error": "No message", "status": "error"}

    if GATEWAY_URL:
        # Create MCP client with SigV4 signature using mcp-proxy-for-aws
        mcp_factory = lambda: aws_iam_streamablehttp_client(
            endpoint=GATEWAY_URL,
            aws_region=AWS_REGION,
            aws_service="bedrock-agentcore"
        )
        mcp_client = MCPClient(mcp_factory)
        agent = Agent(model=model, tools=[mcp_client])
        result = agent(message)
    else:
        agent = Agent(model=model)
        result = agent(message)

    return {"response": str(result.message), "status": "success"}

if __name__ == "__main__":
    app.run()

The key point is the core part for connecting to the Gateway with IAM authentication using aws_iam_streamablehttp_client:

mcp_factory = lambda: aws_iam_streamablehttp_client(
    endpoint=GATEWAY_URL,
    aws_region=AWS_REGION,
    aws_service="bedrock-agentcore"
)
Parameter Value Description
endpoint Gateway URL AgentCore Gateway endpoint URL
aws_region us-east-1 etc. AWS Region
aws_service bedrock-agentcore Service name used for SigV4 signature

By specifying bedrock-agentcore as the aws_service, the correct SigV4 signature is added to Gateway requests. Nice and simple to use!

The rest is nearly identical to a standard MCP Client implementation.
The client executes list_tools_sync to synchronize with Gateway tools.

mcp_factory = lambda: aws_iam_streamablehttp_client(
            endpoint=GATEWAY_URL,
            aws_region=AWS_REGION,
            aws_service="bedrock-agentcore"
        )
with MCPClient(mcp_factory) as mcp:
    agent = Agent(model=model, tools=mcp.list_tools_sync())
    result = agent(message)

Trying It Out

Let's deploy this configuration using Terraform and verify that requests are properly sent to the Gateway!

Clone the Repository

First, clone the repository:

git clone https://github.com/yuu551/runtime-to-gateway-for-iam.git
cd runtime-to-gateway-for-iam

Initialize and Deploy Terraform

Initialize Terraform:

terraform init

Then, check the deployment plan:

terraform plan

After confirming that Gateway, Runtime, Lambda functions, and other resources will be created,
execute the deployment:

terraform apply

When prompted for confirmation, enter yes:

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

When deployment is complete, you'll see output like this:

Apply complete! Resources: 23 added, 0 changed, 0 destroyed.

Outputs:

gateway_endpoint = "https://xxxxxxxxxx.bedrock-agentcore.us-east-1.amazonaws.com"
runtime_endpoint = "https://yyyyyyyyyy.bedrock-agentcore.us-east-1.amazonaws.com"

Deployment complete!

Testing from the Console

Now that we've deployed, let's test by sending requests from the console.
We expect to receive responses from the Lambda function attached to the Gateway.

  • Addition tool
  • Multiplication tool
  • Greeting tool
  • Tool search tool made available by Gateway
{"prompt": "Tell me what I can do with tools"}

CleanShot 2026-01-18 at 08.40.47@2x

The tool list was returned as expected!
Now let's check if the addition tool works:

{"prompt": "What is 100 + 12?"}

CleanShot 2026-01-18 at 08.41.14@2x

The tool executed without issues! Looking at the logs confirms the tool execution:

{
  "message": {
    "tool_calls": [
      {
        "type": "function",
        "id": "tooluse_c9qOEPXPS2a6OR8UxN4tfg",
        "function": {
          "name": "iam-auth-demo-lambda-target___add_numbers",
          "arguments": {
            "a": 100,
            "b": 12
          }
        }
      }
    ],
    "role": "assistant"
  },
  "index": 0,
  "finish_reason": "tool_use"
}

This confirms that Runtime→Gateway works with IAM authentication!

Conclusion

Using mcp-proxy-for-aws makes it convenient to call Gateway from Runtime without worrying about SigV4 signatures.
For simple configurations, IAM authentication can be a viable option as it doesn't require Cognito or secret management.

On the other hand, for multi-agent environments where tool-level authorization is needed per agent, consider using M2M authentication + Interceptor/Policy combinations.

I hope this article was helpful!
Thank you for reading!!

Share this article

FacebookHatena blogX

Related articles