Skip to main content
New in version: 2.12.0 The OAuth proxy enables FastMCP servers to authenticate with OAuth providers that don’t support Dynamic Client Registration (DCR). This includes virtually all traditional OAuth providers: GitHub, Google, Azure, AWS, Discord, Facebook, and most enterprise identity systems. For providers that do support DCR (like Descope and WorkOS AuthKit), use RemoteAuthProvider instead. MCP clients expect to register automatically and obtain credentials on the fly, but traditional providers require manual app registration through their developer consoles. The OAuth proxy bridges this gap by presenting a DCR-compliant interface to MCP clients while using your pre-registered credentials with the upstream provider. When a client attempts to register, the proxy returns your fixed credentials. When a client initiates authorization, the proxy handles the complexity of callback forwarding—storing the client’s dynamic callback URL, using its own fixed callback with the provider, then forwarding back to the client after token exchange. This approach enables any MCP client (whether using random localhost ports or fixed URLs like Claude.ai) to authenticate with any traditional OAuth provider, all while maintaining full OAuth 2.1 and PKCE security.
For providers that support OIDC discovery (Auth0, Google with OIDC configuration, Azure AD), consider using OIDC Proxy for automatic configuration. OIDC Proxy extends the OAuth proxy to automatically discover endpoints from the provider’s /.well-known/openid-configuration URL, simplifying setup.

Implementation

Provider Setup Requirements

Before using the OAuth proxy, you need to register your application with your OAuth provider:
  1. Register your application in the provider’s developer console (GitHub Settings, Google Cloud Console, Azure Portal, etc.)
  2. Configure the redirect URI as your FastMCP server URL plus your chosen callback path:
    • Default: https://your-server.com/auth/callback
    • Custom: https://your-server.com/your/custom/path (if you set redirect_path)
    • Development: http://localhost:8000/auth/callback
  3. Obtain your credentials: Client ID and Client Secret
  4. Note the OAuth endpoints: Authorization URL and Token URL (usually found in the provider’s OAuth documentation)
The redirect URI you configure with your provider must exactly match your FastMCP server’s URL plus the callback path. If you customize redirect_path in the OAuth proxy, update your provider’s redirect URI accordingly.

Basic Setup

Here’s how to implement the OAuth proxy with any provider:
from fastmcp import FastMCP
from fastmcp.server.auth import OAuthProxy
from fastmcp.server.auth.providers.jwt import JWTVerifier

# Configure token verification for your provider
# See the Token Verification guide for provider-specific setups
token_verifier = JWTVerifier(
    jwks_uri="https://your-provider.com/.well-known/jwks.json",
    issuer="https://your-provider.com",
    audience="your-app-id"
)

# Create the OAuth proxy
auth = OAuthProxy(
    # Provider's OAuth endpoints (from their documentation)
    upstream_authorization_endpoint="https://provider.com/oauth/authorize",
    upstream_token_endpoint="https://provider.com/oauth/token",

    # Your registered app credentials
    upstream_client_id="your-client-id",
    upstream_client_secret="your-client-secret",

    # Token validation (see Token Verification guide)
    token_verifier=token_verifier,

    # Your FastMCP server's public URL
    base_url="https://your-server.com",

    # Optional: customize the callback path (default is "/auth/callback")
    # redirect_path="/custom/callback",
)

mcp = FastMCP(name="My Server", auth=auth)

Configuration Parameters

OAuthProxy Parameters

