
OIDC Auth from Claude Code to an AgentCore Gateway
Wire Claude Code on your laptop to a remote Bedrock AgentCore Gateway using its native OAuth 2.1 + PKCE support — no mcp-remote shim required.
OIDC Auth from Claude Code to an AgentCore Gateway — No Proxy
When you put an Amazon Bedrock AgentCore Gateway in front of your MCP tools, the gateway stops being an open door. Every request has to carry a valid token, and the gateway checks it before a single tool call reaches your backend. That's exactly what you want for a remote MCP endpoint — but it raises a practical question: how does the Claude Code CLI running on your laptop actually get one of those tokens?
The short answer: Claude Code does the whole OAuth 2.1 dance for you. No
mcp-remote shim, no long npx command line, no hand-managed token file. You add the server, run /mcp , click through a browser login, and you're connected. This post walks through how the pieces fit — the OIDC flow, what the gateway is (and isn't) doing, and the config that ties it together.The three roles
OIDC auth here is a triangle. Getting the mental model right makes everything else obvious:
- The identity provider (IdP) — issues tokens. In my setup this is an Amazon Cognito user pool, but the gateway is IdP-agnostic: Okta, Auth0, Microsoft Entra ID, or any provider that exposes a
.well-known/openid-configurationendpoint works the same way.
- The AgentCore Gateway — the OAuth resource server . It validates tokens; it never issues them. Think of it as a bouncer with a copy of the IdP's signing keys, not the box office.
- Claude Code — the OAuth client . It runs the authorization-code + PKCE flow directly against the IdP, gets a JWT, and presents it to the gateway on every request.
The key insight that trips people up: the gateway is not your login page. It doesn't implement an authorization endpoint. Claude Code authenticates with Cognito, gets a JWT, and only then talks to the gateway — which checks the token's signature, expiration, issuer, and audience against the IdP's OIDC discovery document and either lets the request through or returns a 401.
The flow, step by step
Here's what happens the first time Claude Code connects to a gateway that requires auth:
Plain text
Claude Code (laptop) Cognito (IdP) AgentCore Gateway│ │ ││ 1. GET /mcp (no token) │ ││───────────────────────────┼───────────────────────────▶││ │ 401 + WWW-Authenticate│◀───────────────────────────────────────────────────────││ 2. discover auth server │ ││ (PRM → OIDC config) │ ││───────────────────────────▶│ ││ 3. open browser → login + PKCE challenge ││───────────────────────────▶│ ││ 4. redirect to localhost callback w/ auth code ││◀───────────────────────────│ ││ 5. exchange code + verifier for JWT ││───────────────────────────▶│ ││◀───────────────────────────│ access token (JWT) ││ 6. GET /mcp Authorization: Bearer <JWT> ││───────────────────────────┼───────────────────────────▶││ │ validate JWT vs JWKS ✔ ││◀───────────────────────────────────────────────────────││ tools available │
Two details worth calling out:
PKCE is what makes a public client safe. Claude Code is a public client — it has no client secret to protect (a CLI on your laptop can't keep one secret anyway). Instead it uses PKCE (RFC 7636) : it generates a one-time code verifier, sends a hash of it with the authorization request, and proves possession of the original when exchanging the code for a token. Even if someone intercepts the authorization code off the localhost redirect, it's useless without the verifier.
Discovery is automatic on modern gateways. The current MCP authorization spec uses Protected Resource Metadata (RFC 9728) : the gateway's 401 carries a
WWW-Authenticate header pointing at /.well-known/oauth-protected-resource , which in turn points Claude Code at the IdP's discovery document. Claude Code follows that chain and configures the OAuth flow with no manual endpoint wiring — you don't hand it the Cognito domain, the token URL, or the scopes. It finds them.The config: what you actually type
This is where Claude Code's native OAuth earns its keep. If you've set up remote MCP for Kiro, Cursor, or Claude Desktop, you've probably seen the
mcp-remote pattern — a npx proxy that brokers the OAuth flow and caches tokens in ~/.mcp-auth/ :JSON
{"mcpServers": {"gateway": {"command": "npx","args": ["-y", "mcp-remote","https://<gateway-id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp","8080","--static-oauth-client-info", "{\"client_id\":\"...\"}"]}}}
That works, but it's a stdio shim wrapping an HTTP server, plus a token cache you manage by hand. Claude Code speaks HTTP + OAuth natively, so the config collapses to this:
JSON
{"mcpServers": {"gateway": {"type": "http","url": "https://<gateway-id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp/","oauth": {"clientId": "<your-cognito-app-client-id>","callbackPort": 8080}}}}
Or skip the file entirely and let the CLI write it:
Bash
claude mcp add --transport http \--client-id <your-cognito-app-client-id> --callback-port 8080 \gateway https://<gateway-id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp/
A few things are doing quiet work here:
-
clientIdwith no secret. This is the public-client + PKCE path. Don't pass--client-secret.
-
callbackPort: 8080. Cognito requires redirect URIs to be pre-registered, so I pin the callback port to one I've allow-listed on the user pool client (localhost:8080/oauth/callback). If your IdP supports dynamic client registration, you can drop this and Claude Code picks a random port.
- The trailing slash on
/mcp/. AgentCore Gateway is picky about it; match whatever your gateway's endpoint expects.
Then, inside a session:
Plain text
/mcp
Pick the server, choose Authenticate, and your browser opens to the Cognito login page. Sign in, approve, and the status flips to ✔ connected. On a headless box (SSH, no browser), the newer
claude mcp login gateway prints the URL for manual paste-back.Where the token lives
One meaningful difference from the proxy approach: Claude Code does not use
~/.mcp-auth/ . It manages the token lifecycle itself, storing credentials in your OS secure storage (the system keychain on macOS) and refreshing them automatically when they expire. If a refresh fails, /mcp shows the server as needing authentication and you re-auth with one click. To wipe credentials, use Clear authentication in the /mcp menu.That means the troubleshooting steps written for
mcp-remote — rm -rf ~/.mcp-auth , --debug logs in that directory — don't apply. The OIDC flow is identical; the token plumbing is Claude Code's, not the proxy's.Inbound vs. outbound: don't conflate them
Everything above is inbound auth — laptop → gateway. There's a separate outbound leg — gateway → the downstream MCP targets it fronts — and it plays by different rules. Outbound auth to MCP-server targets requires OAuth with the
client_credentials grant (machine-to-machine, no user in the loop), which Cognito user pools don't offer. If your gateway needs to authenticate outbound to protected targets, that's where AgentCore Identity and its token vault, or an external IdP like Auth0/Okta, come in.For the laptop-to-gateway story in this post, none of that matters — inbound JWT validation against Cognito is the whole picture. But it's worth knowing the two legs are independent so you don't go hunting for a
client_credentials config you don't need.Why this pattern is nice
- The gateway is your single validation point. Every MCP client — Claude Code, Kiro, a CI job — presents a JWT, and the gateway checks it the same way. Your backend never sees an unauthenticated request.
- No secrets on the laptop. PKCE means there's nothing to leak from
.mcp.json. The file is safe to commit at project scope and share with a team.
- You reuse the IdP you already have. The gateway trusts whatever OIDC provider issues your users' tokens. If your org already runs Okta or Entra ID, the gateway validates those tokens without you standing up anything new.
If you want to see the backend side of this — how the gateway and its Cognito user pool get provisioned as part of an app — I wrote about that pattern in Amplify + Bedrock AgentCore, A Clean Pattern for GenAI Apps . And for what actually lives behind a gateway once tools are wired up, Give Your AI Agent an HPC Cluster Through MCP Tools walks through a real toolset.
Takeaways
- AgentCore Gateway is a resource server: it validates JWTs, it doesn't issue them. Your IdP (Cognito, Okta, Auth0, Entra ID) is the authorization server.
- Claude Code has native OAuth 2.1 + PKCE for remote HTTP MCP servers — a small
oauthblock in.mcp.json(or oneclaude mcp addcommand) replaces the wholemcp-remoteproxy setup.
- Tokens live in your OS keychain, managed and refreshed by Claude Code — not in
~/.mcp-auth/.
- Inbound (client → gateway) and outbound (gateway → targets) auth are separate problems. The laptop flow only needs inbound JWT validation.
Add the server, run
/mcp , log in once, and your local Claude Code is talking to a locked-down gateway with a token it manages for you. That's the whole trick.Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article