---
title: "MCP Authorization Explained: How MCP Uses OAuth 2.1 · Highflame"
description: "How MCP authorizes access: the OAuth 2.1 roles, authorization-server discovery, the PKCE flow, and audience-bound tokens, explained plainly with diagrams."
doc_version: "0.1.0"
last_updated: "2026-08-07T04:48:55.536Z"
canonical: "https://www.highflame.com/learn/mcp-authorization"
---

[Home](/index.md)·[Learn](/learn.md)·MCP Authorization

Reference

# MCP Authorization

MCP authorization is how a Model Context Protocol client gets permission to call a protected MCP server. For HTTP transports the spec builds on OAuth 2.1: the MCP server is a resource server, a separate authorization server issues audience-bound access tokens, and the client obtains them through a PKCE-protected authorization-code flow. Servers running over STDIO skip this and read credentials from the environment.

Justin AlbrethsenLast reviewed July 2026

Key takeaways

-   For HTTP transports, MCP authorization is OAuth 2.1: the MCP server is a resource server and a separate authorization server issues the tokens.
-   The client discovers the authorization server from the MCP server via RFC 9728 Protected Resource Metadata, starting from a 401 response.
-   Tokens are obtained through a PKCE-protected authorization-code flow and bound to one server with the RFC 8707 resource parameter.
-   The server must validate that a token was issued for it, and must never pass a client's token through to an upstream API.

MCP authorization is optional. A server can be wide open, and many local ones are. But the moment a Model Context Protocol server needs to know who is calling, the spec defines exactly one way to do it for HTTP transports: OAuth 2.1. (Servers that run over STDIO skip all of this and read credentials from the environment instead.) This guide walks the flow the [MCP authorization spec](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) defines, in plain terms.

## The three roles

OAuth splits the job across three parties, and MCP maps onto them directly:

-   The **MCP server** is the _resource server_. It holds the protected tools and data, and it checks the access token on every request.
-   The **MCP client** (Claude Code, Cursor, an agent) is the _OAuth client_. It obtains a token and attaches it to each call.
-   The **authorization server** interacts with the user and issues the access token. It can be hosted with the MCP server or be a completely separate service, such as your corporate identity provider.

The key idea: the MCP server never issues its own tokens or handles your password. It only _validates_ tokens that its authorization server minted specifically for it.

## Finding the authorization server

A client usually does not know, up front, who authorizes a given server. It finds out by asking. The first unauthenticated request comes back `401 Unauthorized` with a `WWW-Authenticate` header pointing at the server’s Protected Resource Metadata (RFC 9728). That metadata names the `authorization_servers`. The client then fetches the authorization server’s own metadata (RFC 8414, or OpenID Connect Discovery) to learn its endpoints and confirm it supports PKCE. MCP servers must publish protected-resource metadata, so this discovery step always has something to find.

FIG 1 · AUTHORIZATION SERVER DISCOVERY 401 → PROTECTED RESOURCE METADATA → AS METADATA

## Getting a token: the PKCE code flow

With the authorization server located, the client runs a standard OAuth 2.1 authorization-code flow, hardened with PKCE. It generates a one-time secret (the PKCE verifier and its `S256` challenge), opens a browser to the authorization server, and includes a `resource` parameter naming the exact MCP server it wants the token for. The user authorizes in the browser, the authorization server redirects back with a short-lived authorization code, and the client exchanges that code plus its PKCE verifier for an access token. From then on the client sends `Authorization: Bearer <token>` on every MCP request, and the server validates that the token was issued for it before doing anything.

FIG 2 · AUTHORIZATION CODE FLOW WITH PKCE SIGN IN → AUDIENCE-BOUND ACCESS TOKEN

## Identifying the client without pre-registration

One MCP-specific wrinkle: a client and a server usually have no prior relationship, so how does the server know which client is asking? MCP supports three answers, in priority order. A **pre-registered client ID** when the two already know each other. **Client ID Metadata Documents (CIMD)**, where the client’s `client_id` is an HTTPS URL pointing at its own metadata JSON, for the common no-relationship case. And **Dynamic Client Registration** (RFC 7591), kept mainly for backwards compatibility.

CIMD is the one worth understanding, because it is what lets a brand-new client connect to a server it has never met without anyone provisioning credentials first. The client’s `client_id` is just a URL like `https://app.example.com/client.json`, and that URL serves a small JSON document listing the client’s name and allowed redirect URIs. When the authorization server sees a URL-shaped `client_id`, it fetches that document, validates that the `client_id` inside matches the URL exactly and that the redirect URI is on the allowed list, and shows the user the client’s name on the consent screen. Because the server is fetching an attacker-suppliable URL, it must guard against SSRF, which is why authorization servers often allowlist trusted domains.

FIG 3 · CLIENT ID METADATA DOCUMENTS IDENTIFY A CLIENT WITH NO PRIOR RELATIONSHIP

## The rules that make it safe

