How MCP Authentication Works with Amazon Cognito and CIMD
Ever wondered how to add authentication to a remote MCP server using Amazon Cognito?
At first, the architecture appears straightforward:
1. Send the user to Cognito.
2. Receive an access token.
3. Validate the token at the MCP server.
That works when the application requesting access is already known to Cognito. But remote MCP servers introduce another question:
What happens when the MCP client is an IDE, desktop application, or agent that your Cognito user pool has never seen before?
OAuth authorization needs to know both who the user is and which application is requesting access. Cognito can authenticate the user, but it normally requires the application and its callback URLs to be registered in advance.
Client ID Metadata Documents, or CIMD, provide a standard way for an MCP client to identify itself without requiring a separate client registration at every authorization server.
The integration becomes easier to reason about once we separate two responsibilities:
- CIMD identifies the OAuth client requesting access.
- Cognito authenticates the human user granting access.
This article explains the complete MCP authorization flow, the point where Cognito’s native model stops, and the two practical architectures available.
The three roles in MCP authorization
For a remote MCP server using HTTP, the roles map directly to OAuth:
- The MCP client is the OAuth client. It could be an IDE, desktop application, or agent host.
- The MCP server is the protected resource, also called the resource server.
- The authorization server authenticates the user, evaluates the request, and issues an access token for the MCP server.
Amazon Cognito can act as the authorization server when every OAuth client is registered in its user pool. In an open CIMD architecture, Cognito instead acts as the upstream identity provider while a CIMD-aware authorization layer becomes the authorization server visible to MCP clients.
This distinction matters because the MCP server should not become a login system. Its responsibilities are narrower:
- Tell the MCP client where authorization happens.
- Receive an access token after authorization.
- Validate that the token was issued specifically for this MCP server and contains the required permissions.
A protected request begins like this:
MCP client -> MCP server: request without an access token
MCP server -> MCP client: 401 + protected-resource metadata location
MCP client -> authorization server: discover metadata and authorize
MCP client -> MCP server: retry with a bearer tokenBefore the authorization server can approve the request, it must identify the MCP client. This is where CIMD fits.
What is a Client ID Metadata Document?
A Client ID Metadata Document is a JSON document published by an OAuth client at an HTTPS URL. The URL itself becomes the client’s client_id.
For example:
{
"client_id": "https://client.example.com/oauth/mcp-client.json",
"client_name": "Example Desktop Client",
"client_uri": "https://client.example.com",
"redirect_uris": [
"http://127.0.0.1:49152/oauth/callback"
],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}This example describes a public desktop client:
- It uses authorization code rather than receiving a token in the browser redirect.
- It has no client secret because a desktop application cannot safely protect one.
- It uses a loopback callback on the user’s machine.
- It must use PKCE to protect the authorization-code exchange.
An authorization server announces CIMD support in its metadata:
{
"client_id_metadata_document_supported": true
}When the authorization server receives the metadata URL as client_id, it retrieves the document and validates it. At a minimum, it must check that:
- The metadata was retrieved over HTTPS
- The client_id inside the document exactly matches the document URL
- The requested redirect URI exactly matches an entry in redirect_uris
- The requested grant and response types are supported
The authorization server can cache valid metadata according to HTTP caching rules. It does not need to create a permanent client record for each new MCP integration.
The complete MCP authentication flow
The interactive flow has four stages.
1. The MCP server advertises its authorization server
The MCP client first calls the remote MCP endpoint without a token. The server returns HTTP 401 and points the client to its protected-resource metadata:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp",
scope="documents:read"The protected-resource metadata identifies the MCP server and the authorization server that can issue tokens for it:
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["documents:read", "documents:write"]
}The resource value is important. It gives the MCP server a stable identity that can later appear as the audience of the access token.
2. The client discovers CIMD support
The MCP client retrieves the authorization server metadata:
{
"issuer": "https://auth.example.com",
"authorization_endpoint": "https://auth.example.com/oauth2/authorize",
"token_endpoint": "https://auth.example.com/oauth2/token",
"jwks_uri": "https://auth.example.com/.well-known/jwks.json",
"client_id_metadata_document_supported": true,
"code_challenge_methods_supported": ["S256"]
}The client must verify that the issuer exactly matches the issuer used for discovery. This prevents an attacker-controlled discovery document from redirecting credentials to a different authorization server.
3. The authorization server identifies the client and authenticates the user
The MCP client sends an authorization request containing:
- the CIMD URL as client_id
- an exact redirect URI from the document
- a PKCE code challenge
- the canonical MCP server URI as resource
- the required scopes
- a random state value.
GET /oauth2/authorize?
response_type=code&
client_id=https%3A%2F%2Fclient.example.com%2Foauth%2Fmcp-client.json&
redirect_uri=http%3A%2F%2F127.0.0.1%3A49152%2Foauth%2Fcallback&
code_challenge=…&
code_challenge_method=S256&
resource=https%3A%2F%2Fmcp.example.com%2Fmcp&
scope=documents%3Aread&
state=…The authorization server retrieves the client document, verifies the request, and then authenticates the user. If Cognito is the upstream identity provider, the authorization server redirects the browser to Cognito for login, federation, or MFA.
4. The MCP client exchanges the code and calls the server
After authentication and consent, the authorization server returns an authorization code to the MCP client’s verified callback URI.
The client exchanges the code using its PKCE verifier and includes the same MCP resource value in the token request. It then sends the access token to the MCP server:
Authorization: Bearer eyJraWQiOiJ…The MCP server must validate more than the JWT signature. It should verify:
- iss: the expected authorization server issued the token
- aud: the token was issued for this MCP server
- exp and nbf: the token is currently valid
- scope: the token permits the requested operation; and
- the token type and any application-specific claims used by authorization policy.
An ID token is not a substitute for this access token. The ID token describes a user’s authentication to a client. The access token authorizes a request to the MCP resource.
Where Amazon Cognito fits
Amazon Cognito user pools provide:
- managed login and federation
- MFA and user-session management
- OAuth authorization and token endpoints
- authorization code with PKCE
- custom scopes and JWT access tokens
- signing keys through a JWKS endpoint
- resource binding for user access tokens.
But Cognito’s authorization endpoint requires client_id to be the ID of an app client that already exists in the user pool. The redirect URI must also be registered on that app client.
CIMD uses a different contract: client_id is an HTTPS URL, and the authorization server retrieves the callback URLs from the document.
That means publishing a CIMD document or MCP protected-resource metadata does not make Cognito CIMD-aware. A thin proxy that only changes the client_id is also unsafe because it can collapse many unrelated clients into one trusted Cognito application.
Get Vineet Jain’s stories in your inbox
Join Medium for free to get updates from this writer.
The right architecture depends on whether you already know every MCP client.
Option 1: Pre-register MCP clients in Cognito
Use this design when you control the MCP clients or maintain a small list of approved integrations.
Create a public Cognito app client for each trusted client shape. Configure:
- authorization code as the only browser grant
- no client secret for desktop, CLI, and other public clients
- a client requirement to use PKCE with S256
- only the callback URLs the client needs
- the minimum custom scopes required by the MCP server
- resource binding so the access token audience identifies the MCP server.
The MCP client receives the Cognito app client ID through configuration. Cognito authenticates the user and issues the access token. The MCP server trusts Cognito’s issuer and signing keys.
This is the simplest design for an internal platform. The tradeoff is onboarding: every new client or callback pattern requires a Cognito configuration change.
Pre-registration is a supported MCP client-registration mechanism, but Cognito is not an MCP-specific authorization server. Test the complete flow with every MCP host you intend to support, including its discovery behavior, resource parameter handling, callback format, and token refresh behavior.
Option 2: Put a CIMD-aware authorization layer in front of Cognito
Use this design when previously unknown but compatible MCP clients must connect without manual registration.
The authorization layer becomes the OAuth authorization server visible to MCP clients. Cognito becomes its upstream OpenID Connect identity provider.
The authorization layer owns the MCP-facing responsibilities:
- authorization-server metadata and the CIMD support flag
- safe retrieval and validation of client metadata documents
- redirect URI enforcement
- authorization code, PKCE, and state handling
- scope and consent policy
- resource or audience binding
- access-token issuance, refresh, revocation, and JWKS publication
- auditing, rate limiting, and abuse protection.
Cognito continues to own user authentication, federation, MFA, and the upstream login session.
Concrete Cognito configuration
The Cognito side remains conventional:
- Create or reuse a user pool and configure a user-pool domain.
- Create one confidential app client for the authorization layer.
- Enable the authorization-code grant and only the identity scopes the layer needs, commonly openid, profile, and email.
- Register one tightly controlled callback such as https://auth.example.com/callback/cognito.
.
- Store the Cognito app client secret in a managed secret store available only to the authorization layer.
- Configure the authorization layer to discover and validate Cognito as its upstream OIDC issuer.
The authorization layer then exposes a separate MCP-facing OAuth surface at https://auth.example.com. Its metadata advertises CIMD support. Its signing keys and JWKS are separate from Cognito’s. Its access tokens use the authorization layer as iss and the canonical MCP server URI as aud.
That separation is deliberate:
Cognito proves the upstream user identity to the authorization layer. The authorization layer proves access to the MCP server.
Do not map every CIMD client to one shared Cognito client and forward arbitrary callback URLs. That removes the redirect validation CIMD is meant to provide and can create open-redirect and confused-deputy vulnerabilities.
How to choose between the two designs
If three managed clients call one internal MCP server, pre-registration is probably easier to operate.
If unknown clients must connect across organizational boundaries, CIMD removes a real onboarding and lifecycle problem. Cognito can still provide the user identity layer, but a CIMD-aware authorization server must sit between the MCP client and Cognito.
Security checks that deserve explicit tests
At the MCP server
- Return HTTP 401, not an MCP tool error, when authentication is required.
- Publish RFC 9728 protected-resource metadata.
- Include the exact resource URI and the minimum required scope.
- Accept access tokens only in the Authorization header.
- Validate signature, issuer, audience, expiry, and scope on every request.
- Return 403 with insufficient_scope when the token is valid but under-scoped.
At the CIMD-aware authorization layer
- Require HTTPS client metadata URLs with a path.
- Compare the document’s client_id with its URL using exact string comparison.
- Match redirect URIs exactly.
- Do not automatically follow redirects while retrieving metadata.
- Block private, loopback, link-local, and otherwise disallowed fetch targets.
- Apply connection timeouts, response-size limits, JSON validation, and safe caching.
- Require authorization code with PKCE S256 for public clients.
- Bind access tokens to the MCP resource.
- Rotate signing keys and publish a stable JWKS endpoint.
At Cognito
- Use a dedicated user-pool app client for the authorization layer or each pre-registered MCP client.
- Keep callback URLs narrow and explicit.
- Configure only the OAuth flows, identity providers, and scopes that are needed.
- Prefer short-lived access tokens and define the refresh-token policy deliberately.
- Treat Cognito groups and custom claims as inputs to authorization, not as replacements for resource-level checks.
The architecture question to answer first
The central decision is not simply whether to enable authentication.
It is:
Who identifies the OAuth client, who authenticates the user, and who issues the access token that the MCP server will trust?
For controlled clients, Cognito can perform most of that flow directly through pre-registered app clients.
For previously unknown clients, CIMD identifies the client, Cognito authenticates the user, and a CIMD-aware authorization layer connects the two.
Once those responsibilities are explicit, MCP authentication becomes much easier to design and test.
Version note
This article reflects the MCP 2026–07–28 specification and the OAuth Client ID Metadata Document Internet-Draft available on October 12, 2026. CIMD remains an Internet-Draft, so implementations should track changes in both the IETF draft and MCP SDK support.
Sources
MCP 2026–07–28: Authorization
MCP 2026–07–28: Client Registration
MCP 2026–07–28: Authorization Server Discover
OAuth Client ID Metadata Document draft
Amazon Cognito authorization endpoint
Amazon Cognito token endpoint
Amazon Cognito resource binding






