Inicio - Documentación - POM AI - 09 MCP - Start here - `/pom-ai-mcp/oauth/token`: OAuth token exchange

/pom-ai-mcp/oauth/token: OAuth token exchange

Where an authorization code becomes an access token, and where expired tokens are renewed. The client handles it; you rarely see it unless something fails.

https://example.com/pom-ai-mcp/oauth/token

POST only. Anything else returns 405.

Two grant types

authorization_code exchanges a fresh code for tokens. The client sends the code, its client ID, the redirect URI it used, the resource, and the PKCE code_verifier.

refresh_token renews an expiring access token. The client sends the refresh token, its client ID and the resource.

Anything else returns unsupported_grant_type.

What comes back

An access token, a refresh token, token_type: Bearer and expires_in.

Token Lifetime
Access token 1 hour
Refresh token 30 days

A client refreshes silently, so a working connection keeps working for up to thirty days of activity. After that, authorization is repeated.

What is checked

The exchange is strict, and each check corresponds to a failure you might see.

The resource must match. Compared against this site's MCP endpoint by scheme, host, port and path. A mismatch returns invalid_resource. This is the single most common OAuth failure, and it almost always means the client's configured URL differs from the endpoint — a www variant, a trailing path, or the wrong site of a multisite.

The client must exist. Unknown client IDs return invalid_client. After a Reset OAuth, previously registered clients produce this until they re-register.

The code must be valid. It must exist, be unused, be within its 10-minute life, belong to this client, match the redirect URI used, and match the resource. Otherwise invalid_grant.

PKCE must verify. The code_verifier must hash to the challenge sent at authorization. Otherwise invalid_grant.

A refresh token must be current. It must not be revoked or expired, must belong to this client, and must match the resource.

No client secret

The endpoint uses token_endpoint_auth_method: none. Clients are public and hold no secret, which is why PKCE carries the proof of possession.

A client trying to authenticate with a secret is not doing anything useful; the server does not expect one.

Rate limiting

5 requests per minute per IP, shared with the registration endpoint.

A client retrying a failing exchange in a loop will hit 429. When diagnosing, change one thing and try once rather than repeatedly — the rate limit will otherwise mask whether your fix worked.

Tokens are bound and hashed

Each token is bound to its client, the site's blog ID, the resource, the approving user and the approved scopes. Tokens are stored as hashes, never in plain text.

Consequences worth knowing:

  • a token from one site never works on another;
  • refreshing preserves the original scopes — it does not widen them;
  • revoked and expired tokens are pruned automatically.

Requirements

  • OAuth enabled, or the endpoint returns 404 with oauth_disabled.
  • Pretty permalinks, since the path is a rewrite rule.

The localized form /es/pom-ai-mcp/oauth/token is also accepted where POM Translate prefixes URLs.

In the log

Successful exchanges are recorded with the grant type, client ID and the user the token acts as. A run of authorization_code entries for the same client suggests refresh is failing and the client is re-authorizing more often than it should.

When it fails

Error Fix
invalid_resource Compare the client's URL against the MCP endpoint exactly
invalid_client Re-register; usually follows a Reset OAuth
invalid_grant The code expired, was reused, or the redirect URI differs — restart authorization
unsupported_grant_type The client attempted a flow this server does not offer
rate_limited Wait a minute; stop retrying in a loop
oauth_disabled Enable OAuth in the MCP settings

If the exchange succeeds but subsequent MCP calls still return 401, the token is fine and the Authorization header is being stripped in transit. The diagnostics in the MCP settings panel report whether that header arrives.