Which should authenticate your MCP server: OAuth 2.1 or API keys? Since the Model Context Protocol's November 2025 authorization spec, the answer for remote production servers is no longer a matter of taste — the spec mandates OAuth 2.1 with PKCE, RFC 8707 resource indicators, and protected-resource metadata. API keys still have a legitimate place, but it has shrunk to a well-defined corner: local, single-user development.
This post is a mechanism-level comparison of the two approaches — how each actually works, what breaks when credentials leak, and what the spec now requires. For the complete guide to securing MCP servers end to end (registration, tool scoping, rate limits, guardrails), see Securing MCP Servers: An Authentication Guide.
What the November 2025 MCP spec requires
As of July 2026, the current MCP authorization specification (finalized November 2025) makes three things mandatory for HTTP-based MCP servers that implement authorization. If you evaluated MCP auth before late 2025, your notes are stale.
OAuth 2.1 with PKCE, always. MCP clients must implement OAuth 2.1 and must use PKCE (Proof Key for Code Exchange) on authorization code flows — for confidential clients as well as public ones. PKCE binds the token request to the client that initiated the flow, so an intercepted authorization code is useless to an attacker who does not hold the code verifier.
RFC 8707 resource indicators. Clients must include a resource parameter identifying the specific MCP server the token is intended for. The authorization server mints a token audience-bound to that server, and the server must reject tokens not addressed to it. This is the spec's answer to token replay and confused-deputy attacks: a token minted for your CRM's MCP server cannot be presented to your payments MCP server, even if both trust the same authorization server.
Protected-resource metadata. MCP servers must publish OAuth protected-resource metadata that tells clients which authorization server(s) protect them. Discovery is standardized: a client connecting to an unfamiliar server can find the right authorization server, register, and obtain a correctly-scoped token without out-of-band configuration.
None of this applies to API keys, because API keys sit entirely outside the OAuth model — which is precisely their weakness at scale.
Why the authentication mechanism matters for MCP
MCP servers are a more dangerous thing to leave open than a typical REST API. They expose tools — functions that read data, mutate state, send messages, execute code. An unauthenticated or weakly authenticated MCP server hands every one of those capabilities to whoever finds the endpoint.
The clients are also unusual: they are machines — agents, orchestrators, IDEs — not humans with browsers. There is no cookie session, no consent screen at request time, no human to notice something odd. Identity verification, authorization boundaries, and audit attribution all have to work programmatically, at machine speed. For the full checklist of the security surface beyond authentication, see MCP Server Security: A Complete Checklist.
That framing is what makes the OAuth-vs-API-keys comparison substantive. The two mechanisms differ on exactly the properties that matter for non-human callers: credential lifetime, scoping, audience binding, revocation, and rotation.
API keys: the mechanics
An API key is a static bearer secret. The server (or its operator) generates a random string, stores a hash of it, and hands the plaintext to the client. The client presents it in a header on every request; the server hashes and compares. That is the entire protocol.
The strengths follow from the simplicity:
- Zero additional infrastructure. No authorization server, no token endpoint, no metadata documents. One header, one lookup.
- No extra round trips. The key is the credential and the token in one; there is no exchange step.
- Universal support. Every HTTP client ever written can send a static header.
The weaknesses follow from the same simplicity:
- Long-lived by default. A key is valid until someone deliberately revokes it. Keys leak — into shell history, environment dumps, log lines, git commits — and a leaked key keeps working for months if nobody notices.
- Unscoped. A key either works or it does not. There is no standard way to express "this caller may invoke
search_documentsbut notdelete_records." Any scoping is a bespoke, per-server implementation. - Not audience-bound. Nothing ties a key to a specific resource. If you reuse a key across servers (common in practice), one compromised server exposes all of them.
- Manual rotation. Rotating a key means coordinating every client that holds it, or running multiple valid keys through a transition window. In practice this friction means keys do not get rotated.
- Weak attribution. Keys get shared between people, agents, and CI jobs. When the audit log says "key
mcp_prod_3did X," you often cannot say which principal that actually was. This is the same anti-pattern as shared service accounts — see how to authenticate AI agents for why per-principal identity matters.
OAuth 2.1: the mechanics
OAuth 2.1 separates identity from access. The client registers once with an authorization server and holds long-term credentials (a client ID and secret, or a private key). To call an MCP server, it exchanges those credentials for a short-lived access token — for machine-to-machine cases, via the client credentials grant; for user-delegated cases, via the authorization code grant with PKCE.
Under the MCP spec, the token request includes the RFC 8707 resource parameter naming the target server. The authorization server mints a token with:
- A short lifetime — typically minutes to an hour. A stolen token expires on its own.
- Scopes — a standard, coarse-grained permission vocabulary the MCP server can enforce ("read-only tools," "admin tools").
- An audience — the specific MCP server the token is valid for. The server rejects tokens minted for anyone else.
Revocation and rotation change character entirely. Revoking access means telling one system — the authorization server — to stop issuing tokens to that client; outstanding tokens age out in minutes. Rotating client credentials does not disrupt in-flight work, because tokens, not the credentials themselves, flow on the wire.
The cost is real: you need an authorization server, clients must implement the token exchange and refresh logic, and clock skew, caching, and introspection add operational surface. That cost is why the spec's discovery machinery (protected-resource metadata) matters — it removes the per-server configuration burden that historically made OAuth painful to adopt.
Mechanism-by-mechanism comparison
| Property | API key | OAuth 2.1 (per MCP spec) |
|---|---|---|
| Credential on the wire | Static long-lived secret | Short-lived access token |
| Lifetime of a leak | Until manually revoked | Minutes (token TTL) |
| Permission scoping | None standard | OAuth scopes |
| Audience binding | None | RFC 8707 resource indicators |
| Replay across servers | Possible if key reused | Rejected (wrong audience) |
| Revocation | Per-key, manual, per-server | Central, at the authorization server |
| Rotation | Coordinated client updates | Transparent (tokens refresh) |
| Discovery | Out-of-band docs | Protected-resource metadata |
| Infrastructure required | Almost none | Authorization server |
| Spec status for remote MCP | Not covered | Mandated (Nov 2025) |
The pattern is consistent: API keys optimize for time-to-first-request; OAuth 2.1 optimizes for what happens after something goes wrong. Blast radius — in time, in scope, and in reach across servers — is the axis on which they genuinely differ.
The confused-deputy problem, concretely
The strongest argument for the OAuth-2.1-plus-resource-indicators model is an attack API keys cannot express a defense against. Suppose an agent holds a credential trusted by two MCP servers: a document search server and an email server. A prompt-injected instruction convinces the agent to call the email server with the credential it was given for search. With a shared API key, the email server has no way to know the credential was never meant for it — the key is valid, the request succeeds.
With RFC 8707, the token the agent holds carries an audience of the search server. The email server checks the audience, finds itself absent, and rejects the call — regardless of what the agent was tricked into attempting. Authorization survives even when the agent's judgment does not. This layered thinking — controls that hold when the model is manipulated — is the core of prompt injection defense as well.
When API keys are still the right call
The spec's OAuth requirements target HTTP-based remote servers. A local MCP server running over stdio transport on a developer's laptop, used by one person, holding no production credentials, gains little from an authorization server. API keys (or no auth at all, for purely local loopback servers) are defensible there.
The failure mode to avoid is drift: the local prototype becomes a shared staging server, then quietly becomes production, and the API key from week one is still the only lock on the door. Set the trigger explicitly — the moment a server is reachable by more than one principal or touches non-test data, it moves to OAuth 2.1.
How Praesidia handles this
Praesidia's connection model applies the OAuth pattern without requiring each team to stand up its own authorization infrastructure. Registered MCP servers receive client credentials that are exchanged for short-lived tokens via the OAuth 2.1 client credentials flow, and every connection is subject to controls that operate above authentication: content guardrails on what flows through the connection, and policies covering rate limits, geographic restrictions, and time-based access.
Authentication proves who is calling; those connection-level controls decide what the caller may do — which is where most real-world MCP incidents are actually stopped. For the step-by-step setup, see Securing MCP Servers: An Authentication Guide.
Recommendations
Use API keys only for local, single-user, stdio-based development. Move to OAuth 2.1 with PKCE and resource indicators before anything is remotely reachable — the November 2025 spec makes this the compliance baseline for MCP authorization, not a nice-to-have. Issue distinct credentials per client, keep token lifetimes short, and enforce audience checks on the server.
The cost of switching authentication methods grows with every client you add. Teams that adopt the spec-compliant pattern early avoid a migration that gets harder each month.
Common questions
Does the MCP spec ban API keys? No. The November 2025 authorization spec defines how OAuth 2.1 must work when an HTTP-based MCP server implements authorization; local stdio servers are outside its scope. But for remote servers, spec-compliant clients expect OAuth discovery and token flows — an API-key-only remote server is nonstandard and loses interoperability with conforming clients.
Why is PKCE required even for confidential clients? PKCE binds the authorization code to the client instance that requested it, defeating authorization-code interception and injection attacks. Requiring it universally — rather than only for public clients, as OAuth 2.0 did — removes a class of downgrade and misconfiguration errors. It costs one hash computation per flow.
What are RFC 8707 resource indicators in one sentence?
A resource parameter in the token request that names the specific server the token is for, so the authorization server can mint an audience-restricted token that no other server will accept.
Can I use the same API key for multiple MCP servers? You can, but you should not: a shared key means revoking access to one compromised server cuts off every server relying on that key, and a key stolen from one server works against all the others. This is exactly the replay problem resource indicators exist to solve.
How do I handle token refresh for long-running agent tasks? Request a fresh access token before each task, or centralize token management in a gateway or governance layer so individual agents never implement refresh logic themselves. Centralizing also means token-handling bugs get fixed once, not once per agent.