MCP server authentication follows one rule set for remote servers and another for local ones. A server reached over HTTP acts as an OAuth 2.1 resource server: it publishes Protected Resource Metadata (RFC 9728) pointing to its authorization server, accepts only tokens issued for itself, and never passes a client's token through to another API. A local server started over stdio skips OAuth and reads its credentials from the environment. Clients carry the rest of the load, with PKCE and Resource Indicators (RFC 8707) on every request. This guide explains each requirement in the MCP authorization specification and the mistakes that most often break them.
When MCP server authentication applies
Authorization is optional in the MCP specification, but the scope is precise when you use it. Servers on an HTTP-based transport should follow the specification's authorization flow. Servers on stdio should not follow it and should retrieve credentials from the environment instead. Servers on any other transport must follow the established security practices for that protocol.
Optional does not mean safe to skip. A remote server without authentication is open to anyone who learns its address, and a local server that listens on HTTP without a token can be reached by other processes on the same machine. OWASP lists insufficient authentication and authorization as MCP07 in its MCP Top 10 for that reason.
The roles: resource server, client and authorization server
The specification maps MCP onto standard OAuth roles. A protected MCP server is an OAuth 2.1 resource server: it accepts and checks access tokens. An MCP client, such as a coding agent, is an OAuth 2.1 client acting for the user. The authorization server talks to the user and issues tokens; it can be part of your MCP server or a separate identity provider. For an internal server, the authorization server is usually your existing identity provider, which keeps sign-in, multi-factor authentication and offboarding in one place. Keeping those roles separate in your head matters, because most MCP server authentication mistakes come from a server acting as two of them at once without the checks each role needs.
Discovery: how clients find your authorization server
MCP servers must implement OAuth 2.0 Protected Resource Metadata, defined in RFC 9728. That document must include an authorization_servers field naming at least one authorization server. Your server has two ways to point clients at it, and must implement one: a resource_metadata URL in the WWW-Authenticate header of every 401 Unauthorized response, or the document served at a well-known path such as /.well-known/oauth-protected-resource.
The specification also asks servers to include a scope parameter in that header, naming the scopes the request needs. That one field is how you keep clients from requesting everything up front, which ties directly to the scope minimization guidance in the security best practices.
Validating tokens without passing them through
This is the part your server code owns entirely. The specification says MCP servers must validate access tokens as OAuth 2.1 describes, must check that each token was issued specifically for them as the intended audience, and must answer invalid or expired tokens with HTTP 401. Tokens arrive in the Authorization: Bearer header on every request and must never be accepted from the query string.
The most important line is the shortest: MCP servers must not accept or transit any other tokens. If your server calls an upstream API, it acts as a separate OAuth client to that API and uses a separate token issued by the upstream authorization server. Forwarding the client's token instead, called token passthrough, is explicitly forbidden. The security best practices page explains why: it bypasses rate limits and audience checks downstream, makes the audit trail show the wrong caller, and lets one stolen token reach every service that trusts it.
What clients must do: PKCE, resource indicators and registration
Some protections only work if the client does its part, and knowing them helps you test your server against real clients. MCP clients must use PKCE with the S256 method and must refuse to proceed if the authorization server's metadata does not advertise PKCE support. Clients must send the resource parameter from RFC 8707 in both the authorization and token requests, set to your server's canonical URI, so the token is bound to your server alone.
For registration, the 2025-11-25 specification lists three options in priority order: a pre-registered client ID when one exists, Client ID Metadata Documents, where the client ID is an HTTPS URL pointing to a JSON description of the client, and Dynamic Client Registration as a fallback. The 2026-07-28 revision deprecates Dynamic Client Registration in favor of Client ID Metadata Documents, keeping it only for backward compatibility. If you run the authorization server, support metadata documents, and protect the fetch: the specification warns that fetching a URL an unknown client supplies is a server-side request forgery risk.
API keys for internal and local servers
Not every server needs the full OAuth flow. For a stdio server, the specification's own guidance is to read credentials from the environment, so an API key in an environment variable is the expected pattern. For an internal HTTP server used by a handful of known clients, an API key can be reasonable if it is sent in a header over HTTPS, scoped to that one server, stored hashed on your side, and rotated on a schedule. Once a server is reachable by clients you do not control, move to the OAuth profile, so users never paste long-lived keys into config files.
Whichever you choose, give each key or client its own identity. One shared key for every developer means you cannot tell their calls apart in your logs, and you cannot revoke one person's access without breaking everyone else's.
Common MCP server authentication mistakes
The same errors show up again and again. Accepting any valid token from your identity provider without checking its audience, which lets tokens minted for another service work on yours. Forwarding the client's token to an upstream API. Publishing every scope in scopes_supported and granting them all on first login, which the security best practices list as a common mistake. Accepting tokens in URLs, where they end up in logs and browser history. Letting a proxy server with a static client ID skip per-client consent, the confused deputy problem the specification requires proxies to prevent. And treating a session or state handle as proof of identity, which the best practices forbid.
Each of these maps to the OWASP MCP Top 10, mostly MCP01 for token mismanagement, MCP02 for scope creep and MCP07 for insufficient authorization. They are also among the first things worth testing before you publish.
A short test plan covers most of them. Call your server with no token and confirm a 401 with a WWW-Authenticate header that points to your metadata. Call it with a valid token issued for a different resource and confirm it is rejected. Call a tool that needs more scope than the token carries and confirm a 403 with error="insufficient_scope", as the specification describes. Put a token in the query string and confirm it is ignored. Finally, check your own logs and confirm no token appears in them in full.
Next step: get MCP server authentication right, then check the rest
MCP server authentication is one part of running a server safely; transport, input validation, sandboxing and publishing are covered in our guide on how to secure MCP servers. To see which servers across your own organization accept unauthenticated calls or over-broad tokens, run the MCP security scanner.