Announcing CIMD support for MCP Client registration
Learn more

MCP authorization guide: OAuth 2.1, resource metadata, CIMD and DCR

MCP authorization guide: the MCP server as an OAuth resource server, with CIMD recommended and DCR deprecated in the 2026-07-28 spec

To add OAuth 2.1 to a remote MCP server, you make the server an OAuth resource server and let a separate authorization server issue its tokens. The server answers unauthenticated requests with a 401 that points to its protected resource metadata, accepts only tokens issued for its own URL, and checks scopes per tool. Clients do the rest: they discover the authorization server, register, run the authorization code flow with PKCE, and send the token on every request. Everything below follows the current MCP authorization spec, version 2026-07-28.

TL;DR

  • The MCP server is a resource server. It publishes protected resource metadata (RFC 9728), validates the token audience (RFC 8707), and never passes the client's token to another API.
  • Clients register with the authorization server through a pre-registered client ID, a Client ID Metadata Document (CIMD), or Dynamic Client Registration (DCR). The 2026-07-28 spec deprecates DCR in favor of CIMD.
  • The same release adds two client checks worth knowing: validate the iss parameter in the authorization response (RFC 9207), and keep stored credentials per authorization server.

Our take

Since June 2025, when MCP servers became OAuth resource servers, the server-side rules have held steady: publish protected resource metadata, check the audience on every token, and check scopes per tool. What moved in November 2025 and July 2026 was client registration and discovery. DCR went from recommended (SHOULD) to optional (MAY) to deprecated, and CIMD arrived. That is a good reason to keep the authorization server outside the MCP server, as a service you can update without redeploying your tools.

What changed in the 2026-07-28 spec

If you built MCP auth against the 2025-06-18 or 2025-11-25 versions, these are the changes that need work. Most are listed in the changelog; the step-up scope rules and the new refresh token section are on the authorization page itself.

Change
What it means
Who has to act
DCR deprecated
Dynamic Client Registration stays allowed (MAY) for authorization servers that do not support CIMD, but CIMD is now the recommended path (client registration)
Authorization servers: add CIMD support. Clients: prefer CIMD when it is advertised
iss in authorization responses
Authorization servers SHOULD return iss (RFC 9207). Clients MUST check it against the issuer they recorded before redeeming the code
Both
Credentials keyed by issuer
Stored client credentials MUST be keyed by the authorization server's issuer and not reused with another one; CIMD client IDs are the exception, since they work across servers
Clients
application_type in DCR
Clients that still use DCR MUST send an application_type, and SHOULD use native for desktop, CLI and localhost apps and web for remote browser apps
Clients
Step-up scope rules
Servers SHOULD list every scope a call needs in one challenge; clients SHOULD request the union of earlier and newly challenged scopes
Both
Stateless protocol
Protocol sessions and the Mcp-Session-Id header are gone. The security guidance on session hijacking is replaced by guidance on state handles
Servers

Our walkthrough of the July 2026 spec update covers the rest of the release, including the stateless protocol and Tasks.

The authorization flow, step by step

Here is what happens the first time a client connects to a protected MCP server at https://mcp.example.com/mcp.

1. The server rejects the unauthenticated call. It returns 401 with a WWW-Authenticate header that points to its protected resource metadata, and it SHOULD name the scopes the client needs:

HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp", scope="files:read"

Since the 2025-11-25 version, the header is one of two options: a server can instead rely on clients finding the metadata at a well-known path. Clients MUST use the header URL when it is there; otherwise they try /.well-known/oauth-protected-resource/mcp first, then /.well-known/oauth-protected-resource at the root (discovery). Either path works for the server. Point the resource_metadata header at whichever one you serve.

2. The client reads the protected resource metadata. The document (RFC 9728) MUST list at least one authorization server:

{ "resource": "https://mcp.example.com/mcp", "authorization_servers": ["https://auth.example.com"], "scopes_supported": ["files:read"], "bearer_methods_supported": ["header"] }

3. The client discovers the authorization server. For an issuer with a path, such as https://auth.example.com/tenant1, clients try three URLs in order:

  1. https://auth.example.com/.well-known/oauth-authorization-server/tenant1 (RFC 8414)
  2. https://auth.example.com/.well-known/openid-configuration/tenant1
  3. https://auth.example.com/tenant1/.well-known/openid-configuration

The issuer in the document MUST equal the issuer used to build the URL, or the client MUST NOT use it. If the metadata does not list code_challenge_methods_supported, the client MUST stop, because it cannot confirm PKCE support.

4. The client registers, in this order of preference: a client ID it already has for this authorization server, a CIMD URL if the server advertises client_id_metadata_document_supported, DCR if it advertises a registration_endpoint, and finally asking the user for client details.

