Introduce RemoteAuthProvider for cleaner external identity provider integration, update docs (#1346)

This commit is contained in:
Jeremiah Lowin 2025-08-02 17:36:38 -07:00 committed by GitHub
commit ec52e74b48
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 758 additions and 769 deletions

View file

@ -10,159 +10,184 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.11.0" />
<Tip>
Authentication is only relevant for FastMCP's HTTP-based transports (`http` and `sse`). STDIO transport relies on the security of the local environment where it runs.
</Tip>
Authentication in MCP presents unique challenges that differ from traditional web applications. MCP clients need to discover authentication requirements automatically, negotiate OAuth flows without user intervention, and work seamlessly across different identity providers. FastMCP addresses these challenges by providing authentication patterns that integrate with the MCP protocol while remaining simple to implement and deploy.
FastMCP provides a powerful and flexible authentication system designed to fit modern application needs. Authentication is a fast-moving and often confusing part of the MCP specification, so FastMCP endeavors to make it as straightforward as possible while adhering to industry best practices as the MCP community evolves new standards.
<Tip>
Authentication applies only to FastMCP's HTTP-based transports (`http` and `sse`). The STDIO transport inherits security from its local execution environment.
</Tip>
<Warning>
**Authentication is rapidly evolving in MCP.** The specification and best practices are changing quickly. FastMCP aims to provide stable, secure patterns that adapt to these changes while keeping your code simple and maintainable.
</Warning>
## Authentication Patterns
## MCP Authentication Challenges
MCP supports a variety of authentication options depending on how much of the authentication complexity you want to pull into your server itself. This can be thought of as a trade-off between whether your MCP server acts as a **Resource Server (RS)** that protects resources, an **Authorization Server (AS)** that handles user authentication and issues tokens, or neither.
Traditional web authentication assumes a human user with a browser who can interact with login forms and consent screens. MCP clients are often automated systems that need to authenticate without human intervention. This creates several unique requirements:
Think of it as a spectrum:
**Automatic Discovery**: MCP clients must discover authentication requirements by examining server metadata rather than encountering login redirects.
- **No responsibility:** Your server has no authentication *(none)*
- **Minimal responsibility:** Your server only validates tokens issued elsewhere *(RS)*
- **Moderate responsibility:** Your server coordinates with external identity providers *(RS + remote AS)* — **recommended for most users**
- **Full responsibility:** Your server handles the entire authentication lifecycle *(RS + AS)*
**Programmatic OAuth**: OAuth flows must work without human interaction, relying on pre-configured credentials or Dynamic Client Registration.
### Unauthenticated
**Token Management**: Clients need to obtain, refresh, and manage tokens automatically across multiple MCP servers.
Unauthenticated FastMCP servers run without any mechanisms to protect their components. All tools and resources are publicly accessible to any client that can connect to your server.
**Protocol Integration**: Authentication must integrate cleanly with MCP's transport mechanisms and error handling.
**Use this when:**
- Building development or testing environments
- Creating internal tools where network access controls provide sufficient security
- Prototyping before implementing proper authentication
These challenges mean that not all authentication approaches work well with MCP. The patterns that do work fall into three categories based on the level of authentication responsibility your server assumes.
<Warning>
## Understanding Authentication Responsibility
**Security considerations:**
- Only suitable for trusted environments or carefully designed public APIs
- Consider network-level security (VPNs, firewalls, private networks)
- Exercise extreme caution when exposing unauthenticated servers to the public internet
- Ensure any public endpoints only expose non-sensitive data or operations
</Warning>
Authentication responsibility exists on a spectrum. Your MCP server can validate tokens created elsewhere, coordinate with external identity providers, or handle the complete authentication lifecycle internally. Each approach involves different trade-offs between simplicity, security, and control.
### Token Verification
### Token Validation
Token verification is the conceptually simplest approach to authentication, where your FastMCP server acts as a pure **Resource Server**. Your server validates `Bearer` tokens on incoming requests but has no knowledge of how those tokens were obtained. This is analogous to how a web server validates API keys on incoming requests. Read more in the [token verification documentation](/servers/auth/token-verification).
Your server validates tokens but delegates their creation to external systems. This approach treats your MCP server as a pure resource server that trusts tokens signed by known issuers.
<Note>
**Protocol Note:** While simple to implement, this pattern operates somewhat outside the formal MCP authentication flow, which expects OAuth-style interactions. It's best suited for internal systems or when you have full control over token generation.
</Note>
Token validation works well when you already have authentication infrastructure that can issue structured tokens like JWTs. Your existing API gateway, microservices platform, or enterprise SSO system becomes the source of truth for user identity, while your MCP server focuses on its core functionality.
**Use this when:**
- You just need to validate tokens issued by another system
- Building internal microservices that trust a central auth service
- Working with static, long-lived API keys
- You control both the token issuer and your FastMCP server
The key insight is that token validation separates authentication (proving who you are) from authorization (determining what you can do). Your MCP server receives proof of identity in the form of a signed token and makes access decisions based on the claims within that token.
**Responsibilities you're taking on:**
- Token validation logic
- Ensuring tokens are securely transmitted to your server
- Managing token lifecycle in your issuing system
This pattern excels in microservices architectures where multiple services need to validate the same tokens, or when integrating MCP servers into existing systems that already handle user authentication.
### Remote OAuth
### External Identity Providers
This is the **recommended pattern for most FastMCP users** and follows the 2025-6-18 MCP protocol update. Your FastMCP server acts as a **Resource Server** and integrates with an external, trusted **Authorization Server** like WorkOS, Auth0, or Okta. You can learn more about this pattern in the [remote OAuth documentation](/servers/auth/remote-oauth).
Your server coordinates with established identity providers to create seamless authentication experiences for MCP clients. This approach leverages OAuth 2.0 and OpenID Connect protocols to delegate user authentication while maintaining control over authorization decisions.
**Use this when:**
- You want to integrate with external identity providers
- Building user-facing applications that need SSO
- You want enterprise-grade authentication without building it yourself
- You need features like multi-factor authentication, social logins, or directory sync
External identity providers handle the complex aspects of authentication: user credential verification, multi-factor authentication, account recovery, and security monitoring. Your MCP server receives tokens from these trusted providers and validates them using the provider's public keys.
**Responsibilities you're taking on:**
- Configuring your server to trust the external provider
- Token validation using the provider's public keys
- Mapping token claims to your application's user model
The MCP protocol's support for Dynamic Client Registration makes this pattern particularly powerful. MCP clients can automatically discover your authentication requirements and register themselves with your identity provider without manual configuration.
**What the external provider handles:**
- User login and consent flows
- Token issuance and management
- User account management
- Security features like MFA and fraud detection
This approach works best for production applications that need enterprise-grade authentication features without the complexity of building them from scratch. It scales well across multiple applications and provides consistent user experiences.
### Full OAuth Server
### Full OAuth Implementation
<Warning>
**This is extremely advanced.** Most people should not build their own OAuth server. It requires deep security expertise and ongoing maintenance. Consider this only if you need complete control and have the resources to implement it securely.
</Warning>
Your server implements a complete OAuth 2.0 authorization server, handling everything from user credential verification to token lifecycle management. This approach provides maximum control at the cost of significant complexity.
In this pattern, your FastMCP server acts as both the **Authorization Server** and **Resource Server**, handling the entire authentication lifecycle from user login to token validation. Read more in the [full OAuth server documentation](/servers/auth/full-oauth-server).
Full OAuth implementation means building user interfaces for login and consent, implementing secure credential storage, managing token lifecycles, and maintaining ongoing security updates. The complexity extends beyond initial implementation to include threat monitoring, compliance requirements, and keeping pace with evolving security best practices.
**Use this when:**
- Building a completely standalone application with its own user database
- You need full control over every aspect of authentication
- Prototyping OAuth flows locally without external dependencies
- You have the security expertise to implement OAuth securely
This pattern makes sense only when you need complete control over the authentication process, operate in air-gapped environments, or have specialized requirements that external providers cannot meet.
**Responsibilities you're taking on:**
- Secure user credential storage and verification
- OAuth flow implementation
- Token lifecycle management
- Security measures like rate limiting and attack prevention
- User consent and account management interfaces
- Ongoing security updates and compliance
## FastMCP Implementation
## Configuring Authentication
FastMCP translates these authentication responsibility levels into three concrete classes that handle the complexities of MCP protocol integration.
FastMCP provides a variety of `AuthProvider` classes that can be used to configure your server. To use one, instantiate it and pass it to your FastMCP server's `auth` parameter.
### TokenVerifier
<CodeGroup>
`TokenVerifier` provides pure token validation without OAuth metadata endpoints. This class focuses on the essential task of determining whether a token is valid and extracting authorization information from its claims.
The implementation handles JWT signature verification, expiration checking, and claim extraction. It validates tokens against known issuers and audiences, ensuring that tokens intended for your server are not accepted by other systems.
```python Token Verification
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier
jwt_verifier = JWTVerifier(...)
auth = JWTVerifier(
jwks_uri="https://your-auth-system.com/.well-known/jwks.json",
issuer="https://your-auth-system.com",
audience="your-mcp-server"
)
mcp = FastMCP(name="My Server", auth=jwt_verifier)
mcp = FastMCP(name="Protected Server", auth=auth)
```
```python Remote OAuth
This example configures token validation against a JWT issuer. The `JWTVerifier` will fetch public keys from the JWKS endpoint and validate incoming tokens against those keys. Only tokens with the correct issuer and audience claims will be accepted.
`TokenVerifier` works well when you control both the token issuer and your MCP server, or when integrating with existing JWT-based infrastructure.
→ **Complete guide**: [Token Verification](/servers/auth/token-verification)
### RemoteAuthProvider
`RemoteAuthProvider` combines token validation with OAuth discovery metadata, enabling MCP clients to automatically discover and authenticate with external identity providers.
This class extends `TokenVerifier` functionality by adding OAuth 2.0 protected resource endpoints that advertise your authentication requirements. MCP clients can examine these endpoints to understand which identity providers you trust and how to obtain valid tokens.
The implementation handles the OAuth metadata generation required by the MCP specification while delegating actual token validation to an underlying `TokenVerifier`. This separation allows you to use different token validation strategies while maintaining consistent OAuth discovery behavior.
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.workos import AuthKitProvider
auth_provider = AuthKitProvider(...)
auth = AuthKitProvider(
authkit_domain="https://your-project.authkit.app",
base_url="https://your-fastmcp-server.com"
)
mcp = FastMCP(name="My Server", auth=auth_provider)
mcp = FastMCP(name="Enterprise Server", auth=auth)
```
</CodeGroup>
### Environment Variables
This example uses WorkOS AuthKit as the external identity provider. The `AuthKitProvider` automatically configures token validation against WorkOS and provides the OAuth metadata that MCP clients need for automatic authentication.
For providers that support it, you can configure authentication entirely through environment variables.
`RemoteAuthProvider` excels for production applications that need professional identity management without implementation complexity.
There are two steps to this process:
→ **Complete guide**: [Remote OAuth](/servers/auth/remote-oauth)
1. Set `FASTMCP_SERVER_AUTH` to the registered name of your provider. For example, `JWT` for the `JWTVerifier` or `AUTHKIT` for the `AuthKitProvider`.
2. Set the appropriate environment variables for your provider in order to configure it. These are provider-specific and can be found in the provider's documentation. Not all providers will support environment variable configuration for all of their settings.
### OAuthProvider
For example, to configure a JWT verifier, you would set:
`OAuthProvider` implements a complete OAuth 2.0 authorization server within your MCP server. This class handles the full authentication lifecycle from user credential verification to token management.
The implementation provides all required OAuth endpoints including authorization, token, and discovery endpoints. It manages client registration, user consent, and token lifecycle while integrating with your user storage and authentication logic.
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.oauth import MyOAuthProvider
auth = MyOAuthProvider(
user_store=your_user_database,
client_store=your_client_registry,
# Additional configuration...
)
mcp = FastMCP(name="Auth Server", auth=auth)
```
This example shows the basic structure of a custom OAuth provider. The actual implementation requires significant additional configuration for user management, client registration, and security policies.
`OAuthProvider` should be used only when you have specific requirements that external providers cannot meet and the expertise to implement OAuth securely.
→ **Complete guide**: [Full OAuth Server](/servers/auth/full-oauth-server)
## Configuration Approaches
FastMCP supports both programmatic configuration for maximum flexibility and environment-based configuration for deployment simplicity.
### Programmatic Configuration
Programmatic configuration provides complete control over authentication settings and allows for complex initialization logic. This approach works well during development and when you need to customize authentication behavior based on runtime conditions.
Authentication providers are instantiated directly in your code with their required parameters. This makes dependencies explicit and allows your IDE to provide helpful autocompletion and type checking.
### Environment Configuration
Environment-based configuration separates authentication settings from application code, enabling the same codebase to work across different deployment environments without modification.
FastMCP automatically detects authentication configuration from environment variables when no explicit `auth` parameter is provided. The configuration system supports all authentication providers and their various options.
```bash
export FASTMCP_SERVER_AUTH=JWT
export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://your-idp.com/.well-known/jwks.json"
export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://auth.example.com/jwks"
export FASTMCP_SERVER_AUTH_JWT_ISSUER="https://auth.example.com"
export FASTMCP_SERVER_AUTH_JWT_AUDIENCE="mcp-server"
```
And now your FastMCP server will automatically be configured with the JWT verifier:
With these environment variables set, creating an authenticated FastMCP server requires no additional configuration:
```python
from fastmcp import FastMCP
# Assumes the environment variables are set as above
mcp = FastMCP(name="My Protected Server")
assert mcp.auth is not None
assert mcp.auth.jwks_uri == "https://your-idp.com/.well-known/jwks.json"
# Authentication automatically configured from environment
mcp = FastMCP(name="My Server")
```
Note that if you provide an `auth` parameter to your FastMCP server, it will override the environment variable configuration. You can also set `auth=None` to disable authentication entirely and prohibit environment variable configuration.
This approach simplifies deployment pipelines and follows twelve-factor app principles for configuration management.
## Choosing Your Implementation
The authentication approach you choose depends on your existing infrastructure, security requirements, and operational constraints.
**For most production applications, external identity providers offer the best balance of security, features, and simplicity.** This approach provides enterprise-grade authentication without implementation complexity and scales well as your application grows. The main trade-off is requiring users to sign up with your chosen identity provider, but this also brings benefits like professional user management, security monitoring, and compliance features.
**Token validation works well when you already have authentication infrastructure that issues structured tokens.** If your organization already uses JWT-based systems, API gateways, or enterprise SSO that can generate tokens, this approach integrates seamlessly while keeping your MCP server focused on its core functionality. The simplicity comes from leveraging existing investment in authentication infrastructure.
**Full OAuth implementation should be avoided unless you have compelling reasons that external providers cannot address.** Air-gapped environments, specialized compliance requirements, or unique organizational constraints might justify this approach, but it requires significant security expertise and ongoing maintenance commitment. The complexity extends far beyond initial implementation to include threat monitoring, security updates, and keeping pace with evolving attack vectors.
FastMCP's architecture supports migration between these approaches as your requirements evolve. You can integrate with existing token systems initially and migrate to external identity providers as your application scales, or implement custom solutions when your requirements outgrow standard patterns.

View file

@ -11,119 +11,219 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.11.0" />
<Warning>
**This is an extremely advanced pattern.** Building a secure, production-ready OAuth 2.1 server is a complex undertaking that requires deep expertise in authentication protocols, cryptography, and security best practices.
**This is an extremely advanced pattern that most users should avoid.** Building a secure OAuth 2.1 server requires deep expertise in authentication protocols, cryptography, and security best practices. The complexity extends far beyond initial implementation to include ongoing security monitoring, threat response, and compliance maintenance.
This pattern exists primarily to support the MCP protocol specification's requirements. **Most users should strongly prefer the [Remote OAuth pattern](/servers/auth/remote-oauth)** to integrate with a dedicated identity provider like WorkOS, Auth0, or Okta.
**Use [Remote OAuth](/servers/auth/remote-oauth) instead** unless you have compelling requirements that external identity providers cannot meet, such as air-gapped environments or specialized compliance needs.
</Warning>
In the **Full OAuth Server** pattern, your FastMCP server acts as both the **Authorization Server (AS)** and the **Resource Server (RS)**. It becomes responsible for the entire authentication lifecycle:
The Full OAuth Server pattern exists to support the MCP protocol specification's requirements. Your FastMCP server becomes both an Authorization Server and Resource Server, handling the complete authentication lifecycle from user login to token validation.
- **User Management**: Storing user credentials, profiles, and permissions
- **Client Registration**: Managing MCP client applications and their credentials
- **Authentication Flow**: Handling login pages, multi-factor authentication, and user consent
- **Token Lifecycle**: Issuing, refreshing, and revoking access tokens
- **Security Controls**: Rate limiting, audit logging, and threat detection
This documentation exists for completeness - the vast majority of applications should use external identity providers instead.
This pattern should only be considered if you have strict requirements that prevent using external identity providers, such as air-gapped environments or highly specialized compliance needs.
## OAuthProvider
## Building an OAuth Provider
FastMCP provides the `OAuthProvider` abstract class that implements the OAuth 2.1 specification. To use this pattern, you must subclass `OAuthProvider` and implement all required abstract methods.
To implement this pattern, you must subclass `fastmcp.server.auth.auth.OAuthProvider` and implement all of its abstract methods. This class extends the low-level OAuth authorization server interface and requires implementing the complete OAuth 2.1 specification.
<Note>
`OAuthProvider` handles OAuth endpoints, protocol flows, and security requirements, but delegates all storage, user management, and business logic to your implementation of the abstract methods.
</Note>
```python
from fastmcp.server.auth.auth import OAuthProvider
from mcp.server.auth.provider import (
AccessToken, AuthorizationCode, RefreshToken, AuthorizationParams
)
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
## Required Implementation
class MyOAuthProvider(OAuthProvider):
"""
A production OAuth provider implementation.
WARNING: This is a simplified example. A real implementation
requires extensive security considerations, persistent storage,
proper error handling, and adherence to OAuth 2.1 security
best practices.
"""
def __init__(self, base_url: str):
super().__init__(base_url=base_url)
# Initialize your database connections, cryptographic keys, etc.
# === Client Management ===
async def get_client(self, client_id: str) -> OAuthClientInformationFull | None:
"""Retrieve client information by ID from your database."""
# Query your client database and return client info or None if not found
raise NotImplementedError
async def register_client(self, client_info: OAuthClientInformationFull) -> None:
"""Store new client registration information."""
# Validate and save client metadata to your database
# May raise RegistrationError if client data is invalid
raise NotImplementedError
# === Authorization Flow ===
async def authorize(
self, client: OAuthClientInformationFull, params: AuthorizationParams
) -> str:
"""
Handle authorization request and return redirect URL.
Many implementations redirect to a third-party OAuth provider, creating
a chain: Client -> MCP Server -> External IdP -> MCP Server -> Client.
You must generate an authorization code with at least 128 bits of entropy.
"""
# Authenticate user, get consent, generate auth code, return redirect URL
raise NotImplementedError
async def load_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: str
) -> AuthorizationCode | None:
"""Load authorization code from storage by code string."""
# Look up stored authorization code, return None if not found or expired
raise NotImplementedError
# === Token Management ===
async def exchange_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: AuthorizationCode
) -> OAuthToken:
"""Exchange authorization code for access and refresh tokens."""
# Validate code, generate new token pair, invalidate the auth code
raise NotImplementedError
async def load_refresh_token(
self, client: OAuthClientInformationFull, refresh_token: str
) -> RefreshToken | None:
"""Load refresh token from storage by token string."""
# Look up refresh token, return None if not found or expired
raise NotImplementedError
async def exchange_refresh_token(
self,
client: OAuthClientInformationFull,
refresh_token: RefreshToken,
scopes: list[str]
) -> OAuthToken:
"""Exchange refresh token for new access/refresh token pair."""
# Should rotate both tokens for security best practices
raise NotImplementedError
async def load_access_token(self, token: str) -> AccessToken | None:
"""Load and validate access token - called on every protected request."""
# Look up token, check expiration, return None if invalid
raise NotImplementedError
async def revoke_token(self, token: AccessToken | RefreshToken) -> None:
"""Revoke access or refresh token."""
# Should revoke both access and refresh tokens regardless of which is provided
# Do nothing if token is already invalid or revoked
raise NotImplementedError
# === Token Verification (AuthProvider interface) ===
async def verify_token(self, token: str) -> AccessToken | None:
"""Verify bearer token for incoming requests."""
# This is called on every protected MCP request
# Typically delegates to load_access_token
return await self.load_access_token(token)
```
You must implement these abstract methods to create a functioning OAuth server:
### Client Management
<Card icon="code" title="Client Management Methods">
<ParamField body="get_client" type="async method">
Retrieve client information by ID from your database.
<Expandable title="Parameters">
<ParamField body="client_id" type="str">
Client identifier to look up
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="OAuthClientInformationFull | None" type="return type">
Client information object or `None` if client not found
</ParamField>
</Expandable>
</ParamField>
<ParamField body="register_client" type="async method">
Store new client registration information in your database.
<Expandable title="Parameters">
<ParamField body="client_info" type="OAuthClientInformationFull">
Complete client registration information to store
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="None" type="return type">
No return value
</ParamField>
</Expandable>
</ParamField>
</Card>
### Authorization Flow
<Card icon="code" title="Authorization Flow Methods">
<ParamField body="authorize" type="async method">
Handle authorization request and return redirect URL. Must implement user authentication and consent collection.
<Expandable title="Parameters">
<ParamField body="client" type="OAuthClientInformationFull">
OAuth client making the authorization request
</ParamField>
<ParamField body="params" type="AuthorizationParams">
Authorization request parameters from the client
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="str" type="return type">
Redirect URL to send the client to
</ParamField>
</Expandable>
</ParamField>
<ParamField body="load_authorization_code" type="async method">
Load authorization code from storage by code string. Return `None` if code is invalid or expired.
<Expandable title="Parameters">
<ParamField body="client" type="OAuthClientInformationFull">
OAuth client attempting to use the authorization code
</ParamField>
<ParamField body="authorization_code" type="str">
Authorization code string to look up
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="AuthorizationCode | None" type="return type">
Authorization code object or `None` if not found
</ParamField>
</Expandable>
</ParamField>
</Card>
### Token Management
<Card icon="code" title="Token Management Methods">
<ParamField body="exchange_authorization_code" type="async method">
Exchange authorization code for access and refresh tokens. Must validate code and create new tokens.
<Expandable title="Parameters">
<ParamField body="client" type="OAuthClientInformationFull">
OAuth client exchanging the authorization code
</ParamField>
<ParamField body="authorization_code" type="AuthorizationCode">
Valid authorization code object to exchange
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="OAuthToken" type="return type">
New OAuth token containing access and refresh tokens
</ParamField>
</Expandable>
</ParamField>
<ParamField body="load_refresh_token" type="async method">
Load refresh token from storage by token string. Return `None` if token is invalid or expired.
<Expandable title="Parameters">
<ParamField body="client" type="OAuthClientInformationFull">
OAuth client attempting to use the refresh token
</ParamField>
<ParamField body="refresh_token" type="str">
Refresh token string to look up
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="RefreshToken | None" type="return type">
Refresh token object or `None` if not found
</ParamField>
</Expandable>
</ParamField>
<ParamField body="exchange_refresh_token" type="async method">
Exchange refresh token for new access/refresh token pair. Must validate scopes and token.
<Expandable title="Parameters">
<ParamField body="client" type="OAuthClientInformationFull">
OAuth client using the refresh token
</ParamField>
<ParamField body="refresh_token" type="RefreshToken">
Valid refresh token object to exchange
</ParamField>
<ParamField body="scopes" type="list[str]">
Requested scopes for the new access token
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="OAuthToken" type="return type">
New OAuth token with updated access and refresh tokens
</ParamField>
</Expandable>
</ParamField>
<ParamField body="load_access_token" type="async method">
Load an access token by its token string.
<Expandable title="Parameters">
<ParamField body="token" type="str">
The access token to verify
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="AccessToken | None" type="return type">
The access token object, or `None` if the token is invalid
</ParamField>
</Expandable>
</ParamField>
<ParamField body="revoke_token" type="async method">
Revoke access or refresh token, marking it as invalid in storage.
<Expandable title="Parameters">
<ParamField body="token" type="AccessToken | RefreshToken">
Token object to revoke and mark invalid
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="None" type="return type">
No return value
</ParamField>
</Expandable>
</ParamField>
<ParamField body="verify_token" type="async method">
Verify bearer token for incoming requests. Return `AccessToken` if valid, `None` if invalid.
<Expandable title="Parameters">
<ParamField body="token" type="str">
Bearer token string from incoming request
</ParamField>
</Expandable>
<Expandable title="Returns">
<ParamField body="AccessToken | None" type="return type">
Access token object if valid, `None` if invalid or expired
</ParamField>
</Expandable>
</ParamField>
</Card>
Each method must handle storage, validation, security, and error cases according to the OAuth 2.1 specification. The implementation complexity is substantial and requires expertise in OAuth security considerations.
<Warning>
**Security Notice:** OAuth server implementation involves numerous security considerations including PKCE, state parameters, redirect URI validation, token binding, replay attack prevention, and secure storage requirements. Mistakes can lead to serious security vulnerabilities.
</Warning>

View file

@ -1,7 +1,7 @@
---
title: Remote OAuth
sidebarTitle: Remote OAuth
description: Integrate with external identity providers like WorkOS, Auth0, or Okta by trusting them to handle user authentication.
description: Integrate your FastMCP server with external identity providers like WorkOS, Auth0, and corporate SSO systems.
icon: camera-cctv
tag: NEW
---
@ -10,138 +10,180 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.11.0" />
**Remote OAuth** is the recommended pattern for securing most production applications. In this model, your FastMCP server acts as a **Resource Server (RS)** and integrates with an external, trusted **Authorization Server (AS)**, such as WorkOS, Auth0, or a corporate SSO system.
Remote OAuth integration allows your FastMCP server to leverage external identity providers while maintaining the automated authentication flows that MCP clients require. This approach provides enterprise-grade authentication features without the complexity of implementing them yourself, making it the recommended pattern for most production applications.
This approach lets you leverage robust, feature-rich identity platforms for user management, multi-factor authentication, and social logins, while your FastMCP server focuses on its core job: providing tools and resources.
<Tip>
Remote OAuth requires identity providers that support **Dynamic Client Registration (DCR)**. This enables MCP clients to automatically register and authenticate without manual configuration steps.
</Tip>
### How It Works
## The Remote OAuth Challenge
The flow relies on the MCP client's ability to discover your server's authentication requirements. Your server doesn't handle logins itself; it tells the client where to find the real identity provider.
Traditional OAuth flows assume human users with web browsers who can interact with login forms, consent screens, and redirects. MCP clients operate differently - they're often automated systems that need to authenticate programmatically without human intervention.
The key endpoint is **`/.well-known/oauth-protected-resource`** which returns static metadata pointing to the authorization server. You can optionally also provide **`/.well-known/oauth-authorization-server`** that forwards the authorization server's metadata for convenience.
This creates several unique requirements that standard OAuth implementations don't address well:
**Automatic Discovery**: MCP clients must discover authentication requirements by examining server metadata rather than encountering HTTP redirects. They need to know which identity provider to use and how to reach it before making any authenticated requests.
**Programmatic Registration**: Clients need to register themselves with identity providers automatically. Manual client registration doesn't work when clients might be dynamically created tools or services.
**Seamless Token Management**: Clients must obtain, store, and refresh tokens without user interaction. The authentication flow needs to work in headless environments where no human is available to complete OAuth consent flows.
**Protocol Integration**: The authentication process must integrate cleanly with MCP's JSON-RPC transport layer and error handling mechanisms.
These requirements mean that your MCP server needs to do more than just validate tokens - it needs to provide discovery metadata that enables MCP clients to understand and navigate your authentication requirements automatically.
## MCP Authentication Discovery
MCP authentication discovery relies on well-known endpoints that clients can examine to understand your authentication requirements. Your server becomes a bridge between MCP clients and your chosen identity provider.
The core discovery endpoint is `/.well-known/oauth-protected-resource`, which tells clients that your server requires OAuth authentication and identifies the authorization servers you trust. This endpoint contains static metadata that points clients to your identity provider without requiring any dynamic lookups.
```mermaid
sequenceDiagram
participant Client
participant FastMCPServer as FastMCP (RS)
participant ExternalIdP as External IdP (AS)
participant FastMCPServer as FastMCP Server
participant ExternalIdP as Identity Provider
Client->>FastMCPServer: 1. GET /.well-known/oauth-protected-resource
FastMCPServer-->>Client: 2. "Use https://my-idp.com for auth"
note over Client, ExternalIdP: Client goes directly to the IdP
Client->>ExternalIdP: 3. GET /.well-known/oauth-authorization-server
ExternalIdP-->>Client: 4. OAuth endpoints & capabilities
Client->>ExternalIdP: 3. Authenticate & get token via DCR
ExternalIdP-->>Client: 4. Access token
Client->>ExternalIdP: 5. User authenticates & gets token
ExternalIdP-->>Client:
Client->>FastMCPServer: 6. MCP request with Bearer token
note right of FastMCPServer: Server verifies the token
FastMCPServer->>FastMCPServer: 7. Verify token signature <br/> (using IdP's public keys)
FastMCPServer-->>Client: 8. MCP Response
Client->>FastMCPServer: 5. MCP request with Bearer token
FastMCPServer->>FastMCPServer: 6. Verify token signature
FastMCPServer-->>Client: 7. MCP response
```
## Building a Custom Provider
This flow separates concerns cleanly: your MCP server handles resource protection and token validation, while your identity provider handles user authentication and token issuance. The client coordinates between these systems using standardized OAuth discovery mechanisms.
To connect to any identity provider, you create a custom `AuthProvider` subclass. This class has two main responsibilities:
## FastMCP Remote Authentication
1. **Verifying Tokens:** Validate tokens issued by the external provider.
2. **Forwarding Metadata:** Tell MCP clients where to find the external provider's login pages and token endpoints.
<VersionBadge version="2.11.1" />
### Step 1: Verifying Tokens
FastMCP provides `RemoteAuthProvider` to handle the complexities of remote OAuth integration. This class combines token validation capabilities with the OAuth discovery metadata that MCP clients require.
Your provider must implement the `verify_token` method. For most modern identity providers that issue JWTs, you can simply delegate this task to FastMCP's built-in `JWTVerifier`.
### RemoteAuthProvider
`RemoteAuthProvider` works by composing a [`TokenVerifier`](/servers/auth/token-verification) with authorization server information. A `TokenVerifier` is another FastMCP authentication class that focuses solely on token validation - signature verification, expiration checking, and claim extraction. The `RemoteAuthProvider` takes that token validation capability and adds the OAuth discovery endpoints that enable MCP clients to automatically find and authenticate with your identity provider.
This composition pattern means you can use any token validation strategy (JWT verification, introspection endpoints, custom validation logic) while maintaining consistent OAuth discovery behavior. The separation allows you to change token validation approaches without affecting the client discovery experience.
The class automatically generates the required OAuth metadata endpoints using the MCP SDK's standardized route creation functions. This ensures compatibility with MCP clients while reducing the implementation complexity for server developers.
### Basic Implementation
Most applications can use `RemoteAuthProvider` directly without subclassing. The implementation requires a `TokenVerifier` instance, a list of trusted authorization servers, and your server's URL for metadata generation.
```python
from fastmcp.server.auth.auth import AuthProvider
from fastmcp import FastMCP
from fastmcp.server.auth import RemoteAuthProvider
from fastmcp.server.auth.providers.jwt import JWTVerifier
from mcp.server.auth.provider import AccessToken
from pydantic import AnyHttpUrl
class MyIdPAuthProvider(AuthProvider):
def __init__(self):
super().__init__()
# The verifier validates tokens from the upstream provider.
self.token_verifier = JWTVerifier(
jwks_uri="https://my-idp.com/.well-known/jwks.json",
issuer="https://my-idp.com",
audience="my-fastmcp-api"
)
# Configure token validation for your identity provider
token_verifier = JWTVerifier(
jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
issuer="https://auth.yourcompany.com",
audience="mcp-production-api"
)
async def verify_token(self, token: str) -> AccessToken | None:
return await self.token_verifier.verify_token(token)
# Create the remote auth provider
auth = RemoteAuthProvider(
token_verifier=token_verifier,
authorization_servers=[AnyHttpUrl("https://auth.yourcompany.com")],
resource_server_url="https://api.yourcompany.com"
)
mcp = FastMCP(name="Company API", auth=auth)
```
### Step 2: Adding Discovery Metadata
This configuration creates a server that accepts tokens issued by `auth.yourcompany.com` and provides the OAuth discovery metadata that MCP clients need. The `JWTVerifier` handles token validation using your identity provider's public keys, while the `RemoteAuthProvider` generates the required OAuth endpoints.
Next, implement the `customize_auth_routes` method. The essential endpoint is `/.well-known/oauth-protected-resource` which tells clients where to find your authorization server. You can optionally add the authorization server forwarding endpoint for convenience.
The `authorization_servers` list tells MCP clients which identity providers you trust. The `resource_server_url` identifies your server in OAuth metadata, enabling proper token audience validation.
### Custom Endpoints
You can extend `RemoteAuthProvider` to add additional endpoints beyond the standard OAuth protected resource metadata. These don't have to be OAuth-specific - you can add any endpoints your authentication integration requires.
```python
import httpx
from starlette.responses import JSONResponse
from starlette.routing import Route
class MyIdPAuthProvider(AuthProvider):
# ... (init and verify_token from above) ...
class CompanyAuthProvider(RemoteAuthProvider):
def __init__(self):
token_verifier = JWTVerifier(
jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
issuer="https://auth.yourcompany.com",
audience="mcp-production-api"
)
super().__init__(
token_verifier=token_verifier,
authorization_servers=[AnyHttpUrl("https://auth.yourcompany.com")],
resource_server_url="https://api.yourcompany.com"
)
def customize_auth_routes(self, routes: list[Route]) -> list[Route]:
# Essential: Tell clients which authorization server to use
async def protected_resource_metadata(request):
return JSONResponse({
"resource": "https://my-fastmcp-server.com",
"authorization_servers": ["https://my-idp.com"],
"bearer_methods_supported": ["header"],
})
def get_routes(self) -> list[Route]:
"""Add custom endpoints to the standard protected resource routes."""
routes.append(Route("/.well-known/oauth-protected-resource", protected_resource_metadata))
# Get the standard OAuth protected resource routes
routes = super().get_routes()
# Optional: Forward the authorization server's metadata for convenience
# (Clients can also fetch this directly from the IdP)
# Add authorization server metadata forwarding for client convenience
async def authorization_server_metadata(request):
async with httpx.AsyncClient() as client:
resp = await client.get("https://my-idp.com/.well-known/oauth-authorization-server")
resp.raise_for_status()
return JSONResponse(resp.json())
routes.append(Route("/.well-known/oauth-authorization-server", authorization_server_metadata))
response = await client.get(
"https://auth.yourcompany.com/.well-known/oauth-authorization-server"
)
response.raise_for_status()
return JSONResponse(response.json())
routes.append(
Route("/.well-known/oauth-authorization-server", authorization_server_metadata)
)
return routes
mcp = FastMCP(name="Company API", auth=CompanyAuthProvider())
```
### Step 3: Using Your Provider
This pattern uses `super().get_routes()` to get the standard protected resource routes, then adds additional endpoints as needed. A common use case is providing authorization server metadata forwarding, which allows MCP clients to discover your identity provider's capabilities through your MCP server rather than contacting the identity provider directly.
With these two methods implemented, your auth provider is now fully integrated with your identity provider. You can now use your custom provider with FastMCP by passing it to the `auth` parameter of your `FastMCP` instance:
## WorkOS AuthKit Integration
```python
from fastmcp import FastMCP
mcp = FastMCP(name="My Secure Server", auth=MyIdPAuthProvider())
```
## Example: WorkOS AuthKit Provider
FastMCP provides a built-in provider for **WorkOS AuthKit** that handles this entire pattern for you. It's a perfect example of the remote OAuth pattern in action.
**Prerequisites:**
1. A WorkOS account with an AuthKit project.
2. **Dynamic Client Registration (DCR)** must be enabled in your WorkOS application settings.
3. Your FastMCP server's URL must be added as a **Redirect URI** in your WorkOS project (can be localhost for development).
WorkOS AuthKit provides an excellent example of remote OAuth integration. The `AuthKitProvider` demonstrates how to implement both token validation and OAuth metadata forwarding in a production-ready package.
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.workos import AuthKitProvider
# The AuthKitProvider implements both metadata forwarding and token validation.
auth_provider = AuthKitProvider(
# Your unique AuthKit domain from the WorkOS dashboard
auth = AuthKitProvider(
authkit_domain="https://your-project.authkit.app",
# The URL of THIS FastMCP server (can be localhost for development)
base_url="https://your-fastmcp-server.com"
base_url="https://your-mcp-server.com"
)
mcp = FastMCP(name="My WorkOS-Protected Server", auth=auth_provider)
mcp = FastMCP(name="Protected Application", auth=auth)
```
<Tip>
For a complete, step-by-step tutorial on using this provider, see the [**WorkOS AuthKit Integration Guide**](/integrations/authkit).
</Tip>
The `AuthKitProvider` automatically configures JWT validation against WorkOS's public keys and provides both protected resource metadata and authorization server metadata forwarding. This implementation handles the complete remote OAuth integration with minimal configuration.
WorkOS's support for Dynamic Client Registration makes it particularly well-suited for MCP applications. Clients can automatically register themselves with your WorkOS project and obtain the credentials needed for authentication without manual intervention.
→ **Complete WorkOS tutorial**: [AuthKit Integration Guide](/integrations/authkit)
## Implementation Considerations
Remote OAuth integration requires careful attention to several technical details that affect reliability and security.
**Token Validation Performance**: Your server validates every incoming token by checking signatures against your identity provider's public keys. Consider implementing key caching and rotation handling to minimize latency while maintaining security.
**Error Handling**: Network issues with your identity provider can affect token validation. Implement appropriate timeouts, retry logic, and graceful degradation to maintain service availability during identity provider outages.
**Audience Validation**: Ensure that tokens intended for your server are not accepted by other applications. Proper audience validation prevents token misuse across different services in your ecosystem.
**Scope Management**: Map token scopes to your application's permission model consistently. Consider how scope changes affect existing tokens and plan for smooth permission updates.
The complexity of these considerations reinforces why external identity providers are recommended over custom OAuth implementations. Established providers handle these technical details with extensive testing and operational experience.

View file

@ -1,7 +1,7 @@
---
title: Token Verification
sidebarTitle: Token Verification
description: Protect your server by validating bearer tokens.
description: Protect your server by validating bearer tokens issued by external systems.
icon: key
tag: NEW
---
@ -10,202 +10,191 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.11.0" />
In the **Token Verification** pattern, your FastMCP server acts as a pure **Resource Server**. It validates Bearer tokens on incoming requests and uses the token claims to authorize access to protected resources (tools, resources, and prompts). It does not participate in user login, token issuance, or consent flows - it trusts tokens issued by another system. This is equivalent to how a traditional web server validates API keys on incoming requests.
Token verification enables your FastMCP server to validate bearer tokens issued by external systems without participating in user authentication flows. Your server acts as a pure resource server, focusing on token validation and authorization decisions while delegating identity management to other systems in your infrastructure.
This is the right pattern if you have another system responsible for generating tokens and you simply need your FastMCP server to trust them or use the information in the token to make decisions.
<Note>
Token verification operates somewhat outside the formal MCP authentication flow, which expects OAuth-style discovery. It's best suited for internal systems, microservices architectures, or when you have full control over token generation and distribution.
</Note>
## JWT Verification
## Understanding Token Verification
JWT (JSON Web Token) verification is the recommended approach for production environments. The `JWTVerifier` class validates tokens using secure, asymmetric public key cryptography. This means your server only needs access to a public key to verify tokens, while the corresponding private key used for signing remains secure on your identity provider.
Token verification addresses scenarios where authentication responsibility is distributed across multiple systems. Your MCP server receives structured tokens containing identity and authorization information, validates their authenticity, and makes access control decisions based on their contents.
### Using the JWTVerifier
This pattern emerges naturally in microservices architectures where a central authentication service issues tokens that multiple downstream services validate independently. It also works well when integrating MCP servers into existing systems that already have established token-based authentication mechanisms.
The most common and flexible approach is to point the verifier at a **JSON Web Key Set (JWKS)** endpoint. This allows your identity provider to rotate signing keys automatically without requiring you to update your server's configuration.
### The Token Verification Model
<CodeGroup>
```python Using a JWKS Endpoint (Recommended)
Token verification treats your MCP server as a resource server in OAuth terminology. The key insight is that token validation and token issuance are separate concerns that can be handled by different systems.
**Token Issuance**: Another system (API gateway, authentication service, or identity provider) handles user authentication and creates signed tokens containing identity and permission information.
**Token Validation**: Your MCP server receives these tokens, verifies their authenticity using cryptographic signatures, and extracts authorization information from their claims.
**Access Control**: Based on token contents, your server determines what resources, tools, and prompts the client can access.
This separation allows your MCP server to focus on its core functionality while leveraging existing authentication infrastructure. The token acts as a portable proof of identity that travels with each request.
### Token Security Considerations
Token-based authentication relies on cryptographic signatures to ensure token integrity. Your MCP server validates tokens using public keys corresponding to the private keys used for token creation. This asymmetric approach means your server never needs access to signing secrets.
Token validation must address several security requirements: signature verification ensures tokens haven't been tampered with, expiration checking prevents use of stale tokens, and audience validation ensures tokens intended for your server aren't accepted by other systems.
The challenge in MCP environments is that clients need to obtain valid tokens before making requests, but the MCP protocol doesn't provide built-in discovery mechanisms for token endpoints. Clients must obtain tokens through separate channels or prior configuration.
## FastMCP Token Verification
FastMCP provides the `TokenVerifier` class to handle token validation complexity while remaining flexible about token sources and validation strategies.
### TokenVerifier Design
`TokenVerifier` focuses exclusively on token validation without providing OAuth discovery metadata. This makes it ideal for internal systems where clients already know how to obtain tokens, or for microservices that trust tokens from known issuers.
The class validates token signatures, checks expiration timestamps, and extracts authorization information from token claims. It supports various token formats and validation strategies while maintaining a consistent interface for authorization decisions.
You can subclass `TokenVerifier` to implement custom validation logic for specialized token formats or validation requirements. The base class handles common patterns while allowing extension for unique use cases.
### JWT Token Verification
JSON Web Tokens (JWTs) represent the most common token format for modern applications. FastMCP's `JWTVerifier` validates JWTs using industry-standard cryptographic techniques and claim validation.
#### JWKS Endpoint Integration
JWKS endpoint integration provides the most flexible approach for production systems. The verifier automatically fetches public keys from a JSON Web Key Set endpoint, enabling automatic key rotation without server configuration changes.
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier
# The verifier will periodically fetch keys from this URL
# to validate incoming tokens.
# Configure JWT verification against your identity provider
verifier = JWTVerifier(
jwks_uri="https://my-identity-provider.com/.well-known/jwks.json",
issuer="https://my-identity-provider.com/",
audience="my-mcp-server-identifier"
jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
issuer="https://auth.yourcompany.com",
audience="mcp-production-api"
)
mcp = FastMCP(name="My Secure Server", auth=verifier)
mcp = FastMCP(name="Protected API", auth=verifier)
```
```python Using a Static Public Key
This configuration creates a server that validates JWTs issued by `auth.yourcompany.com`. The verifier periodically fetches public keys from the JWKS endpoint and validates incoming tokens against those keys. Only tokens with the correct issuer and audience claims will be accepted.
The `issuer` parameter ensures tokens come from your trusted authentication system, while `audience` validation prevents tokens intended for other services from being accepted by your MCP server.
#### Static Public Key Verification
Static public key verification works when you have a fixed signing key and don't need automatic key rotation. This approach simplifies deployment in environments where JWKS endpoints aren't available.
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier
# This public key corresponds to the private key used by your token issuer.
# Use this only if a JWKS endpoint is not available.
# Use a static public key for token verification
public_key_pem = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy...
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""
verifier = JWTVerifier(
public_key=public_key_pem,
issuer="https://my-identity-provider.com/",
audience="my-mcp-server-identifier"
issuer="https://auth.yourcompany.com",
audience="mcp-production-api"
)
mcp = FastMCP(name="My Secure Server", auth=verifier)
```
</CodeGroup>
<Card icon="code" title="JWTVerifier Constructor">
<ResponseField name="JWTVerifier" type="class">
<Expandable title="Parameters">
<ResponseField name="public_key" type="str | None">
PEM-encoded public key for JWT verification. Use this for static public key configuration. Cannot be used together with `jwks_uri`.
</ResponseField>
<ResponseField name="jwks_uri" type="str | None">
URI to fetch JSON Web Key Set (JWKS) for automatic key rotation. Cannot be used together with `public_key`.
</ResponseField>
<ResponseField name="issuer" type="str | None">
Expected JWT issuer claim (`iss`) for validation. If provided, tokens must have a matching issuer.
</ResponseField>
<ResponseField name="audience" type="str | list[str] | None">
Expected JWT audience claim (`aud`) for validation. Can be a single string or list of accepted audiences.
</ResponseField>
<ResponseField name="algorithm" type="str | None" default="RS256">
JWT signing algorithm (e.g., RS256, HS256, ES256, PS256)
</ResponseField>
<ResponseField name="required_scopes" type="list[str] | None">
List of scopes that all tokens must have. Tokens missing any required scope will be rejected.
</ResponseField>
<ResponseField name="resource_server_url" type="str | None">
Resource server URL for OAuth protocol compliance
</ResponseField>
</Expandable>
</ResponseField>
</Card>
### Environment Variable Configuration
You can configure the `JWTVerifier` entirely through environment variables. Set `FASTMCP_SERVER_AUTH=JWT` to enable automatic configuration, then provide the verifier's settings.
Example configuration:
```bash
export FASTMCP_SERVER_AUTH=JWT
export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://your-idp.com/.well-known/jwks.json"
export FASTMCP_SERVER_AUTH_JWT_ISSUER="https://your-idp.com"
export FASTMCP_SERVER_AUTH_JWT_AUDIENCE="your-server-id"
mcp = FastMCP(name="Protected API", auth=verifier)
```
Your FastMCP server will now be automatically configured with JWT verification:
```python
from fastmcp import FastMCP
This configuration validates tokens using a specific public key. The key must correspond to the private key used by your token issuer. While less flexible than JWKS endpoints, this approach works well for controlled environments or when using dedicated signing keys.
# This server is automatically protected with JWT verification
# based on the environment variables.
mcp = FastMCP(name="My Protected Server")
```
### Development and Testing
<Expandable title="JWTVerifier Environment Variables">
<ParamField body="FASTMCP_SERVER_AUTH_JWT_PUBLIC_KEY" type="str">
PEM-encoded public key for JWT verification. Use this for static public key configuration.
</ParamField>
Development environments often need simpler token management without the complexity of full JWT infrastructure. FastMCP provides tools specifically designed for these scenarios.
<ParamField body="FASTMCP_SERVER_AUTH_JWT_JWKS_URI" type="str">
URI to fetch JSON Web Key Set (JWKS). Use this for automatic key rotation support.
</ParamField>
#### Static Token Verification
<ParamField body="FASTMCP_SERVER_AUTH_JWT_ISSUER" type="str">
Expected JWT issuer claim for validation
</ParamField>
<ParamField body="FASTMCP_SERVER_AUTH_JWT_AUDIENCE" type="str">
Expected JWT audience claim for validation
</ParamField>
<ParamField body="FASTMCP_SERVER_AUTH_JWT_ALGORITHM" type="str" default="RS256">
JWT signing algorithm (e.g., RS256, HS256, ES256)
</ParamField>
<ParamField body="FASTMCP_SERVER_AUTH_JWT_REQUIRED_SCOPES" type="str">
Comma-separated list of required scopes that all tokens must have
</ParamField>
<ParamField body="FASTMCP_SERVER_AUTH_JWT_RESOURCE_SERVER_URL" type="str">
Resource server URL for OAuth protocol compliance
</ParamField>
</Expandable>
## Development Tools
For local development and testing, managing JWTs can be cumbersome. FastMCP provides simpler tools for these scenarios.
### Static Token Verification
The `StaticTokenVerifier` validates tokens against a hardcoded set of tokens and claims. It's perfect for quickly getting a secure server running in your local environment for testing or prototyping without the complexity of a real identity provider.
<Warning>
**Never use static token verification in production.** Tokens are stored as plain text strings, which is highly insecure.
</Warning>
To use the static token verifier, you need to provide a dictionary of tokens and their claims. Each key of the dictionary is a token, and the value is a dictionary of token data. Note that the tokens will be stored as plain text strings and validated as-is. For example, the following configuration would recognize a token provided in the `Authorization` header as `Bearer dev-token-for-alice` and load `alice@example.com` as the client ID:
Static token verification enables rapid development by accepting predefined tokens with associated claims. This approach eliminates the need for token generation infrastructure during development and testing.
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import StaticTokenVerifier
# Define development tokens and their associated claims
verifier = StaticTokenVerifier(
tokens={
"dev-token-for-alice": {
"client_id": "alice@example.com",
"scopes": ["read:data", "write:data"]
"dev-alice-token": {
"client_id": "alice@company.com",
"scopes": ["read:data", "write:data", "admin:users"]
},
"readonly-token-for-guest": {
"dev-guest-token": {
"client_id": "guest-user",
"scopes": ["read:data"]
}
},
required_scopes=["read:data"] # Optionally enforce a base scope for all tokens.
required_scopes=["read:data"]
)
mcp = FastMCP(name="Development Server", auth=verifier)
```
### Generating Test Tokens
Clients can now authenticate using `Authorization: Bearer dev-alice-token` headers. The server will recognize the token and load the associated claims for authorization decisions. This approach enables immediate development without external dependencies.
To help test a `JWTVerifier`-protected server, FastMCP includes a simple `RSAKeyPair` utility to generate a key pair and sign your own JWTs. This is for convenience in development and is not intended for production use.
<Warning>
Static token verification stores tokens as plain text and should never be used in production environments. It's designed exclusively for development and testing scenarios.
</Warning>
#### Test Token Generation
Test token generation helps when you need to test JWT verification without setting up complete identity infrastructure. FastMCP includes utilities for generating test key pairs and signed tokens.
```python
from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
# Generate a key pair for testing
key_pair = RSAKeyPair.generate()
# Configure your server with the public key
verifier = JWTVerifier(
public_key=key_pair.public_key,
issuer="https://test.yourcompany.com",
audience="test-mcp-server"
)
# Generate a test token using the private key
test_token = key_pair.create_token(
subject="test-user-123",
issuer="https://test.yourcompany.com",
audience="test-mcp-server",
scopes=["read", "write", "admin"]
)
print(f"Test token: {test_token}")
```
This pattern enables comprehensive testing of JWT validation logic without depending on external token issuers. The generated tokens are cryptographically valid and will pass all standard JWT validation checks.
## Environment Configuration
FastMCP supports both programmatic and environment-based configuration for token verification, enabling flexible deployment across different environments.
Environment-based configuration separates authentication settings from application code, following twelve-factor app principles and simplifying deployment pipelines.
```bash
# Enable JWT verification
export FASTMCP_SERVER_AUTH=JWT
# Configure JWT verification parameters
export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://auth.company.com/.well-known/jwks.json"
export FASTMCP_SERVER_AUTH_JWT_ISSUER="https://auth.company.com"
export FASTMCP_SERVER_AUTH_JWT_AUDIENCE="mcp-production-api"
export FASTMCP_SERVER_AUTH_JWT_REQUIRED_SCOPES="read:data,write:data"
```
With these environment variables configured, your FastMCP server automatically enables JWT verification:
```python
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
# 1. In a secure part of your test setup, generate a key pair.
key_pair = RSAKeyPair.generate()
# 2. Configure your FastMCP server's verifier with the PUBLIC key.
auth_verifier = JWTVerifier(
public_key=key_pair.public_key,
issuer="https://dev.fastmcp.com",
audience="test-server"
)
mcp = FastMCP(name="Test Server", auth=auth_verifier)
# 3. Use the PRIVATE key to create a valid token for your client tests.
test_token = key_pair.create_token(
subject="test-user-123",
issuer="https://dev.fastmcp.com",
audience="test-server",
scopes=["read", "write"]
)
print(f"Generated Test Token:\n{test_token}")
# Authentication automatically configured from environment
mcp = FastMCP(name="Production API")
```
This approach enables the same codebase to run across development, staging, and production environments with different authentication requirements. Development might use static tokens while production uses JWT verification, all controlled through environment configuration.

View file

@ -1,4 +1,4 @@
from .auth import OAuthProvider, TokenVerifier
from .auth import OAuthProvider, TokenVerifier, RemoteAuthProvider
from .providers.jwt import JWTVerifier, StaticTokenVerifier
@ -7,6 +7,7 @@ __all__ = [
"TokenVerifier",
"JWTVerifier",
"StaticTokenVerifier",
"RemoteAuthProvider",
]

View file

@ -1,7 +1,5 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from mcp.server.auth.provider import (
AccessToken,
AuthorizationCode,
@ -11,6 +9,10 @@ from mcp.server.auth.provider import (
from mcp.server.auth.provider import (
TokenVerifier as TokenVerifierProtocol,
)
from mcp.server.auth.routes import (
create_auth_routes,
create_protected_resource_routes,
)
from mcp.server.auth.settings import (
ClientRegistrationOptions,
RevocationOptions,
@ -18,11 +20,8 @@ from mcp.server.auth.settings import (
from pydantic import AnyHttpUrl
from starlette.routing import Route
if TYPE_CHECKING:
pass
class AuthProvider:
class AuthProvider(TokenVerifierProtocol):
"""Base class for all FastMCP authentication providers.
This class provides a unified interface for all authentication providers,
@ -31,9 +30,18 @@ class AuthProvider:
custom authentication routes.
"""
def __init__(self, required_scopes: list[str] | None = None):
"""Initialize the auth provider."""
self.required_scopes: list[str] = required_scopes or []
def __init__(self, resource_server_url: AnyHttpUrl | str | None = None):
"""
Initialize the auth provider.
Args:
resource_server_url: The URL of this resource server. This is used
for RFC 8707 resource indicators, including creating the WWW-Authenticate
header.
"""
if isinstance(resource_server_url, str):
resource_server_url = AnyHttpUrl(resource_server_url)
self.resource_server_url = resource_server_url
async def verify_token(self, token: str) -> AccessToken | None:
"""Verify a bearer token and return access info if valid.
@ -48,22 +56,34 @@ class AuthProvider:
"""
raise NotImplementedError("Subclasses must implement verify_token")
def customize_auth_routes(self, routes: list[Route]) -> list[Route]:
"""Customize authentication routes after standard creation.
def get_routes(self) -> list[Route]:
"""Get the routes for this authentication provider.
This method allows providers to modify or add to the standard OAuth routes.
The default implementation returns the routes unchanged.
Args:
routes: List of standard routes (may be empty for token-only providers)
Each provider is responsible for creating whatever routes it needs:
- TokenVerifier: typically no routes (default implementation)
- RemoteAuthProvider: protected resource metadata routes
- OAuthProvider: full OAuth authorization server routes
- Custom providers: whatever routes they need
Returns:
List of routes (potentially modified or extended)
List of routes for this provider
"""
return routes
return []
def get_resource_metadata_url(self) -> AnyHttpUrl | None:
"""Get the resource metadata URL for RFC 9728 compliance."""
if self.resource_server_url is None:
return None
# Add .well-known path for RFC 9728 compliance
resource_metadata_url = AnyHttpUrl(
str(self.resource_server_url).rstrip("/")
+ "/.well-known/oauth-protected-resource"
)
return resource_metadata_url
class TokenVerifier(AuthProvider, TokenVerifierProtocol):
class TokenVerifier(AuthProvider):
"""Base class for token verifiers (Resource Servers).
This class provides token verification capability without OAuth server functionality.
@ -79,26 +99,71 @@ class TokenVerifier(AuthProvider, TokenVerifierProtocol):
Initialize the token verifier.
Args:
resource_server_url: The URL of this resource server (for RFC 8707 resource indicators)
resource_server_url: The URL of this resource server. This is used
for RFC 8707 resource indicators, including creating the WWW-Authenticate
header.
required_scopes: Scopes that are required for all requests
"""
# Initialize AuthProvider (no args needed)
AuthProvider.__init__(self, required_scopes=required_scopes)
# Handle our own resource_server_url and required_scopes
self.resource_server_url: AnyHttpUrl | None
if resource_server_url is None:
self.resource_server_url = None
elif isinstance(resource_server_url, str):
self.resource_server_url = AnyHttpUrl(resource_server_url)
else:
self.resource_server_url = resource_server_url
super().__init__(resource_server_url=resource_server_url)
self.required_scopes = required_scopes or []
async def verify_token(self, token: str) -> AccessToken | None:
"""Verify a bearer token and return access info if valid."""
raise NotImplementedError("Subclasses must implement verify_token")
class RemoteAuthProvider(AuthProvider):
"""Authentication provider for resource servers that verify tokens from known authorization servers.
This provider composes a TokenVerifier with authorization server metadata to create
standardized OAuth 2.0 Protected Resource endpoints (RFC 9728). Perfect for:
- JWT verification with known issuers
- Remote token introspection services
- Any resource server that knows where its tokens come from
Use this when you have token verification logic and want to advertise
the authorization servers that issue valid tokens.
"""
def __init__(
self,
token_verifier: TokenVerifier,
authorization_servers: list[AnyHttpUrl],
resource_server_url: AnyHttpUrl | str,
):
"""Initialize the remote auth provider.
Args:
token_verifier: TokenVerifier instance for token validation
authorization_servers: List of authorization servers that issue valid tokens
resource_server_url: URL of this resource server. This is used
for RFC 8707 resource indicators, including creating the WWW-Authenticate
header.
"""
super().__init__(resource_server_url=resource_server_url)
self.token_verifier = token_verifier
self.authorization_servers = authorization_servers
async def verify_token(self, token: str) -> AccessToken | None:
"""Verify token using the configured token verifier."""
return await self.token_verifier.verify_token(token)
def get_routes(self) -> list[Route]:
"""Get OAuth routes for this provider.
By default, returns only the standardized OAuth 2.0 Protected Resource routes.
Subclasses can override this method to add additional routes by calling
super().get_routes() and extending the returned list.
"""
assert self.resource_server_url is not None
return create_protected_resource_routes(
resource_url=self.resource_server_url,
authorization_servers=self.authorization_servers,
scopes_supported=self.token_verifier.required_scopes,
)
class OAuthProvider(
AuthProvider,
OAuthAuthorizationServerProvider[AuthorizationCode, RefreshToken, AccessToken],
@ -181,17 +246,33 @@ class OAuthProvider(
"""
return await self.load_access_token(token)
def customize_auth_routes(self, routes: list[Route]) -> list[Route]:
"""Customize OAuth authentication routes after standard creation.
def get_routes(self) -> list[Route]:
"""Get OAuth authorization server routes and optional protected resource routes.
This method allows providers to modify the standard OAuth routes
returned by create_auth_routes. The default implementation returns
the routes unchanged.
Args:
routes: List of standard OAuth routes from create_auth_routes
This method creates the full set of OAuth routes including:
- Standard OAuth authorization server routes (/.well-known/oauth-authorization-server, /authorize, /token, etc.)
- Optional protected resource routes if resource_server_url is configured
Returns:
List of routes (potentially modified)
List of OAuth routes
"""
return routes
# Create standard OAuth authorization server routes
oauth_routes = create_auth_routes(
provider=self,
issuer_url=self.issuer_url,
service_documentation_url=self.service_documentation_url,
client_registration_options=self.client_registration_options,
revocation_options=self.revocation_options,
)
# Add protected resource routes if this server is also acting as a resource server
if self.resource_server_url:
protected_routes = create_protected_resource_routes(
resource_url=self.resource_server_url,
authorization_servers=[self.issuer_url],
scopes_supported=self.required_scopes,
)
oauth_routes.extend(protected_routes)
return oauth_routes

View file

@ -16,7 +16,7 @@ from pydantic import AnyHttpUrl, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing_extensions import TypedDict
from fastmcp.server.auth.auth import TokenVerifier
from fastmcp.server.auth import TokenVerifier
from fastmcp.server.auth.registry import register_provider
from fastmcp.utilities.logging import get_logger
from fastmcp.utilities.types import NotSet, NotSetT

View file

@ -1,15 +1,12 @@
from __future__ import annotations
import httpx
from mcp.server.auth.provider import (
AccessToken,
)
from pydantic import AnyHttpUrl
from pydantic_settings import BaseSettings, SettingsConfigDict
from starlette.responses import JSONResponse
from starlette.routing import BaseRoute, Route
from starlette.routing import Route
from fastmcp.server.auth.auth import AuthProvider, TokenVerifier
from fastmcp.server.auth import RemoteAuthProvider, TokenVerifier
from fastmcp.server.auth.providers.jwt import JWTVerifier
from fastmcp.server.auth.registry import register_provider
from fastmcp.utilities.logging import get_logger
@ -31,7 +28,7 @@ class AuthKitProviderSettings(BaseSettings):
@register_provider("AUTHKIT")
class AuthKitProvider(AuthProvider):
class AuthKitProvider(RemoteAuthProvider):
"""AuthKit metadata provider for DCR (Dynamic Client Registration).
This provider implements AuthKit integration using metadata forwarding
@ -83,8 +80,6 @@ class AuthKitProvider(AuthProvider):
required_scopes: Optional list of scopes to require for all requests
token_verifier: Optional token verifier. If None, creates JWT verifier for AuthKit
"""
super().__init__()
settings = AuthKitProviderSettings.model_validate(
{
k: v
@ -109,19 +104,21 @@ class AuthKitProvider(AuthProvider):
required_scopes=settings.required_scopes,
)
self.token_verifier = token_verifier
# Initialize RemoteAuthProvider with AuthKit as the authorization server
super().__init__(
token_verifier=token_verifier,
authorization_servers=[AnyHttpUrl(self.authkit_domain)],
resource_server_url=self.base_url,
)
async def verify_token(self, token: str) -> AccessToken | None:
"""Verify an AuthKit token using the configured token verifier."""
return await self.token_verifier.verify_token(token)
def get_routes(self) -> list[Route]:
"""Get OAuth routes including AuthKit authorization server metadata forwarding.
def customize_auth_routes(self, routes: list[BaseRoute]) -> list[BaseRoute]:
"""Add AuthKit metadata endpoints.
This adds:
- /.well-known/oauth-authorization-server (forwards AuthKit metadata)
- /.well-known/oauth-protected-resource (returns FastMCP resource info)
This returns the standard protected resource routes plus an authorization server
metadata endpoint that forwards AuthKit's OAuth metadata to clients.
"""
# Get the standard protected resource routes from RemoteAuthProvider
routes = super().get_routes()
async def oauth_authorization_server_metadata(request):
"""Forward AuthKit OAuth authorization server metadata with FastMCP customizations."""
@ -142,29 +139,13 @@ class AuthKitProvider(AuthProvider):
status_code=500,
)
async def oauth_protected_resource_metadata(request):
"""Return FastMCP resource server metadata."""
return JSONResponse(
{
"resource": self.base_url,
"authorization_servers": [self.authkit_domain],
"bearer_methods_supported": ["header"],
}
# Add AuthKit authorization server metadata forwarding
routes.append(
Route(
"/.well-known/oauth-authorization-server",
endpoint=oauth_authorization_server_metadata,
methods=["GET"],
)
routes.extend(
[
Route(
"/.well-known/oauth-authorization-server",
endpoint=oauth_authorization_server_metadata,
methods=["GET"],
),
Route(
"/.well-known/oauth-protected-resource",
endpoint=oauth_protected_resource_metadata,
methods=["GET"],
),
]
)
return routes

View file

@ -11,12 +11,10 @@ from mcp.server.auth.middleware.bearer_auth import (
RequireAuthMiddleware,
)
from mcp.server.auth.provider import TokenVerifier as TokenVerifierProtocol
from mcp.server.auth.routes import create_auth_routes
from mcp.server.lowlevel.server import LifespanResultT
from mcp.server.sse import SseServerTransport
from mcp.server.streamable_http import EventStore
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
from pydantic import AnyHttpUrl
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
@ -25,7 +23,7 @@ from starlette.responses import Response
from starlette.routing import BaseRoute, Mount, Route
from starlette.types import Lifespan, Receive, Scope, Send
from fastmcp.server.auth.auth import AuthProvider, OAuthProvider, TokenVerifier
from fastmcp.server.auth.auth import AuthProvider
from fastmcp.utilities.logging import get_logger
if TYPE_CHECKING:
@ -71,51 +69,6 @@ class RequestContextMiddleware:
await self.app(scope, receive, send)
def setup_auth_middleware_and_routes(
auth: AuthProvider,
) -> tuple[list[Middleware], list[Route], list[str]]:
"""Set up authentication middleware and routes if auth is enabled.
Args:
auth: An AuthProvider for authentication (TokenVerifier or OAuthProvider)
Returns:
Tuple of (middleware, auth_routes, required_scopes)
"""
middleware: list[Middleware] = [
Middleware(
AuthenticationMiddleware,
backend=BearerAuthBackend(cast(TokenVerifierProtocol, auth)),
),
Middleware(AuthContextMiddleware),
]
auth_routes: list[Route] = []
required_scopes: list[str] = auth.required_scopes or []
# Check if it's an OAuthProvider (has OAuth server capability)
if isinstance(auth, OAuthProvider):
# OAuthProvider: create standard OAuth routes first
standard_routes = list(
create_auth_routes(
provider=auth,
issuer_url=auth.issuer_url,
service_documentation_url=auth.service_documentation_url,
client_registration_options=auth.client_registration_options,
revocation_options=auth.revocation_options,
)
)
# Allow provider to customize routes (e.g., for proxy behavior or metadata endpoints)
auth_routes = auth.customize_auth_routes(standard_routes)
else:
# Simple AuthProvider or TokenVerifier: start with empty routes
# Allow provider to add custom routes (e.g., metadata endpoints)
auth_routes = auth.customize_auth_routes([])
return middleware, auth_routes, required_scopes
def create_base_app(
routes: list[BaseRoute],
middleware: list[Middleware],
@ -183,24 +136,27 @@ def create_sse_app(
)
return Response()
# Get auth middleware and routes
# Set up auth if enabled
if auth:
auth_middleware, auth_routes, required_scopes = (
setup_auth_middleware_and_routes(auth)
)
# Create auth middleware
auth_middleware = [
Middleware(
AuthenticationMiddleware,
backend=BearerAuthBackend(auth),
),
Middleware(AuthContextMiddleware),
]
# Get auth routes and scopes
auth_routes = auth.get_routes()
required_scopes = getattr(auth, "required_scopes", None) or []
# Get resource metadata URL for WWW-Authenticate header
resource_metadata_url = auth.get_resource_metadata_url()
server_routes.extend(auth_routes)
server_middleware.extend(auth_middleware)
# Determine resource_metadata_url for TokenVerifier
resource_metadata_url = None
if isinstance(auth, TokenVerifier) and auth.resource_server_url:
# Add .well-known path for RFC 9728 compliance
resource_metadata_url = AnyHttpUrl(
str(auth.resource_server_url).rstrip("/")
+ "/.well-known/oauth-protected-resource"
)
# Auth is enabled, wrap endpoints with RequireAuthMiddleware
server_routes.append(
Route(
@ -328,22 +284,25 @@ def create_streamable_http_app(
# Add StreamableHTTP routes with or without auth
if auth:
auth_middleware, auth_routes, required_scopes = (
setup_auth_middleware_and_routes(auth)
)
# Create auth middleware
auth_middleware = [
Middleware(
AuthenticationMiddleware,
backend=BearerAuthBackend(cast(TokenVerifierProtocol, auth)),
),
Middleware(AuthContextMiddleware),
]
# Get auth routes and scopes
auth_routes = auth.get_routes()
required_scopes = getattr(auth, "required_scopes", None) or []
# Get resource metadata URL for WWW-Authenticate header
resource_metadata_url = auth.get_resource_metadata_url()
server_routes.extend(auth_routes)
server_middleware.extend(auth_middleware)
# Determine resource_metadata_url for TokenVerifier
resource_metadata_url = None
if isinstance(auth, TokenVerifier) and auth.resource_server_url:
# Add .well-known path for RFC 9728 compliance
resource_metadata_url = AnyHttpUrl(
str(auth.resource_server_url).rstrip("/")
+ "/.well-known/oauth-protected-resource"
)
# Auth is enabled, wrap endpoint with RequireAuthMiddleware
server_routes.append(
Mount(

View file

@ -1,189 +0,0 @@
"""Tests for authentication setup in HTTP apps."""
import pytest
from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend
from mcp.server.auth.provider import AccessToken
from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware
from fastmcp.server.auth.providers.in_memory import InMemoryOAuthProvider
from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
from fastmcp.server.http import setup_auth_middleware_and_routes
class TestSetupAuthMiddlewareAndRoutes:
"""Test setup_auth_middleware_and_routes with TokenVerifier providers."""
@pytest.fixture
def jwt_verifier(self) -> JWTVerifier:
"""Create JWTVerifier for testing."""
key_pair = RSAKeyPair.generate()
return JWTVerifier(
public_key=key_pair.public_key,
issuer="https://test.example.com",
audience="https://api.example.com",
required_scopes=["read", "write"],
)
@pytest.fixture
def in_memory_provider(self) -> InMemoryOAuthProvider:
"""Create InMemoryOAuthProvider for testing."""
return InMemoryOAuthProvider(
base_url="https://test.example.com",
required_scopes=["user"],
)
def test_setup_with_jwt_verifier(self, jwt_verifier: JWTVerifier):
"""Test that setup works with JWTVerifier as TokenVerifier."""
middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
jwt_verifier
)
# Should return middleware list
assert isinstance(middleware, list)
assert len(middleware) == 2 # AuthenticationMiddleware + AuthContextMiddleware
# First middleware should be AuthenticationMiddleware with BearerAuthBackend
auth_middleware = middleware[0]
assert isinstance(auth_middleware, Middleware)
assert auth_middleware.cls == AuthenticationMiddleware
assert "backend" in auth_middleware.kwargs
backend = auth_middleware.kwargs["backend"]
assert isinstance(backend, BearerAuthBackend)
assert backend.token_verifier is jwt_verifier # type: ignore[attr-defined]
# Should return auth routes
assert isinstance(auth_routes, list)
assert len(auth_routes) == 0 # TokenVerifier should not have OAuth routes
# Should return required scopes
assert required_scopes == ["read", "write"]
def test_setup_with_in_memory_provider(
self, in_memory_provider: InMemoryOAuthProvider
):
"""Test that setup works with InMemoryOAuthProvider as TokenVerifier."""
middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
in_memory_provider
)
# Should return middleware list
assert isinstance(middleware, list)
assert len(middleware) == 2
# Backend should use the provider as token verifier
auth_middleware = middleware[0]
backend = auth_middleware.kwargs["backend"]
assert isinstance(backend, BearerAuthBackend)
assert backend.token_verifier is in_memory_provider # type: ignore[attr-defined]
# Should return required scopes
assert required_scopes == ["user"]
def test_setup_preserves_provider_functionality(self, jwt_verifier: JWTVerifier):
"""Test that setup doesn't break the provider's functionality."""
# Setup should not modify the provider
original_issuer = jwt_verifier.issuer
original_scopes = jwt_verifier.required_scopes
middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
jwt_verifier
)
# Provider should be unchanged
assert jwt_verifier.issuer == original_issuer
assert jwt_verifier.required_scopes == original_scopes
# Provider should still work as TokenVerifier
assert hasattr(jwt_verifier, "verify_token")
assert callable(jwt_verifier.verify_token)
class MockOAuthProvider:
"""Mock OAuth provider that implements TokenVerifier."""
def __init__(self, required_scopes=None, issuer_url="http://localhost:8000"):
from pydantic import AnyHttpUrl
from fastmcp.server.auth.auth import (
ClientRegistrationOptions,
RevocationOptions,
)
self.required_scopes = required_scopes or []
self.issuer_url = AnyHttpUrl(issuer_url)
self.service_documentation_url = None
self.client_registration_options = ClientRegistrationOptions(enabled=False)
self.revocation_options = RevocationOptions(enabled=False)
async def verify_token(self, token: str) -> AccessToken | None:
"""Mock verify_token implementation."""
if token == "valid-token":
return AccessToken(
token=token,
client_id="mock-client",
scopes=self.required_scopes,
expires_at=None,
)
return None
def customize_auth_routes(self, routes):
"""Mock customize_auth_routes implementation."""
return routes
class TestSetupWithMockProvider:
"""Test setup function with mock provider."""
def test_setup_with_mock_token_verifier(self):
"""Test that setup works with any TokenVerifier implementation."""
mock_provider = MockOAuthProvider(required_scopes=["mock-scope"])
middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
mock_provider # type: ignore[arg-type]
)
# Should work with any TokenVerifier
assert len(middleware) == 2
auth_middleware = middleware[0]
backend = auth_middleware.kwargs["backend"]
assert isinstance(backend, BearerAuthBackend)
assert backend.token_verifier is mock_provider # type: ignore[attr-defined]
assert required_scopes == ["mock-scope"]
async def test_setup_middleware_can_authenticate(self):
"""Test that the setup middleware can actually authenticate requests."""
mock_provider = MockOAuthProvider()
middleware, _, _ = setup_auth_middleware_and_routes(mock_provider) # type: ignore[arg-type]
# Extract the BearerAuthBackend
auth_middleware = middleware[0]
backend = auth_middleware.kwargs["backend"]
# Test authentication with valid token
from starlette.requests import HTTPConnection
scope = {
"type": "http",
"headers": [(b"authorization", b"Bearer valid-token")],
}
conn = HTTPConnection(scope)
result = await backend.authenticate(conn) # type: ignore[attr-defined]
assert result is not None
credentials, user = result
assert user.username == "mock-client"
# Test authentication with invalid token
scope = {
"type": "http",
"headers": [(b"authorization", b"Bearer invalid-token")],
}
conn = HTTPConnection(scope)
result = await backend.authenticate(conn) # type: ignore[attr-defined]
assert result is None