mcp.auth.oauth_proxy

A server-side OAuth provider that fronts an upstream OAuth identity provider which does NOT support Dynamic Client Registration (GitHub, Google, Azure, etc.). It is the D analogue of FastMCP's OAuthProxy.

The proxy presents a full DCR-capable OAuth surface to MCP clients — RFC 9728 Protected Resource Metadata, RFC 8414 Authorization Server Metadata, and an RFC 7591 Dynamic Client Registration endpoint — while transparently using a single set of fixed upstream client credentials with the real IdP. MCP clients (including this SDK's own OAuthClient, which insists on DCR) can therefore complete an authorization-code + PKCE flow against an upstream that has no DCR support at all.

Concretely the proxy: * advertises its own /authorize, /token, and /register endpoints in the AS metadata it publishes (RFC 8414), so clients discover the proxy rather than the upstream; * answers DCR (/register) by handing every client the same fixed upstream client_id (RFC 7591 §3.2.1), recording the client's dynamic redirect URI so it can be honoured after the upstream round-trip; * proxies /authorize by redirecting to the upstream authorization endpoint with the fixed upstream client_id and the proxy's own fixed callback URL (base_url + redirect_path); * proxies /token by exchanging the code at the upstream token endpoint using the fixed upstream credentials; * maps the upstream access token to a TokenInfo via a configured TokenValidator (e.g. introspectionVerifier, jwtVerifier, or staticVerifier), so it plugs into ResourceServerConfig.validator.

The pure builders (registrationResponseJson, authorizationServerMetadata, proxyAuthorizeUrl, proxyTokenForm) carry no HTTP or clock state so they are unit-testable with a mocked upstream.

Members

Classes

ConsentRequiredException
class ConsentRequiredException

Thrown by OAuthProxy.authorize when the dynamically-registered client (identified by its redirect_uri) has not yet been granted user consent. The integrator must present a consent screen, record approval via OAuthProxy.grantConsent, and only then build the upstream redirect. This enforces the confused-deputy MUST: consent is obtained for each dynamically registered client before forwarding to the upstream authorization server.

InMemoryConsentStore
class InMemoryConsentStore

A simple in-memory ConsentStore bounded against unauthenticated growth: the number of approved clients is capped, evicting the oldest approval first when the cap is reached, so the (otherwise insert-only) consent map cannot grow process memory without bound.

InMemoryRedirectUriRegistry
class InMemoryRedirectUriRegistry

A simple in-memory RedirectUriRegistry bounded against unauthenticated growth: each /register call is scoped under its server-issued registrationHandle, and when the number of live registrations exceeds the cap the oldest registration (and all its redirect URIs) is evicted as a unit. The cap is what keeps an unauthenticated POST /register flood from growing process memory without bound.

InvalidRedirectUriException
class InvalidRedirectUriException

Thrown by OAuthProxy.authorize when the client-supplied redirect_uri is not an exact match against a previously-registered redirect_uri (RFC 6749 §3.1.2.2 / §10.6) or uses a scheme that is not allowed (RFC 8252 §7.3). The proxy fails closed: it neither mints proxy state nor forwards the request upstream. An HTTP mount maps this to a 400 invalid_request.

OAuthProxy
class OAuthProxy

A reusable OAuth proxy provider. Construct it from an OAuthProxyConfig, then read the metadata surface to publish, drive the authorize/token proxying, and obtain a TokenValidator for ResourceServerConfig.validator.

Functions

authorizationServerMetadata
AuthorizationServerMetadata authorizationServerMetadata(OAuthProxyConfig cfg)

The RFC 8414 Authorization Server Metadata the proxy publishes. It advertises the proxy's own /authorize, /token, and /register endpoints (NOT the upstream's), mandates PKCE S256, and lists the supported scopes. Because a registration_endpoint is present, MCP clients select Dynamic Client Registration and obtain the fixed upstream credentials transparently.

authorizationServerMetadataJson
Json authorizationServerMetadataJson(OAuthProxyConfig cfg)

