OAuth 2.1 and Bearer Token Rotation for Remote MCP Servers

12 min read
Brett Calloway
astrologyMCPOAuthsecurityAI agents

How OAuth 2.1 with PKCE, refresh-token rotation, and protected resource metadata secure Remote MCP servers in 2026, plus when bearer keys still suffice.

TL;DR

  • The November 2025 MCP specification mandates OAuth 2.1 with PKCE when authorization is supported on HTTP-based Remote MCP servers, plus refresh-token rotation per OAuth 2.1 §4.3.1 and Protected Resource Metadata per RFC 9728. Treat the spec SHOULD as a MUST for any public-internet server.
  • Static bearer API keys still work for personal-tier Remote MCP where one developer holds one key.
  • Build this against the per-product Remote MCP endpoint at /mcp/{slug} in minutes.

About the author: Brett Calloway is a Developer Advocate and AI Integration Specialist with 12 years of experience in API development. He writes on building context-rich AI agents using Model Context Protocol, with three years focused on AI-native infrastructure for spiritual and wellness applications.

Remote MCP servers reached a security inflection point in late 2025. The November 2025 specification revision added a normative authorization profile based on OAuth 2.1, mandated PKCE on every authorization code flow, required Protected Resource Metadata per RFC 9728 for resource discovery, and called out refresh-token rotation as the public-client default. For developers shipping Remote MCP servers covering astrology, tarot, or numerology endpoints, that changes the day-one bar. Bearer-only auth still has a place, but only when the trust boundary is one developer and one key. This guide walks the full Streamable HTTP authorisation flow as a numbered procedure, anchors each piece to the MCP spec, and shows when bearer keys still earn their seat.

What the MCP spec requires for public Remote MCP servers

The MCP November 2025 authorization profile pulls together six RFCs into one normative checklist. A public Remote MCP server must act as an OAuth 2.1 resource server, advertise its authorization servers via Protected Resource Metadata at /.well-known/oauth-protected-resource, validate access tokens for audience binding per RFC 8707, and reject any token not issued for its canonical URI. The MCP client must implement PKCE with the S256 challenge method, parse WWW-Authenticate headers on 401, and resolve the authorization server through one of two well-known URIs.

Three discovery rules anchor this:

  1. The MCP server returns WWW-Authenticate: Bearer resource_metadata="<url>" on every unauthenticated request.
  2. The metadata document lists authorization_servers as an array, each with its own discovery URL.
  3. The client retries against /.well-known/oauth-authorization-server and, as a fallback, /.well-known/openid-configuration to resolve issuer metadata.

Authorization is OPTIONAL for stdio transports, where credentials come from the environment. For HTTP-based transports such as Streamable HTTP, the spec uses SHOULD to encourage conformance. Treat that SHOULD as a MUST if your server is reachable on the public internet.

Ready to ship a Remote MCP server with OAuth-ready surface? Astrology API exposes 130+ endpoints behind one key. See pricing.

How OAuth 2.1 with PKCE flows for a Remote MCP client

OAuth 2.1 with PKCE breaks into eleven concrete steps from first probe to refreshed token. The MCP spec layers RFC 8707 (resource indicator) and RFC 9728 (protected resource metadata) on top of the standard authorization code grant. Read the procedure once, then run it against /mcp/astrology or any of the other per-product Remote MCP endpoints.

  1. The client sends an unauthenticated request to the Remote MCP endpoint, for example POST /mcp/astrology.
  2. The server returns 401 Unauthorized with WWW-Authenticate: Bearer resource_metadata="https://roxyapi.com/.well-known/oauth-protected-resource" and an optional scope parameter.
  3. The client fetches the Protected Resource Metadata document and reads authorization_servers, resource, and scopes_supported.
  4. The client resolves the authorization server issuer metadata via /.well-known/oauth-authorization-server, falling back to /.well-known/openid-configuration if the first returns 404.
  5. The client confirms code_challenge_methods_supported includes S256. If absent, the client refuses to proceed (no PKCE, no flow).
  6. The client generates a 43 to 128 character code_verifier, derives code_challenge = BASE64URL(SHA256(code_verifier)), and stores both in session state.
  7. The client redirects the user to authorization_endpoint with response_type=code, code_challenge, code_challenge_method=S256, resource=https%3A%2F%2Froxyapi.com%2Fmcp%2Fastrology, state, and the requested scope.
  8. The user authenticates and consents. The authorization server redirects back with code and the original state.
  9. The client posts to token_endpoint with grant_type=authorization_code, code, code_verifier, and the same resource parameter from step 7.
  10. The token endpoint returns a short-lived access_token (typically 5 to 15 minutes), an optional refresh_token, and expires_in.
  11. The client retries the original Remote MCP request with Authorization: Bearer <access_token>. The server validates the token audience against its canonical URI per RFC 8707 §2 before processing.

