AI & MCP

OAuth for MCP servers: the flow, the checks and what stays with the client

A remote MCP server uses OAuth 2.1: a 401, metadata, consent, an opaque token. Here are the six endpoints, the required checks and our server's limit.

Key points

  • Revision 2025-06-18 requires OAuth 2.1, RFC 9728, RFC 8414 and the RFC 8707 resource parameter.
  • The server answers 401 with WWW-Authenticate and leaves metadata discovery to the client.
  • PKCE S256, an exact redirect_uri match and refresh rotation are the three checks that stop a stolen token.

A remote MCP server authenticates the client with OAuth 2.1, across six endpoints, and never asks the client for a password. The flow is the resource server one: 401, metadata, consent, and an opaque token bound to a single resource.

What the specification requires

Revision 2025-06-18 starts from OAuth 2.1 (draft -13) and imposes a subset of it. The MCP server must publish RFC 9728 Protected Resource Metadata, the authorization server must publish RFC 8414 metadata, and the client must send the RFC 8707 resource parameter in both the authorization request and the token request. PKCE is mandatory and the redirect_uri must match a registered value exactly, not a prefix.

The flow in five steps

  • The client calls /mcp without a token and gets 401 with a WWW-Authenticate header pointing at the resource metadata.
  • It reads /.well-known/oauth-protected-resource and finds the authorization server.
  • It reads /.well-known/oauth-authorization-server and, if needed, registers the client with POST /oauth/register.
  • It opens the browser on /oauth/authorize with code_challenge and resource; the user approves and comes back to the client with ?code=.
  • It exchanges the code on /oauth/token with code_verifier and receives an access token and a refresh token.

Registering the client

curl -s -X POST https://ui.eneawork.it/oauth/register \
  -H "Content-Type: application/json" \
  -d '{"client_name":"Claude Code","redirect_uris":["http://localhost:3000/callback"]}'

The response carries a client_id and token_endpoint_auth_method: none. There is no client secret, and that is a decision: an MCP client is an editor or a desktop app, and a secret inside a binary is read with strings. RFC 7591 dynamic registration is recommended, not mandatory: a server without it must hand out a client id another way.

The six endpoints

EndpointMethodWhat it returns
/.well-known/oauth-protected-resourceGETresource, authorization_servers, scopes_supported
/.well-known/oauth-authorization-serverGETissuer, authorization_endpoint, token_endpoint, registration_endpoint
/oauth/registerPOSTa public client_id and the registered redirect_uris
/oauth/authorizeGET, POSTthe consent page, then a redirect with ?code=
/oauth/tokenPOSTaccess_token, refresh_token, expires_in
/mcpPOST401 with WWW-Authenticate until a valid token arrives

Where our server draws the line

The token is opaque and stored as a SHA-256 hash, then resolved to an account. It carries no RFC 8707 audience claim: with a single public resource the difference is nil, with two resources the audience check would be required. The scope is one, mcp: there is no way to grant catalogue reads while denying the install command.

The limitation

The flow protects the transport, not the content. A tool can return text that contains instructions, and a client that follows them turns the result into prompt injection. The specification says as much: results stay untrusted data, and user consent does not make them safe.

Where to continue

Building the server is covered in building an MCP server, tool design in designing an MCP tool. For the client side read securing a coding agent, and for real login screens the review of authentication blocks. The catalogue behind the token is 111,985 components and 1,556 blocks; a record with no licence declared upstream is minhxthanh login form.

Frequently asked questions

Do I need OAuth for a local MCP server?

No. The specification says a stdio server must not follow the OAuth flow and must read credentials from the environment. OAuth is for the HTTP transport, where the client is remote.

Why does the code last only 60 seconds?

Because an intercepted code is worth a token. Sixty seconds are enough for the browser to return to the client and short enough to make a code copied from a log useless. The value is in backend/mcp-oauth.js, checked on 1 November 2026.

What happens if a refresh token is stolen?

The refresh token rotates on every use and the old row is deleted. Whichever copy is used first wins, the second gets invalid_grant. The user sees a broken connection and revokes the client from the account page.

Must the client send the token on every request?

Yes. OAuth 2.1 requires it on every HTTP request, even inside the same MCP session. The token never goes in the query string, because it would end up in proxy logs.

How many scopes does your server expose?

One, mcp. It covers search, metadata reads and the install commands your plan allows. There is no separate write scope because the server is read-only.