Authentication
Protected Resource Metadata
RFC 9728 resource server discovery for OAuth-protected APIs and MCP servers
Last verified 2026-09-24
Summary
Publish your resource server's metadata at /.well-known/oauth-protected-resource so that clients, agents, and external services can discover your authorization requirements at runtime. Clients use this metadata to validate against the correct JWKS and learn which you support. RFC 9728 does not standardize a DPoP-requirement field; if you require , advertise it separately (see the note below).
resource: your or MCP server's resource URIauthorization_servers: list of trusted identity providersscopes_supported: all scopes your API recognizesjwks_uri: public key endpoint for validationbearer_methods_supported: accepted transport methods- Cache with
Cache-Control: max-age=3600
When an MCP client, external agent, or service needs to call your API, it must know: which authorization servers to trust, which scopes you support, and where to validate JWTs. RFC 8414 Authorization Server Metadata defines the authorization server metadata endpoint. RFC 9728 Protected Resource Metadata defines the metadata that a resource server exposes about itself.
Publish this metadata at /.well-known/oauth-protected-resource to allow clients (including MCP) to discover your auth requirements and validate tokens correctly.
Metadata Document Structure
{
"resource": "https://api.example.com",
"authorization_servers": ["https://auth.example.com/"],
"scopes_supported": [
"invoices:read",
"invoices:write",
"customers:read",
"customers:write",
"payments:read",
"payments:write"
],
"jwks_uri": "https://auth.example.com/.well-known/jwks.json",
"bearer_methods_supported": ["header"],
"resource_signing_alg_values_supported": ["RS256", "ES256"],
"resource_name": "Example API"
}Key fields:
resource- your API's base URI or MCP endpoint resource identifierauthorization_servers- list of auth servers that can issue tokens you acceptscopes_supported- all scopes your API recognizesjwks_uri- where to fetch public keys for JWT validationbearer_methods_supported- accepted bearer token locations, usually["header"]resource_signing_alg_values_supported- algorithms used when the resource signs responses or metadataresource_name- human-readable resource name for client UI
Authorization-server capabilities such as grant_types_supported, response_types_supported, token endpoints, and token endpoint auth methods belong in the authorization server metadata document, not the protected resource metadata document.
Agent Registration Discovery
If your service supports agentic user registration through auth.md, keep the split explicit:
- Protected-resource metadata tells the agent which resource it is calling and which authorization servers protect it.
- Authorization server metadata advertises the optional
agent_authblock with identity, claim, and provider-event endpoints. Token revocation remains a top-level OAuth endpoint. auth.mdgives agents a readable walkthrough of the same flow, including supported registration methods, scopes, error recovery, and contact links.
A protected resource can point agents toward the registration path without registration details directly:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource"The authorization server metadata then advertises supported agent registration flows through its optional agent_auth block - see auth.md for the full field-by-field example and the current registration model. Treat this metadata as discovery only; authorization still depends on validating the issued bearer token on every request.
Next.js Implementation
// app/.well-known/oauth-protected-resource/route.ts
import { NextResponse } from "next/server";
export async function GET() {
const metadata = {
resource: "https://api.example.com",
authorization_servers: ["https://auth.example.com/"],
scopes_supported: [
"invoices:read",
"invoices:write",
"customers:read",
"customers:write",
"payments:read",
"payments:write",
],
jwks_uri: "https://auth.example.com/.well-known/jwks.json",
bearer_methods_supported: ["header"],
resource_signing_alg_values_supported: ["RS256", "ES256"],
resource_name: "Example API",
};
return NextResponse.json(metadata, {
headers: { "Cache-Control": "public, max-age=3600" },
});
}Serve it at https://api.example.com/.well-known/oauth-protected-resource (standard location) or https://api.example.com/.well-known/oauth-protected-resource.json (alternative).
How MCP Uses This
MCP clients (this metadata discovery has been part of the spec since the 2025-11-25 revision) fetch this metadata to:
- Discover which authorization servers can issue tokens
- Validate tokens against the correct JWKS endpoint
- Learn which scopes the API supports
- Use the correct token endpoint for
DPoP is not part of this discovery flow in the ratified MCP spec - see DPoP for why. If your resource server independently requires DPoP (an OAuth-layer decision, not an MCP one), advertise it through a field you define and document in your own metadata, since RFC 9728 does not standardize one.
When an MCP server calls your resource server, it:
- Fetches
/.well-known/oauth-protected-resource - Validates the resource identifier matches
- Fetches the JWKS from the declared
jwks_uri - On every request, validates the JWT signature against JWKS and verifies
issandaudclaims - Includes a DPoP proof only if your resource server independently requires sender-constrained tokens
// MCP client example (simplified)
async function callResourceServer(token: string, endpoint: string) {
// Fetch metadata
const metadata = await fetch("https://api.example.com/.well-known/oauth-protected-resource").then(
(r) => r.json(),
);
// Fetch JWKS
const jwks = await fetch(metadata.jwks_uri).then((r) => r.json());
// Validate token before sending
// (server-side validation happens too)
// If DPoP is required by this resource or token, generate a proof
let dpopProof = null;
if (requiresDpop(metadata)) {
dpopProof = await generateDPopProof("GET", endpoint);
}
const headers: Record<string, string> = {
Authorization: `Bearer ${token}`,
};
if (dpopProof) {
headers["DPoP"] = dpopProof;
}
const res = await fetch(endpoint, { headers });
return res.json();
}Current 2026-07-28 behavior: the metadata document itself is unchanged, but two client
obligations around it tighten. Clients must validate the iss parameter on the authorization
response against the issuer they discovered through this document, per RFC 9207 (SEP-2468), and
must store client credentials keyed by that issuer rather than by resource, re-registering when a
resource's authorization_servers entry changes (SEP-2352). A stable, correct issuer in your
authorization server metadata matters more than before: changing it invalidates every client
credential registered against it.
Minimal Metadata for Simple APIs
If you're not using DPoP or token exchange yet, a minimal metadata document is acceptable:
{
"resource": "https://api.example.com",
"authorization_servers": ["https://auth.example.com/"],
"scopes_supported": ["read", "write"],
"jwks_uri": "https://auth.example.com/.well-known/jwks.json",
"bearer_methods_supported": ["header"]
}Publishing Metadata in OpenAPI
You can reference protected resource metadata in your spec:
openapi: 3.1.0
info:
title: Example API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
oauth2:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://auth.example.com/token
scopes:
invoices:read: Read invoices
invoices:write: Create and update invoices
paths:
/invoices:
get:
security:
- oauth2: [invoices:read]
responses:
"200":
description: List of invoices
x-oauth-protected-resource: /.well-known/oauth-protected-resourceThis allows OpenAPI tools to automatically discover your OAuth requirements.
Cache and Update Strategy
The metadata document is relatively stable but can change when you:
- Add new scopes
- Support DPoP
- Add token exchange support
- Change authorization servers
Clients typically cache metadata for 1 hour (specify Cache-Control: max-age=3600). When you make changes, the cache expires naturally. For immediate propagation, rotate the URI or notify clients.
Checklist
- Endpoint is served at
/.well-known/oauth-protected-resource -
resourcematches your API's base URI or MCP endpoint resource identifier -
authorization_serversincludes all trusted identity providers -
scopes_supportedis exhaustive and matches your API's permission model -
jwks_uripoints to a valid JWKS endpoint with current public keys -
bearer_methods_supportedis explicit and does not permit query-string bearer tokens - Authorization server metadata advertises the grants and token endpoint methods clients can use
- Response includes
Cache-Controlheader with appropriate TTL - Metadata is served with
Content-Type: application/json
See Also
- DPoP - sender-constrained tokens
- Token Exchange - RFC 8693 delegation
- MCP Authorization - MCP's use of metadata
- auth.md - agentic registration discovery
- RFC 8414: OAuth 2.0 Authorization Server Metadata - base specification
- RFC 9728: OAuth 2.0 Protected Resource Metadata - resource server extension