Step 5 detail: PKCE capability discovery

OAuth 2.1 §4.1.1 mandates S256. MCP clients must check code_challenge_methods_supported in the authorization server metadata before sending the authorization request. If the field is absent, the client treats it as no PKCE support and aborts. OpenID Connect Discovery does not formally define this field, but MCP requires it anyway. Servers issuing OpenID metadata without it fail the MCP profile.

Step 7 detail: the resource parameter

RFC 8707 binds the eventual access token to a specific resource. The client must URL-encode the canonical MCP server URI in the resource query parameter on both the authorization request and the token request. Example: resource=https%3A%2F%2Froxyapi.com%2Fmcp%2Fastrology. The aud claim on the token then carries that URI, and the MCP server rejects any inbound token whose audience is not its own canonical URI.

Step 10 detail: short-lived access tokens

OAuth 2.1 §7.1 instructs authorization servers to issue short-lived access tokens to limit blast radius from leaks. The MCP spec inherits that recommendation. Five to fifteen minutes is the practical sweet spot. Anything longer raises the cost of a leak; anything shorter chews bandwidth on refresh-token churn.

Step 11 detail: audience validation on the server

The server verifies the aud claim, the iss claim, the signature, and the expiry. Per the spec, MCP servers must not accept or transit tokens issued for other resources. Token passthrough is explicitly forbidden because it triggers the confused-deputy class of vulnerabilities documented in RFC 6819 and the MCP security best practices doc.

When refresh-token rotation is mandatory and how often

Refresh-token rotation is mandatory for public clients per OAuth 2.1 §4.3.1, and the MCP November 2025 spec inherits that requirement verbatim. A public client is any client that cannot keep a secret: browser apps, native desktop tools, CLI agents, and most MCP clients running on a developer laptop. Each refresh exchange returns a new refresh token alongside the new access token, and the old refresh token is invalidated server-side on first reuse.

Refresh-token rotation is not optional for public clients

For public clients, the OAuth 2.1 authorization server MUST rotate refresh tokens. Reusing an old refresh token after rotation must trigger immediate invalidation of the entire token family. Clients that store refresh tokens in plaintext, log them, or send them over non-HTTPS channels are out of compliance. Confidential clients (server-to-server with strong client authentication) may opt out of rotation; nothing else may.

Three rules govern the timing:

RuleTriggerAction
Rotate on every refreshClient posts grant_type=refresh_tokenServer returns new refresh_token, invalidates old
Detect reuseOld refresh token presented twiceServer revokes the entire refresh-token family
Bind to clientToken issued to client AToken MUST NOT be redeemable by client B

Storage rules follow from this. Refresh tokens go in encrypted storage (OS keychain on desktop, server-side session for web, secure enclave on mobile). Never localStorage. Never plaintext on disk. Never query strings. The OAuth 2.1 §4.3 requirement is that refresh tokens stay confidential in transit and at rest. The MCP spec elevates this from SHOULD to operational MUST for any client distributed to users you do not control.

Why bearer API keys still work for personal-tier MCP

Bearer API keys remain a valid authentication method for personal-tier Remote MCP, where the key holder, the agent, and the data subject are the same person. The RoxyAPI Remote MCP endpoint at /mcp/{slug} accepts three credential locations checked in order: the X-API-Key header (preferred), Authorization: Bearer <key> (preferred for Claude Desktop and the Claude API), and api_key=<key> as a query string fallback. This is the single-developer shortcut, and it is fine for AI agents you build for yourself, internal tools, prototypes, and command-line workflows.

The shortcut breaks the moment you distribute. Three real triggers for moving to OAuth 2.1:

  1. The MCP server is exposed to multiple users, each with their own data context.
  2. The client is a third-party AI agent or hosted product that cannot embed a per-user secret.
  3. The client requires a defined consent screen, scoped permissions, or revocable per-session access.

