The first time you stand up a remote MCP server, the authentication problem looks like a login problem. It isn't. The login is the easy part — OAuth has done logins for fifteen years. The hard part, the part the Model Context Protocol's authorization spec is almost entirely about, is proving that a token sitting in an HTTP header was minted for your server and not for someone else's. Get that one check wrong and your friendly little tool proxy becomes an open door wearing a lock.
Here's the shift that catches people. As of the 2025-11-25 revision, the spec stopped suggesting and started requiring: any MCP server reachable over the internet MUST implement OAuth 2.1 with PKCE, using the SHA-256 challenge method — the plain method is forbidden. The "ship a Bearer sk-... and call it a day" pattern is fine on localhost and non-compliant the moment your server has a public URL.
Your server is a resource server, not an identity provider
The single most useful thing to internalize: in MCP, your server does not issue tokens, store passwords, or run a login page. It is an OAuth resource server. Three roles, kept deliberately separate:
- The MCP client (the agent runtime) obtains a token.
- A separate authorization server — your IdP, Auth0, Keycloak, WorkOS, Cognito, whatever — authenticates the user and issues that token.
- Your MCP server does exactly one security job: validate the token on each request and decide what it permits.
People reach for the wrong mental model and try to build the login into the MCP server. Don't. The spec's entire architecture assumes the authorization server is somebody else, and it wires the two together through discovery rather than through code you write.
Discovery: how a client finds the right door (RFC 9728)
If your server doesn't run the IdP, how does a client know which IdP to send the user to? This is what RFC 9728, OAuth 2.0 Protected Resource Metadata, exists for, and the spec makes it non-optional: MCP servers MUST publish it; MCP clients MUST use it for authorization-server discovery.
Concretely, your server serves a JSON document at /.well-known/oauth-protected-resource. A client GETs it before authenticating and learns three things: which authorization server you trust (the issuer), what scopes you understand, and your own canonical resource identifier. That last field is the hinge for everything that follows.
The token isn't a key. It's a key stamped with the name of exactly one lock.
The whole point: the audience claim (RFC 8707)
Now the part that is genuinely non-obvious and genuinely the reason this spec is shaped the way it is.
A valid, unexpired, properly signed OAuth token is not enough to let someone in. Your server MUST also check that the token was issued for it — that its audience claim names your resource identifier, per RFC 8707, Resource Indicators for OAuth 2.0. When the client requests a token, it names the specific resource it intends to call; the authorization server binds that intent into the token's audience. Your job is to reject any token whose audience isn't you, no matter how otherwise legitimate it looks.
Miss this check and you've built the classic OAuth failure: a token leaked or borrowed from some other service sails straight through yours, because you only verified the signature, not the addressee.
The bug all of this is defending against: the confused deputy
Audience binding isn't bureaucratic ceremony. It's the fix for a specific, nasty class of attack — the confused deputy — and once you see it, the rest of the spec snaps into focus.
Picture an MCP server that wraps a third-party API. A token arrives from the client. The lazy implementation forwards that same token downstream to the third-party service. Now two things can go wrong at once: the downstream API may trust the token as if your server vouched for it, and an attacker who steals that token — or who lures a user through a crafted authorization request — gets access nobody consented to grant. One stolen credential, two unlocked systems.
The defense is a one-line rule with teeth: never pass the client's token through. For each downstream hop, your server mints or looks up a separate, audience-scoped token for that specific service. Tokens stop being skeleton keys and go back to being what they were supposed to be — keys cut for one lock.
What 2026-07-28 changes
The release candidate for the 2026-07-28 revision, published 2026-05-21, was billed by the maintainers as the largest change since launch, and the headline is about client identity. Dynamic Client Registration (RFC 7591) — where an agent registers itself with the authorization server on the fly — is deprecated, retained only for backward compatibility. In its place: Client ID Metadata Documents, where a client's ID is simply a URL the authorization server can fetch to learn who it is. Resource Indicators (RFC 8707) become mandatory on clients, not merely recommended.
That trade tells you what the real friction always was. Logins were solved. The thing agents lacked was a stable identity to register and a token they couldn't accidentally hand to the wrong service. The 2026-07-28 spec is the protocol admitting that out loud and fixing both.
If you're still deciding whether you even need a server for your tools, start with MCP vs Function Calling; when you're ready to build one, How to Build an MCP Server and FastMCP vs the Official SDK cover the scaffolding this auth layer bolts onto.



