deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

MCP OAuth 2.1 deep dive: metadata discovery, PKCE and audience-bound tokens

A dev.to walkthrough covers the MCP authorization parts tutorials skip: RFC 9728 metadata discovery, mandatory PKCE, RFC 8707 resource indicators and audience-checked token validation.

MCP OAuth 2.1 deep dive: metadata discovery, PKCE and audience-bound tokens

What the walkthrough covers

A practitioner walkthrough published on dev.to tackles the parts of MCP authorization that most quick-start guides leave out: how clients discover an authorization server, how issued tokens get bound to one specific MCP server, and what a resource server has to verify on every request. The author notes the post was written with AI assistance and reviewed for accuracy.

According to the post, the MCP authorization spec builds on OAuth 2.1 but adds constraints that implementations often skip. An MCP server typically acts as an OAuth resource server — it validates tokens but does not issue them. Clients are expected to locate the authorization server through protected resource metadata (RFC 9728) rather than hardcoded endpoints, tokens must be audience-restricted so a token minted for one server cannot be used against another sharing the same identity provider, and PKCE is mandatory because MCP clients are usually public clients such as CLI tools or desktop apps that cannot safeguard a client secret.

Discovery through protected resource metadata

The first implementation step is serving a JSON document at /.well-known/oauth-protected-resource. It declares the server's own URL in a resource field, lists the authorization servers clients should use, and advertises supported bearer methods. The author argues the resource field matters more than it appears: it is the value the server later compares against the token's aud claim. Omitting the document means every client needs manual configuration with the auth server's URL, an assumption that breaks the first time the organization rotates identity providers.

PKCE plus a resource indicator

On the client side, the flow generates a random code verifier, derives an S256 challenge from it, and includes both the challenge and a resource parameter defined by RFC 8707 in the authorization request. That resource parameter is the piece the author says most tutorials omit — it tells the authorization server which audience to embed in the issued token. Without it, a token a user approved for one MCP server could be accepted by a different, possibly malicious, server if the auth server does not scope tokens per resource.

Four checks before trusting a token

The post's central claim is that verifying a signature is not the same as validating a token. It lists four checks: signature or introspection, which catches forged tokens; an aud claim matching the server's own resource URL, which catches cross-server token replay; exp and nbf timestamps, which catch expired or not-yet-valid tokens; and scope sufficiency for the requested tool, which catches over-privileged calls such as a read-only token deleting records.

The author's observation from practice is that signature checks almost always get done because libraries handle them, while audience checks get skipped because they require the server to know its own identity as a resource — something that only works if the metadata step was done properly in the first place.

Sessions are not tokens

A valid token proves who is calling; it says nothing about the state of an MCP session. The post recommends keeping the two separate. Tokens should be short-lived, roughly 15 to 60 minutes, carry identity and scopes, and be validated on every request. Sessions are server-side state keyed by a session ID sent alongside the token, holding conversation and tool-call context, with a lifetime and idle timeout independent of the token. Refreshing an expired token should not reset an agent's in-progress multi-step workflow, while an idle session should require a fresh authorization handshake even if the token is still technically valid.

The failure mode seen in practice

The most common real-world break, according to the author, is not a missing signature check but audience confusion in multi-tenant setups where one authorization server issues tokens for several MCP servers in the same organization. A team verifies signatures and expiry, calls it done, and months later a token minted for an internal analytics MCP server also works against the customer-facing one because nobody checked aud. The resource indicator and audience check exist specifically to close that gap.

The post ends with a four-question audit: publish the well-known metadata document with a correct resource field, send the RFC 8707 resource parameter during authorization, check aud rather than only signature and expiry, and track sessions independently of token lifetime with their own idle timeout.

Why it matters

MCP servers increasingly front real data and real tool execution, often with several services trusting a single identity provider. The failure the post describes — a correctly signed token for one server being accepted by another — never shows up in a single-client demo and only surfaces in multi-tenant production. Mainstream OAuth libraries make signature verification easy but leave audience and scope enforcement to the application, so the checks highlighted here are precisely the ones teams tend to skip. Anyone building an MCP server that validates tokens should treat discovery, audience binding and session/token separation as baseline requirements rather than polish.

  • #mcp
  • #oauth
  • #authentication
  • #security
  • #api-design

Related posts