mech.app
Dev Tools

OAuth Code Flow for MCP Servers: How AgentCore Gateway Authenticates Agent Requests with User Identity Tokens

Deep dive into implementing OAuth 2.0 authorization code flow for MCP servers on AgentCore Gateway, showing how agent requests carry user identity tokens.

Source: aws.amazon.com
OAuth Code Flow for MCP Servers: How AgentCore Gateway Authenticates Agent Requests with User Identity Tokens

Most agent architectures treat authentication as a service-to-service problem. You give the agent a service account, it calls APIs, and you log the activity. But when agents act on behalf of users, you need user-scoped authorization. The agent must carry proof that Alice, not just “the agent service,” requested access to the CRM.

AWS just published a production guide for implementing OAuth 2.0 authorization code flow as an inbound auth mechanism for Model Context Protocol (MCP) servers hosted on AgentCore Gateway. This is not agent-to-agent communication. This is user identity tokens flowing through the agent request path, validated at the gateway, and forwarded to downstream MCP servers.

The Problem: Agent Requests Need User Identity

When a developer uses an agentic coding assistant like Kiro IDE to query an internal tool server, the request must prove two things:

  1. The agent client is authorized to connect.
  2. The request is made on behalf of a specific authenticated user.

Service credentials solve the first problem. OAuth code flow solves the second. The agent client obtains a user identity token from your organization’s IdP (Okta, Entra ID, Cognito), then includes that token in every MCP request. AgentCore Gateway validates the token before routing the request to the MCP server.

This architecture separates user authentication (handled by the IdP) from agent authorization (handled by the gateway). The MCP server never sees the user’s password or session cookie. It sees a validated identity token.

Architecture: Token Flow from IdP to MCP Server

Here’s the request path:

  1. User initiates action in Kiro IDE (or another MCP client).
  2. Client redirects user to the organization’s IdP for login.
  3. IdP issues authorization code after successful authentication.
  4. Client exchanges code for access token (standard OAuth code flow).
  5. Client sends MCP request to AgentCore Gateway with the access token in the Authorization header.
  6. Gateway validates token against the IdP’s public keys.
  7. Gateway forwards request to the MCP server with the validated user identity.

The gateway acts as both an OAuth client (to exchange the code for a token) and a resource server (to validate incoming tokens). The MCP server never talks to the IdP directly.

Configuration: Three Pieces

You configure three components:

1. Identity Provider (IdP)

Register AgentCore Gateway as an OAuth client application. You need:

  • Client ID and Client Secret (for token exchange).
  • Redirect URI pointing to the gateway’s callback endpoint.
  • Scopes defining what the token grants access to (e.g., openid, profile, email).

The IdP must support the authorization code flow. Most enterprise IdPs do.

2. AgentCore Gateway

Configure the gateway with:

  • OAuth Client ID and Client Secret (from the IdP).
  • Token Endpoint (where the gateway exchanges the code for a token).
  • JWKS URI (where the gateway fetches public keys to validate tokens).
  • Issuer (the expected iss claim in the token).

The gateway validates every incoming token by checking the signature, expiration, issuer, and audience claims.

3. MCP Client (Kiro IDE)

The client must:

  • Redirect the user to the IdP’s authorization endpoint when starting a session.
  • Handle the callback with the authorization code.
  • Exchange the code for an access token.
  • Include the token in the Authorization: Bearer <token> header for every MCP request.

The client is responsible for token refresh. If the token expires mid-conversation, the client must obtain a new token without breaking the agent loop.

Code Flow vs. Client Credentials

AspectAuthorization Code FlowClient Credentials Flow
User identityToken carries user claims (email, roles, groups)Token represents the service, not a user
Use caseAgent acting on behalf of a userService-to-service communication
Token lifetimeShort (minutes to hours), requires refreshLonger, or rotated on a schedule
RevocationUser logout or session terminationService account deactivation
Audit trailLogs show which user triggered the actionLogs show which service triggered the action

Code flow is heavier. You need a browser redirect, user interaction, and token refresh logic. But you get user-scoped permissions and a clear audit trail.

Failure Modes

Token Expiration During Multi-Turn Interaction

If the access token expires while the agent is mid-task, the next MCP request will fail with a 401. The client must:

  1. Detect the 401 response.
  2. Use the refresh token to obtain a new access token.
  3. Retry the failed request with the new token.

If the refresh token is also expired, the client must restart the OAuth flow (redirect the user to the IdP). This breaks the agent loop. You can mitigate this by:

  • Setting longer token lifetimes (if your security policy allows).
  • Proactively refreshing tokens before expiration.
  • Caching the refresh token securely and retrying automatically.

Gateway Misconfiguration

