Protect Composer MCP Access with Cognito OAuth

Composer can protect its MCP endpoint with Amazon Cognito access tokens while continuing to use the official WordPress MCP Adapter transport hooks. Composer does not replace the Adapter and does not need to create a shadow WordPress user for each external principal.

Authorization is the least-privilege intersection of:

  1. the Composer role mapped from the token's Cognito group;
  2. the maximum role allowed for the token's OAuth App Client;
  3. the Composer scopes actually granted in the access token;
  4. the active Site Contract, Blueprint, ownership, validation, and human approval rules.

Access modes

  • Open preserves the backward-compatible WordPress-authenticated path.
  • Protected validates Cognito when a bearer token is present and may retain the configured compatibility path.
  • Protected Required fails closed: no Composer tools are exposed until the identity provider, group mapping, and allowed OAuth client are complete, and every MCP request requires a valid bearer token.

Choose the mode deliberately in the Site Contract and MCP Access screen. Do not enable Protected Required before the OAuth round trip is tested.

Identity provider

Composer may resolve the AWS Region and User Pool ID from another WP Suite component. The MCP Access screen shows the effective Region, User Pool ID, issuer, JWKS URL, source, and readiness. If automatic resolution is absent or wrong, enable manual configuration and enter both values.

Example:

AWS Region: eu-central-1
User Pool ID: eu-central-1_AbCd1234
Issuer: https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_AbCd1234

These identifiers are not secrets. Client secrets and private keys never belong in Composer configuration.

Cognito domain and public App Client

OAuth requires a User Pool domain. Choose either a generated Cognito prefix domain or a custom domain with the required ACM certificate and DNS setup. Creating only an App Client is insufficient: without a domain there is no Hosted UI authorization endpoint.

Create a dedicated public App Client with:

  • authorization code grant;
  • PKCE with S256;
  • no client secret;
  • the exact HTTPS callback URL supplied by the MCP client;
  • openid plus the Composer resource-server scopes below;
  • refresh-token support when required by the client.

WP Suite orchestration v1.0.93 or newer can optionally create the Composer resource server and App Client for a new or existing User Pool. For a newly created pool the deployment wizard also requires a generated or custom Cognito domain. An externally managed existing pool may keep its separately managed domain. Copy the stack output AgentComposerMcpOAuthClientId into Composer's OAuth client mapping.

Callback matching is exact, including path and trailing-slash semantics.

Groups, roles, clients, and scopes

Map exact cognito:groups values to Composer roles. If a principal belongs to several mapped groups, the highest mapped role is the starting role. Then add every allowed App Client ID with a local label and a maximum role. The client ceiling can reduce, never increase, the group-derived role.

Supported OAuth scopes are:

openid
composer/read
composer/draft
composer/propose
composer/publish.request

Composer normalizes Cognito resource-server scope names to its internal composer.read, composer.draft, composer.propose, and composer.publish.request authorization names. Legacy Cognito deployments may also derive sc.group.<group> scopes. The signed cognito:groups claim remains the group source; scopes do not invent group membership.

In the currently tested legacy Cognito flow, requesting openid was necessary for the desired group/claim path. This does not mean that ChatGPT OIDC email or domain claiming must be enabled. Composer validates the signed access token, not the ID token, and does not publish an issuer-invalid proxied OIDC document.

Discovery must be public before authentication

The MCP protected-resource metadata, OAuth authorization-server metadata, and 401 WWW-Authenticate challenge must be available without an existing bearer token. Otherwise the client cannot learn how to authenticate.

Discovery must work through both:

  • direct HTTP at /wp-json/mcp/smartcloud-agent-composer; and
  • an OpenAI Secure MCP Tunnel HTTP profile targeting that same endpoint.

An old STDIO tunnel profile cannot carry the external HTTP challenge and bearer token. Back it up, migrate each site to its own HTTP endpoint, restart the site-specific service, and verify discovery before reconnecting the client.

Configure ChatGPT

  1. Create or edit the developer-mode app and select the correct site tunnel or direct server URL.
  2. Open Advanced OAuth settings and choose a user-defined public OAuth client.
  3. Copy ChatGPT's callback URL into the Cognito App Client exactly.
  4. Enter the App Client ID; leave the client secret empty and token endpoint authentication set to none.
  5. Request openid and the Composer scopes needed by the intended workflow.
  6. Complete the Cognito Hosted UI login and consent flow.
  7. Reconnect, inspect the action catalog, and run one read-only operation first.

401 versus 403

  • 401 Authentication required means the bearer token is missing, invalid, expired, has the wrong issuer/audience/client, or cannot be verified.
  • 403 Forbidden means authentication succeeded but the mapped group, client ceiling, scope, role, ownership, or Site Contract does not authorize the requested tool.

Do not fix either error by granting WordPress Administrator or disabling the contract. Inspect the redacted event details in Audit & portability.

Audit and token safety

Composer records access grants/denials, completed tool calls, the external principal identity, outcome, and redacted diagnostic codes in its tamper-evident audit chain. The admin UI shows browser-local time with an explicit zone and retains the exact UTC timestamp in event details.

Composer must never persist or display the full bearer token, authorization header, client secret, request arguments, or tool result. Temporary token logging is not an acceptance-test technique.

Production checklist

  • Resolved Region, User Pool, issuer, and JWKS URL are correct.
  • A Cognito generated/custom domain serves the Hosted UI.
  • The App Client is public, uses code plus PKCE S256, and has the exact callback.
  • Required groups are present in the access token and mapped exactly.
  • App Client ID, maximum role, and allowed Composer scopes are correct.
  • Unknown clients fail according to policy.
  • Discovery works without a bearer token over the selected HTTP route/tunnel.
  • Reader, Contributor, and Publisher boundaries are tested independently.
  • Publisher can request human approval but cannot directly publish.
  • Successful and denied calls appear in the audit log without sensitive data.

For transport setup, continue with Direct MCP Setup or Secure Tunnel and ChatGPT.