Chapter 18

Authorize Remote MCP as a Protected Resource Server

As soon as an MCP server is reachable over HTTP, it becomes a protected resource server, and the protocol defines OAuth-based authorization for it. Here it's important not to confuse two questions. Authentication answers who is calling the server. Authorization answers which resources and tools are allowed for that subject. For local stdio a trusted launcher and a constrained environment are usually enough; for remote - not.

The authorization flow runs through several steps, and each is needed.

MCP Client
  1. reads protected resource metadata
  2. discovers the authorization server
  3. gets an access token with the needed scope
  4. POST /mcp Authorization: Bearer …

MCP Server
  5. checks issuer, audience, expiry, signature
  6. maps subject/tenant/scopes to the capability
  7. applies object-level authorization in the handler

Rights must be checked not in one place but in layers - and the table shows where exactly.

Layer · Example check · Where to do it

  • Transport - TLS, Origin, body size; Gateway/server edge
  • Token - Issuer, audience, expiry, signature; Auth middleware
  • Capability - Scope release:read; MCP handler boundary
  • Object - The service belongs to the tenant; Domain repository/service
  • Action - Deploy requires approval; Workflow/transaction service

The point of the layers is that none replaces another. The transport layer checks TLS, Origin, and body size. The token layer - issuer, audience, expiry, signature. The capability layer - whether the subject has a scope like release:read. The object layer - whether a specific service belongs to their tenant. The action layer - whether a deploy requires a separate approval.

The naive design puts up one admin token that opens all tools to all hosts - and then compromising one client becomes compromising the whole capability surface. The professional one issues short-lived tokens to a specific MCP resource server, limits scopes, ties the call to a tenant, and re-checks every object.

A server must not accept a token that isn't for it. Checking the signature without checking the audience is insufficient. A confused deputy on a token is the real boundary between a convenient integration and a leak of rights. Don't forward an upstream token to a downstream API if its audience is different.

We've closed rights at the protocol level. But MCP has a separate class of threats - at the junction of untrusted text and real actions - and it's worth taking apart on its own.

Knowledge check

The server verified the token's signature. Is that enough to accept it?

Links