5. The client sends the user to the authorization endpoint with PKCE (S256) and the resource parameter set to the server's canonical URI. Values are shown unencoded for readability:

GET https://auth.example.com/oauth/authorize ?response_type=code &client_id=https://agent.example.com/oauth/client.json &redirect_uri=https://agent.example.com/callback &scope=files:read &state=8f2c... &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM &code_challenge_method=S256 &resource=https://mcp.example.com/mcp

The resource parameter (RFC 8707) MUST be in both the authorization request and the token request, whether or not the authorization server advertises support for it. It is what lets the authorization server issue a token for this MCP server and no other.

6. The client checks the response and redeems the code. If the response carries iss, it MUST match the issuer recorded in step 3, compared as an exact string. If the authorization server advertises authorization_response_iss_parameter_supported and the response has no iss, the client MUST reject it. Then the client exchanges the code at the token endpoint with the PKCE verifier and the same resource.

7. The client calls the server with the token in the Authorization: Bearer header on every request, never in the query string. The server validates it on every request and answers 401 if it is invalid or expired.

What the MCP server must do

These are the server-side rules that matter most. Two of them are easy to miss because a demo works without them: checking the token audience, and never passing a client's token upstream.

Rule
Requirement
Publish protected resource metadata
MUST, with authorization_servers. Serve it at the well-known path, the header, or both
Validate every token
MUST validate tokens per OAuth 2.1 Section 5.2, and MUST confirm the audience identifies this server, usually by its canonical URI. In practice that means checking signature, expiry, issuer and audience
Accept only your own tokens
MUST NOT accept tokens issued for other resources, and MUST NOT pass a client's token through to upstream APIs
Return 401 for bad tokens
MUST, for missing, invalid or expired tokens, with WWW-Authenticate
Return 403 for missing scopes
SHOULD, with error="insufficient_scope", the required scopes and the metadata URL
Ask for all scopes at once
SHOULD list every scope a call needs in one challenge, so the user is not asked again and again
Bind state handles to the user
MUST verify every request on its own, and SHOULD key any state handle to the user in the token

For a worked Node.js example, with token validation and per-route scopes, see securing an MCP server with OAuth 2.1.

Client registration: pre-registered, CIMD or DCR

MCP clients and servers usually have no prior relationship, which is why registration gets so much attention in the spec. There are three ways for a client to get a client ID.

Method
How it works
Status in 2026-07-28
Pre-registered
The client already has credentials for this authorization server, often from an admin or an earlier setup
Preferred when available
Client ID Metadata Document (CIMD)
The client ID is an HTTPS URL. The authorization server fetches the JSON document at that URL and checks it
Recommended; authorization servers SHOULD support it
Dynamic Client Registration (DCR, RFC 7591)
The client POSTs its metadata to a registration endpoint and gets a new client ID
Deprecated; MAY be supported for compatibility

A CIMD document needs only a few fields. Its client_id MUST match the URL it is served from exactly, and it MUST include client_name and redirect_uris:

{ "client_id": "https://agent.example.com/oauth/client.json", "client_name": "Example Agent", "redirect_uris": ["https://agent.example.com/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "token_endpoint_auth_method": "none" }

An authorization server that supports CIMD advertises client_id_metadata_document_supported: true, fetches the document when it sees a URL as a client ID, validates it, caches it according to HTTP cache headers, and shows the redirect URI's hostname on the consent screen. Fetching a URL chosen by the client is a server-side request forgery risk, so the current version of the IETF draft says not to follow redirects or fetch private addresses.

DCR also has an operational cost. Claude registers a new client on every fresh DCR connection, which can leave very large numbers of client records on your authorization server. For high-traffic servers, Anthropic recommends CIMD or Anthropic-held credentials instead. DCR is not going away immediately: under the feature lifecycle policy adopted in the same release, a deprecated feature stays in the spec for at least twelve months before it can be removed.

For the trade-offs by deployment type, see CIMD vs DCR. For a step-by-step CIMD walkthrough with an internal MCP server, see how MCP clients register using CIMD.

Scopes and step-up authorization

A client's first request should use the smallest scope set: the scope from the 401 challenge if there is one, otherwise the server's scopes_supported, which should be a minimal baseline. When a tool needs more, the server answers with 403 and the full set of scopes that call needs:

HTTP/1.1 403 Forbidden WWW-Authenticate: Bearer error="insufficient_scope", scope="files:read files:write", resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp"

A client acting for a user SHOULD then send the user through authorization again, asking for the union of the scopes it already has and the new ones, with a limit on retries. Clients do not all behave the same here. When you pin scopes in Claude Code's configuration, it requests those pinned scopes on a 403 rather than the scope the server named, so test step-up with each client you support.

Refresh tokens

The 2026-07-28 release adds a section on refresh tokens. Clients MUST keep them confidential and MUST NOT assume one will be issued. They SHOULD list refresh_token in their grant types, and MAY request offline_access if the authorization server advertises it; MCP servers SHOULD NOT advertise it themselves. Separately, the spec's security considerations require authorization servers to rotate refresh tokens for public clients.

That covers the token between client and MCP server. If your MCP server also calls third-party APIs for the user, as most MCP integrations do, it holds a second set of tokens with their own refresh rules; token refresh for third-party MCP servers covers that side.

Security mistakes to avoid

The MCP security best practices describe these in detail, with mitigations for each.

Mistake
What goes wrong
What the spec requires
Token passthrough
The server forwards the client's token to an upstream API, or accepts tokens issued for another service. Audit trails and access controls stop meaning anything
Explicitly forbidden. Validate the audience and use separate tokens for upstream calls
Confused deputy in an OAuth proxy
A proxy with one static upstream client ID lets a new client ride on consent another user already gave
Get consent per client before forwarding, show the client name, scopes and redirect URI, and match redirect URIs exactly
SSRF during discovery
Metadata URLs come from the server, so a malicious server can point a client at internal addresses
Clients SHOULD require HTTPS, block private and link-local ranges, and check each redirect
Trusting a state handle
A stolen handle is replayed by someone else
Verify every request with its token; never treat holding a handle as authentication

Which clients support what

Client support varies. Each row reflects the vendor's own documentation. Details checked October 2026.

Client
Registration
Notes
CIMD, DCR, or credentials held by Anthropic or entered by the customer
Uses CIMD when the authorization server advertises it and supports none as a token endpoint auth method; otherwise DCR. Uses only the first authorization server listed
DCR or CIMD, detected automatically, or pre-configured client credentials
--client-id, --client-secret and --callback-port for pre-registered clients
CIMD preferred, DCR and predefined clients supported
Requires S256 and sends resource
DCR, or a static client ID and secret
Static credentials go in the auth object in mcp.json, for providers without DCR or that require an allowlisted redirect URL
CIMD (VS Code hosts a client metadata document at vscode.dev/oauth/client-metadata.json), or a configured oauth.clientId
Set oauth.clientId to use a pre-registered client. The enterpriseManaged option (preview) signs in through the enterprise SSO issuer

With Scalekit

Scalekit acts as the authorization server for your MCP server, so your code only does the resource-server part. The MCP auth quickstart walks through it:

  1. Register the MCP server in the Scalekit dashboard and turn on CIMD and DCR for the clients you expect.
  2. Serve /.well-known/oauth-protected-resource with Scalekit listed as the authorization server.
  3. Validate each token's issuer, audience and expiry with the Node.js, Python, Go or Java SDK, and return 401 with WWW-Authenticate when it fails.
  4. Check scopes per tool and return 403 with insufficient_scope.
  5. Optionally let users sign in with enterprise SSO or social login, or connect your existing auth.

There are full examples for Express and FastAPI, and a separate guide for FastMCP. If you already run Scalekit MCP auth, the 2026-07-28 migration guide covers the move to a resource-bound issuer; during the transition, trust both the old and the new issuer in your token validation.

Further reading

FAQs

How do I add OAuth 2.1 to a remote MCP server?

Treat the server as an OAuth resource server. Publish protected resource metadata that names your authorization server, return 401 with a WWW-Authenticate header to unauthenticated requests, and validate every token's issuer, audience and expiry. Use a separate authorization server, such as Scalekit, to handle client registration, sign-in and token issuance.

Is DCR deprecated in MCP?

Yes. The 2026-07-28 MCP spec deprecates Dynamic Client Registration in favor of Client ID Metadata Documents (CIMD). Authorization servers may still support DCR for clients that need it, and clients fall back to it when CIMD is not advertised.

What is CIMD in MCP?

A Client ID Metadata Document is a way for a client to register without a registration endpoint. The client ID is an HTTPS URL, and the authorization server fetches the JSON document at that URL to learn the client's name and redirect URIs.

Why must MCP clients send the resource parameter?

The resource parameter (RFC 8707) tells the authorization server which MCP server the token is for. The server then issues a token with that audience, and the MCP server can reject tokens meant for anything else.

What should an MCP server return when a token lacks a scope?

HTTP 403 with a WWW-Authenticate header that has error="insufficient_scope", every scope the call needs, and the resource metadata URL. The client can then ask the user to authorize the extra scopes.

No items found.
On this page
Share this article

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.