Most of MCP authorization’s security lives in a handful of requirements:

-   **Audience-bound tokens.** The client must send the `resource` parameter (RFC 8707) naming the target server, and the server must reject any token not issued for it. This is what stops one server’s token being replayed at another.
-   **No token passthrough.** A server must not forward the client’s token to an upstream API. If it calls upstream, it acts as its own OAuth client with a separate token. Passthrough is the confused-deputy bug the spec explicitly forbids.
-   **PKCE is mandatory.** Clients must use `S256` and must refuse to proceed unless the authorization server advertises PKCE support, closing authorization-code interception.
-   **Exact redirect URIs and `state`.** Redirect URIs are matched exactly against pre-registered values, must be HTTPS or `localhost`, and clients verify `state`, closing open-redirect and phishing paths.
-   **Short-lived tokens.** Authorization servers should issue short-lived access tokens and must rotate refresh tokens for public clients, to limit the damage of a leaked one.

## Where MCP authorization stops

Notice what all of this decides: whether a token is valid, and which server it is good for. It does not decide whether a given tool call, with these arguments, right now, should be allowed. OAuth scopes are coarse; they gate which server a token works at, not the individual action. That runtime, per-call authorization is a layer the spec deliberately leaves open, the same gap [Enterprise Managed Authorization](/learn/enterprise-managed-authorization.md) leaves after it decides admission. MCP authorization is necessary and not sufficient: pair it with [server hardening](/learn/mcp-server-security.md) and per-action policy that catch what a valid token can still be steered into doing.

Highflame implements the spec at the door and closes that gap inside. Its open-source identity engine, ZeroID, can act as the MCP authorization server, minting audience-bound tokens; the gateway then validates token audience on every call, refuses token passthrough, brokers a separate upstream credential, and authorizes each individual tool call against one policy. See [build vs buy](/learn/mcp-gateway-build-vs-buy.md) for who should own that layer, and the [platform](/platform#gateway) for how it fits together.

## Frequently asked questions

Does MCP require OAuth?

No. Authorization is optional in MCP. But when a server over an HTTP transport needs it, the spec says implementations SHOULD follow this OAuth 2.1-based flow. Servers over STDIO do not use it and instead read credentials from the environment.

What are the roles in MCP authorization?

Three. The MCP server is the OAuth 2.1 resource server (it validates tokens). The MCP client is the OAuth client (it obtains and sends tokens). A separate authorization server interacts with the user and issues the access tokens; it may be co-hosted with the MCP server or be a different service such as a corporate IdP.

How does an MCP client find the authorization server?

Through RFC 9728 Protected Resource Metadata. The client's first unauthenticated request returns 401 with a WWW-Authenticate header pointing at the server's resource-metadata URL. That metadata lists the authorization\_servers, and the client then fetches the authorization server's own metadata (RFC 8414 or OpenID Connect Discovery) to learn its endpoints.

Why does MCP require PKCE?

PKCE prevents authorization-code interception and injection. Clients MUST use the S256 code challenge method and MUST verify, from the authorization server's metadata, that PKCE is supported before proceeding; if the server does not advertise it, the client must refuse to continue.

What is the resource parameter for?

It binds the token to one server. Under RFC 8707, the client MUST include a resource parameter naming the exact MCP server in both the authorization and token requests, and the server MUST validate that a token was issued for it. This is what stops a token from being replayed at a different service.

What is token passthrough, and is it allowed?

Token passthrough is a server forwarding the client's token to an upstream API. The spec forbids it. If an MCP server calls an upstream service it must act as its own OAuth client with a separate token, and it must reject any token whose audience is not itself. Passthrough creates the confused-deputy problem.

What is a Client ID Metadata Document (CIMD)?

CIMD is how an MCP client identifies itself when it has no prior relationship with the server. The client's client\_id is an HTTPS URL that hosts a JSON document describing the client (its name and allowed redirect URIs). The authorization server fetches that URL, checks that the client\_id inside matches the URL exactly, validates the redirect URI, and shows the client's name on the consent screen. It is MCP's preferred client-registration approach, ahead of dynamic client registration, and lets a brand-new client connect without pre-provisioned credentials.

Justin Albrethsen

Engineering and research on agent identity, runtime policy, and securing autonomous AI at Highflame.

▸ Related

-   [MCP Server Security (guide)](/learn/mcp-server-security.md)
-   [Enterprise Managed Authorization (guide)](/learn/enterprise-managed-authorization.md)
-   [MCP Gateway: Build vs Buy (guide)](/learn/mcp-gateway-build-vs-buy.md)
-   [MCP authorization spec (2025-11-25)](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)

See agent governance against your own agents.

[Book a demo](/contact.md) [Explore the platform](/platform.md)

## Sitemap

Full site map: [/sitemap.md](https://www.highflame.com/sitemap.md). Machine index: [/sitemap-index.xml](https://www.highflame.com/sitemap-index.xml).