If the gateway’s JWKS URI is wrong or the issuer claim doesn’t match, every token validation will fail. Symptoms:

  • All requests return 401 or 403.
  • Gateway logs show “invalid token signature” or “issuer mismatch.”

Fix: Double-check the IdP’s .well-known/openid-configuration endpoint and ensure the gateway’s config matches.

Client Fails to Include Token

If the MCP client forgets to include the Authorization header, the gateway rejects the request immediately. This is a client bug, not a gateway issue. Ensure the client library or SDK is configured to attach the token to every request.

Token Refresh Without Breaking the Agent Loop

The client must handle token refresh transparently. Here’s a minimal example in Python using httpx:

import httpx
import time

class MCPClient:
    def __init__(self, gateway_url, token_endpoint, client_id, client_secret):
        self.gateway_url = gateway_url
        self.token_endpoint = token_endpoint
        self.client_id = client_id
        self.client_secret = client_secret
        self.access_token = None
        self.refresh_token = None
        self.token_expiry = 0

    def _refresh_access_token(self):
        response = httpx.post(
            self.token_endpoint,
            data={
                "grant_type": "refresh_token",
                "refresh_token": self.refresh_token,
                "client_id": self.client_id,
                "client_secret": self.client_secret,
            },
        )
        response.raise_for_status()
        token_data = response.json()
        self.access_token = token_data["access_token"]
        self.refresh_token = token_data.get("refresh_token", self.refresh_token)
        self.token_expiry = time.time() + token_data["expires_in"]

    def _ensure_valid_token(self):
        if time.time() >= self.token_expiry - 60:  # Refresh 1 min before expiry
            self._refresh_access_token()

    def call_mcp_server(self, method, params):
        self._ensure_valid_token()
        response = httpx.post(
            f"{self.gateway_url}/mcp",
            headers={"Authorization": f"Bearer {self.access_token}"},
            json={"method": method, "params": params},
        )
        if response.status_code == 401:
            self._refresh_access_token()
            response = httpx.post(
                f"{self.gateway_url}/mcp",
                headers={"Authorization": f"Bearer {self.access_token}"},
                json={"method": method, "params": params},
            )
        response.raise_for_status()
        return response.json()

This client checks token expiry before every request and retries once on 401. It does not handle the initial authorization code exchange (that requires a browser redirect).

Security Boundaries

The gateway enforces three boundaries:

  1. Token signature validation: Only tokens signed by the IdP’s private key are accepted.
  2. Issuer and audience checks: The token must come from the expected IdP and be intended for the gateway.
  3. Expiration enforcement: Expired tokens are rejected, even if the signature is valid.

The MCP server trusts the gateway to perform these checks. It does not re-validate the token. This means the gateway is a critical security component. If an attacker compromises the gateway, they can forge user identities.

Mitigations:

  • Run the gateway in a private subnet with no direct internet access.
  • Use AWS IAM roles to restrict who can modify the gateway’s configuration.
  • Enable CloudTrail logging for all gateway API calls.
  • Rotate the OAuth client secret regularly.

Observability

You need to log:

  • Token validation failures (signature mismatch, expired token, wrong issuer).
  • Token refresh attempts (success and failure).
  • User identity claims extracted from the token (for audit trails).

AgentCore Gateway integrates with CloudWatch Logs. You can create metric filters to alert on:

  • Spike in 401 responses (indicates widespread token expiration or misconfiguration).
  • Repeated token validation failures from the same client (indicates a compromised client or bug).

Deployment Shape

The gateway runs as a managed service on AWS. You configure it via the AWS Console, CLI, or CloudFormation. The MCP server can run anywhere (Lambda, ECS, EC2, on-premises) as long as the gateway can reach it over the network.

Typical deployment:

  • Gateway: Managed by AWS, no infrastructure to manage.
  • IdP: Okta, Entra ID, or Cognito (also managed).
  • MCP Server: ECS Fargate container in a private subnet, registered with the gateway via service discovery.
  • Client: Kiro IDE running on developer laptops, configured with the gateway URL and OAuth client credentials.

Technical Verdict

Use OAuth code flow with AgentCore Gateway when:

  • Agents act on behalf of users, and you need user-scoped permissions.
  • You already have an enterprise IdP and want to reuse it for agent authentication.
  • You need a clear audit trail showing which user triggered which agent action.
  • You can handle token refresh logic in the client without breaking the user experience.

Avoid it when:

  • Agents act autonomously with no user context (use client credentials or API keys instead).
  • Your IdP does not support the authorization code flow (some legacy systems only support SAML).
  • Token refresh adds unacceptable latency or complexity to the client.
  • You need offline agents that cannot redirect users to a browser for login.

This architecture is production-ready for enterprise environments where user identity matters. It is overkill for internal tools where service accounts are sufficient.