For everything else, the bearer pattern keeps the cognitive load low. Drop your key into an Authorization: Bearer <key> header and the Remote MCP server treats every tools/call as billable through the standard middleware chain, the same as a REST request. The credential precedence is documented in the Remote MCP architecture page linked below, and tools/list stays public while tools/call requires authentication.

How to migrate from bearer to OAuth (developer section)

Migration from a bearer-only Remote MCP integration to an OAuth 2.1 flow is a five-step sequence. Read it before touching code. The biggest mistake is treating OAuth as a wrapper around the existing bearer call: the access token is structurally the same as a bearer token in transit, but the issuance, audience binding, and rotation rules are entirely different. Get those right and the wire format takes care of itself.

# Step 1: discover the protected resource metadata
curl -s -i https://roxyapi.com/mcp/astrology \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# Step 2: parse WWW-Authenticate from the 401, fetch the metadata document
curl -s https://roxyapi.com/.well-known/oauth-protected-resource

# Step 3: redeem the authorization code for an access token
curl -s -X POST https://auth.roxyapi.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=$AUTH_CODE" \
  -d "code_verifier=$CODE_VERIFIER" \
  -d "client_id=$CLIENT_ID" \
  -d "resource=https%3A%2F%2Froxyapi.com%2Fmcp%2Fastrology"

# Step 4: call the Remote MCP tool with the bearer access token
curl -s https://roxyapi.com/mcp/astrology \
  -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

The five steps that matter when you migrate: implement PKCE end to end (no bypass for "trusted" clients), bind every token request with the resource parameter, store refresh tokens in OS keychain or equivalent, rotate on every refresh and detect reuse, and validate aud on the server side. Skip any one and the spec considers you out of conformance. Read the Remote MCP documentation for transport details, then check the related guides at the Astrology MCP server overview and the multi-domain MCP guide for the wider picture. To list your server in the right places, see the 2026 MCP registry guide.

FAQ

What is a Remote MCP server?

A Remote MCP server is a single HTTPS URL that exposes API endpoints as agent-callable tools over the Streamable HTTP transport defined by the Model Context Protocol. Clients connect once and receive every tool definition, then call individual tools via JSON-RPC POST. RoxyAPI runs ten Remote MCP servers, one per product domain, at /mcp/{slug}.

Does the MCP November 2025 spec require OAuth 2.1?

The spec requires authorization servers to implement OAuth 2.1 when an MCP server supports authorization, and HTTP-based transports SHOULD conform to the authorization profile. PKCE with the S256 challenge method is mandatory for clients, and Protected Resource Metadata per RFC 9728 is mandatory for servers. Treat the SHOULD as a MUST for any public-internet Remote MCP server.

How often must refresh tokens rotate for a public MCP client?

Every refresh exchange. OAuth 2.1 §4.3.1 requires that public clients receive a new refresh token on every successful refresh, and that any reuse of an old refresh token revokes the entire token family. Confidential server-to-server clients with strong client authentication may opt out, but a public MCP client running on an end-user device cannot.

Can I still authenticate to a Remote MCP server with a bearer API key?

Yes, when the trust boundary is one developer and one key. RoxyAPI accepts API keys via X-API-Key, Authorization: Bearer, and api_key query parameter on every Remote MCP endpoint. Move to OAuth 2.1 with PKCE the moment a third-party client, multiple users, or scoped consent enters the picture.

What is Protected Resource Metadata and why does it matter?

Protected Resource Metadata is defined by RFC 9728 and discovered at /.well-known/oauth-protected-resource. The MCP server publishes a JSON document listing its authorization_servers, scopes_supported, and canonical resource identifier. Clients use this to find the correct authorization server and to compose the resource parameter on authorization and token requests so audience binding works.

Do I need Dynamic Client Registration for a Remote MCP server?

Only when unknown clients connect. OAuth 2.0 Dynamic Client Registration (RFC 7591) lets a brand-new client obtain credentials at the authorization server without a manual onboarding step. The MCP spec lists this as MAY, with Client ID Metadata Documents preferred for the typical no-prior-relationship case. For a single SDK or a registered partner integration, pre-registration is fine.

The MCP November 2025 specification raises the floor for public Remote MCP servers without raising the floor for personal-tier work. OAuth 2.1 with PKCE, refresh-token rotation, and Protected Resource Metadata are now the default for distribution-grade integrations; bearer keys remain perfectly serviceable for solo developer tooling. Either path runs against the same Astrology API surface, with pricing starting at $39 per month for 25K monthly requests.