Serialize the RFC 8414 AS metadata document the proxy serves at /.well-known/oauth-authorization-server. Emits the proxy endpoints, response_types_supported (RFC 8414 §2 REQUIRED when authorization_endpoint is present), code_challenge_methods_supported, grant_types_supported, token_endpoint_auth_methods_supported, and (when non-empty) scopes_supported.

isAllowedRedirectScheme
bool isAllowedRedirectScheme(string redirectUri)

Whether a client redirect_uri uses a scheme the proxy is willing to relay an authorization code to. https is always allowed; plain http is allowed only for loopback hosts (127.0.0.1, [::1], localhost) per RFC 8252 §7.3. All other schemes (including http to a non-loopback host, and custom/private-use schemes) are rejected so the upstream code can never be relayed over an open-redirect-prone or interceptable channel.

protectedResourceMetadata
ProtectedResourceMetadata protectedResourceMetadata(OAuthProxyConfig cfg)

The RFC 9728 Protected Resource Metadata the proxy publishes: it names the proxy itself as the sole authorization server for the MCP resource.

proxyAuthorizeUrl
string proxyAuthorizeUrl(OAuthProxyConfig cfg, string codeChallenge, string scopeStr, string state)

Build the upstream authorization redirect URL for a proxied /authorize request. The proxy substitutes its OWN fixed upstream client_id and fixed callback URL, forwarding the client-supplied PKCE code_challenge, scope, state, and (RFC 8707) resource. The client's real redirect_uri is NOT sent upstream — the proxy receives the code at its fixed callback and relays it.

proxyRefreshTokenForm
string proxyRefreshTokenForm(OAuthProxyConfig cfg, string refreshToken)

Build the application/x-www-form-urlencoded body for an upstream refresh-token exchange (OAuth 2.1 §4.3 / RFC 6749 §6). Carries grant_type=refresh_token, the client-relayed refresh_token, the proxy's fixed upstream client_id, and (RFC 8707) resource. For client_secret_post the upstream secret is appended to the body; for client_secret_basic it is sent via proxyTokenAuthHeader instead.

proxyTokenAuthHeader
string proxyTokenAuthHeader(OAuthProxyConfig cfg)

The HTTP Authorization header value to use for the upstream token request, or null when no Basic auth applies (i.e. the method is not client_secret_basic, or no secret is configured).

proxyTokenForm
string proxyTokenForm(OAuthProxyConfig cfg, string code, string codeVerifier)

Build the application/x-www-form-urlencoded body for the upstream authorization-code token exchange. Uses the proxy's fixed upstream client_id, fixed callback redirect_uri, the client-supplied PKCE code_verifier, and (RFC 8707) resource. For client_secret_post the upstream secret is appended to the body; for client_secret_basic it is sent via proxyTokenAuthHeader instead.

registrationResponseJson
Json registrationResponseJson(OAuthProxyConfig cfg, string[] requestedRedirectUris)

Serialize the DCR registration response document (RFC 7591 §3.2.1). Always carries client_id and echoes the requested redirect_uris and token_endpoint_auth_method=none (public client).

registrationResult
RegisteredClient registrationResult(OAuthProxyConfig cfg)

Build the RFC 7591 §3.2.1 registration result for a DCR request: the proxy returns the SAME fixed upstream client_id to every client. The proxy never discloses the upstream secret — clients act as public PKCE clients.

Interfaces

ConsentStore
interface ConsentStore

Records that a user has approved a particular dynamically-registered client to be forwarded to the upstream identity provider, and answers whether a given client has already been approved.

RedirectUriRegistry
interface RedirectUriRegistry
Undocumented in source.

Structs

OAuthProxyConfig
struct OAuthProxyConfig

Configuration for an OAuthProxy.

Variables

maxRedirectUrisPerRegistration
enum size_t maxRedirectUrisPerRegistration;

Records the exact set of redirect_uris a dynamically-registered client presented at /register, keyed by a server-issued registration handle, and answers whether a given redirect_uri is an exact member of that set. This is the allowlist the proxy enforces at /authorize before relaying an upstream authorization code. Upper bound on the number of redirect_uris a single /register request may register. An unauthenticated DCR request carrying more than this is truncated to the first maxRedirectUrisPerRegistration entries so one request cannot inflate the registry without bound.