upstream_authorization_endpoint
str
required
URL of your OAuth provider’s authorization endpoint (e.g., https://github.com/login/oauth/authorize)
upstream_token_endpoint
str
required
URL of your OAuth provider’s token endpoint (e.g., https://github.com/login/oauth/access_token)
upstream_client_id
str
required
Client ID from your registered OAuth application
upstream_client_secret
str
required
Client secret from your registered OAuth application
token_verifier
TokenVerifier
required
A TokenVerifier instance to validate the provider’s tokens
base_url
AnyHttpUrl | str
required
Public URL of your FastMCP server (e.g., https://your-server.com)
redirect_path
str
default:"/auth/callback"
Path for OAuth callbacks. Must match the redirect URI configured in your OAuth application
upstream_revocation_endpoint
str | None
Optional URL of provider’s token revocation endpoint
issuer_url
AnyHttpUrl | str | None
Issuer URL for OAuth metadata (defaults to base_url)
service_documentation_url
AnyHttpUrl | str | None
Optional URL to your service documentation
forward_pkce
bool
default:"True"
Whether to forward PKCE (Proof Key for Code Exchange) to the upstream OAuth provider. When enabled and the client uses PKCE, the proxy generates its own PKCE parameters to send upstream while separately validating the client’s PKCE. This ensures end-to-end PKCE security at both layers (client-to-proxy and proxy-to-upstream). - True (default): Forward PKCE for providers that support it (Google, Azure, AWS, GitHub, etc.) - False: Disable only if upstream provider doesn’t support PKCE
token_endpoint_auth_method
str | None
Token endpoint authentication method for the upstream OAuth server. Controls how the proxy authenticates when exchanging authorization codes and refresh tokens with the upstream provider. - "client_secret_basic": Send credentials in Authorization header (most common) - "client_secret_post": Send credentials in request body (required by some providers) - "none": No authentication (for public clients) - None (default): Uses authlib’s default (typically "client_secret_basic") Set this if your provider requires a specific authentication method and the default doesn’t work.
allowed_client_redirect_uris
list[str] | None
List of allowed redirect URI patterns for MCP clients. Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*"). - None (default): All redirect URIs allowed (for MCP/DCR compatibility) - Empty list []: No redirect URIs allowed - Custom list: Only matching patterns allowed These patterns apply to MCP client loopback redirects, NOT the upstream OAuth app redirect URI.
valid_scopes
list[str] | None
List of all possible valid scopes for the OAuth provider. These are advertised to clients through the /.well-known endpoints. Defaults to required_scopes from your TokenVerifier if not specified.
extra_authorize_params
dict[str, str] | None
Additional parameters to forward to the upstream authorization endpoint. Useful for provider-specific parameters that aren’t part of the standard OAuth2 flow.For example, Auth0 requires an audience parameter to issue JWT tokens:
extra_authorize_params={"audience": "https://api.example.com"}
These parameters are added to every authorization request sent to the upstream provider.
extra_token_params
dict[str, str] | None
Additional parameters to forward to the upstream token endpoint during code exchange and token refresh. Useful for provider-specific requirements during token operations.For example, some providers require additional context during token exchange:
extra_token_params={"audience": "https://api.example.com"}
These parameters are included in all token requests to the upstream provider.
client_storage
KVStorage | None
Storage backend for persisting OAuth client registrations. By default, clients are automatically persisted to disk in ~/.config/fastmcp/oauth-proxy-clients/, allowing them to survive server restarts as long as the filesystem remains accessible. This means MCP clients only need to register once and can reconnect seamlessly after your server restarts.
from fastmcp.utilities.storage import InMemoryStorage

# Use in-memory storage for testing (clients lost on restart)
auth = OAuthProxy(..., client_storage=InMemoryStorage())

Using Built-in Providers

FastMCP includes pre-configured providers for common services:
from fastmcp.server.auth.providers.github import GitHubProvider

auth = GitHubProvider(
    client_id="your-github-app-id",
    client_secret="your-github-app-secret",
    base_url="https://your-server.com"
)

mcp = FastMCP(name="My Server", auth=auth)
Available providers include GitHubProvider, GoogleProvider, and others. These handle token verification automatically.

Token Verification

The OAuth proxy requires a compatible TokenVerifier to validate tokens from your provider. Different providers use different token formats:
  • JWT tokens (Google, Azure): Use JWTVerifier with the provider’s JWKS endpoint
  • Opaque tokens (GitHub, Discord): Use provider-specific verifiers or implement custom validation
See the Token Verification guide for detailed setup instructions for your provider.

Scope Configuration

OAuth scopes control what permissions your application requests from users. They’re configured through your TokenVerifier (required for the OAuth proxy to validate tokens from your provider). Set required_scopes to automatically request the permissions your application needs:
JWTVerifier(..., required_scopes = ["read:user", "write:data"])
Dynamic clients created by the proxy will automatically include these scopes in their authorization requests. See the Token Verification section below for detailed setup.

Custom Parameters

Some OAuth providers require additional parameters beyond the standard OAuth2 flow. Use extra_authorize_params and extra_token_params to pass provider-specific requirements. For example, Auth0 requires an audience parameter to issue JWT tokens instead of opaque tokens:
auth = OAuthProxy(
    upstream_authorization_endpoint="https://your-domain.auth0.com/authorize",
    upstream_token_endpoint="https://your-domain.auth0.com/oauth/token",
    upstream_client_id="your-auth0-client-id",
    upstream_client_secret="your-auth0-client-secret",

    # Auth0-specific audience parameter
    extra_authorize_params={"audience": "https://your-api-identifier.com"},
    extra_token_params={"audience": "https://your-api-identifier.com"},

    token_verifier=JWTVerifier(
        jwks_uri="https://your-domain.auth0.com/.well-known/jwks.json",
        issuer="https://your-domain.auth0.com/",
        audience="https://your-api-identifier.com"
    ),
    base_url="https://your-server.com"
)
The proxy also automatically forwards RFC 8707 resource parameters from MCP clients to upstream providers that support them.

OAuth Flow

The flow diagram above illustrates the complete OAuth proxy pattern. Let’s understand each phase:

Registration Phase

When an MCP client calls /register with its dynamic callback URL, the proxy responds with your pre-configured upstream credentials. The client stores these credentials believing it has registered a new app. Meanwhile, the proxy records the client’s callback URL for later use.

Authorization Phase

The client initiates OAuth by redirecting to the proxy’s /authorize endpoint. The proxy:
  1. Stores the client’s transaction with its PKCE challenge
  2. Generates its own PKCE parameters for upstream security
  3. Shows the user a consent page with the client’s details, redirect URI, and requested scopes
  4. If the user approves (or the client was previously approved), redirects to the upstream provider using the fixed callback URL
This dual-PKCE approach maintains end-to-end security at both the client-to-proxy and proxy-to-provider layers. The consent step protects against confused deputy attacks by ensuring you explicitly approve each client before it can complete authorization.

Callback Phase

After user authorization, the provider redirects back to the proxy’s fixed callback URL. The proxy:
  1. Exchanges the authorization code for tokens with the provider
  2. Stores these tokens temporarily
  3. Generates a new authorization code for the client
  4. Redirects to the client’s original dynamic callback URL

Token Exchange Phase

Finally, the client exchanges its authorization code with the proxy to receive the provider’s tokens. The proxy validates the client’s PKCE verifier before returning the stored tokens. This entire flow is transparent to the MCP client—it experiences a standard OAuth flow with dynamic registration, unaware that a proxy is managing the complexity behind the scenes.

PKCE Forwarding

The OAuth proxy automatically handles PKCE (Proof Key for Code Exchange) when working with providers that support or require it. The proxy generates its own PKCE parameters to send upstream while separately validating the client’s PKCE, ensuring end-to-end security at both layers. This is enabled by default via the forward_pkce parameter and works seamlessly with providers like Google, Azure AD, and GitHub. Only disable it for legacy providers that don’t support PKCE:
# Disable PKCE forwarding only if upstream doesn't support it
auth = OAuthProxy(
    ...,
    forward_pkce=False  # Default is True
)

Redirect URI Validation

While the OAuth proxy accepts all redirect URIs by default (for DCR compatibility), you can restrict which clients can connect by specifying allowed patterns:
# Allow only localhost clients (common for development)
auth = OAuthProxy(
    # ... other parameters ...
    allowed_client_redirect_uris=[
        "http://localhost:*",
        "http://127.0.0.1:*"
    ]
)

# Allow specific known clients
auth = OAuthProxy(
    # ... other parameters ...
    allowed_client_redirect_uris=[
        "http://localhost:*",
        "https://claude.ai/api/mcp/auth_callback",
        "https://*.mycompany.com/auth/*"  # Wildcard patterns supported
    ]
)
Check your server logs for “Client registered with redirect_uri” messages to identify what URLs your clients use.

Security

Confused Deputy Attacks

New in version: 2.13.0 A confused deputy attack allows a malicious client to steal your authorization by tricking you into granting it access under your identity. The OAuth proxy works by bridging DCR clients to traditional auth providers, which means that multiple MCP clients connect through a single upstream OAuth application. An attacker can exploit this shared application by registering a malicious client with their own redirect URI, then sending you an authorization link. When you click it, your browser goes through the OAuth flow—but since you may have already authorized this OAuth app before, the provider might auto-approve the request. The authorization code then gets sent to the attacker’s redirect URI instead of a legitimate client, giving them access under your credentials.

Mitigation

FastMCP’s OAuth proxy requires you to explicitly consent whenever any new or unrecognized client attempts to connect to your server. Before any authorization happens, you see a consent page showing the client’s details, redirect URI, and requested scopes. This gives you the opportunity to review and deny suspicious requests. Once you approve a client, it’s remembered so you don’t see the consent page again for that client. The consent mechanism is implemented with CSRF tokens and cryptographically signed cookies to prevent tampering. Learn more:

Environment Configuration

New in version: 2.12.1 For production deployments, configure the OAuth proxy through environment variables instead of hardcoding credentials:
# Specify the provider implementation
export FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.github.GitHubProvider

# Provider-specific credentials
export FASTMCP_SERVER_AUTH_GITHUB_CLIENT_ID="Ov23li..."
export FASTMCP_SERVER_AUTH_GITHUB_CLIENT_SECRET="abc123..."
export FASTMCP_SERVER_AUTH_GITHUB_BASE_URL="https://your-production-server.com"
With environment configuration, your server code simplifies to:
from fastmcp import FastMCP

# Authentication automatically configured from environment
mcp = FastMCP(name="My Server")

@mcp.tool
def protected_tool(data: str) -> str:
    """This tool is now protected by OAuth."""
    return f"Processed: {data}"

if __name__ == "__main__":
    mcp.run(transport="http", port=8000)