Jeremiah Lowin commited on
Commit
8c0df0f
·
unverified ·
1 Parent(s): fb1f4aa

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

Browse files
docs/servers/auth/authentication.mdx CHANGED
@@ -10,159 +10,184 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
10
 
11
  <VersionBadge version="2.11.0" />
12
 
 
 
13
  <Tip>
14
- 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.
15
  </Tip>
16
 
17
- 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.
18
-
19
  <Warning>
20
  **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.
21
  </Warning>
22
 
23
- ## Authentication Patterns
24
 
25
- 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.
26
 
27
- Think of it as a spectrum:
28
 
29
- - **No responsibility:** Your server has no authentication *(none)*
30
- - **Minimal responsibility:** Your server only validates tokens issued elsewhere *(RS)*
31
- - **Moderate responsibility:** Your server coordinates with external identity providers *(RS + remote AS)* — **recommended for most users**
32
- - **Full responsibility:** Your server handles the entire authentication lifecycle *(RS + AS)*
33
 
34
- ### Unauthenticated
35
 
36
- 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.
37
 
38
- **Use this when:**
39
- - Building development or testing environments
40
- - Creating internal tools where network access controls provide sufficient security
41
- - Prototyping before implementing proper authentication
42
 
43
- <Warning>
44
 
45
- **Security considerations:**
46
- - Only suitable for trusted environments or carefully designed public APIs
47
- - Consider network-level security (VPNs, firewalls, private networks)
48
- - Exercise extreme caution when exposing unauthenticated servers to the public internet
49
- - Ensure any public endpoints only expose non-sensitive data or operations
50
- </Warning>
51
 
52
- ### Token Verification
53
 
54
- 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).
55
 
56
- <Note>
57
- **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.
58
- </Note>
59
 
60
- **Use this when:**
61
- - You just need to validate tokens issued by another system
62
- - Building internal microservices that trust a central auth service
63
- - Working with static, long-lived API keys
64
- - You control both the token issuer and your FastMCP server
65
 
66
- **Responsibilities you're taking on:**
67
- - Token validation logic
68
- - Ensuring tokens are securely transmitted to your server
69
- - Managing token lifecycle in your issuing system
70
 
71
- ### Remote OAuth
72
 
73
- 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).
74
 
75
- **Use this when:**
76
- - You want to integrate with external identity providers
77
- - Building user-facing applications that need SSO
78
- - You want enterprise-grade authentication without building it yourself
79
- - You need features like multi-factor authentication, social logins, or directory sync
80
 
81
- **Responsibilities you're taking on:**
82
- - Configuring your server to trust the external provider
83
- - Token validation using the provider's public keys
84
- - Mapping token claims to your application's user model
85
 
86
- **What the external provider handles:**
87
- - User login and consent flows
88
- - Token issuance and management
89
- - User account management
90
- - Security features like MFA and fraud detection
91
 
92
- ### Full OAuth Server
93
 
94
- <Warning>
95
- **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.
96
- </Warning>
97
 
98
- 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).
99
 
100
- **Use this when:**
101
- - Building a completely standalone application with its own user database
102
- - You need full control over every aspect of authentication
103
- - Prototyping OAuth flows locally without external dependencies
104
- - You have the security expertise to implement OAuth securely
105
 
106
- **Responsibilities you're taking on:**
107
- - Secure user credential storage and verification
108
- - OAuth flow implementation
109
- - Token lifecycle management
110
- - Security measures like rate limiting and attack prevention
111
- - User consent and account management interfaces
112
- - Ongoing security updates and compliance
113
 
114
- ## Configuring Authentication
115
 
116
- 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.
117
 
118
- <CodeGroup>
119
 
 
120
 
121
- ```python Token Verification
122
  from fastmcp import FastMCP
123
  from fastmcp.server.auth.providers.jwt import JWTVerifier
124
 
125
- jwt_verifier = JWTVerifier(...)
 
 
 
 
126
 
127
- mcp = FastMCP(name="My Server", auth=jwt_verifier)
128
  ```
129
 
130
- ```python Remote OAuth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  from fastmcp import FastMCP
132
  from fastmcp.server.auth.providers.workos import AuthKitProvider
133
 
134
- auth_provider = AuthKitProvider(...)
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- mcp = FastMCP(name="My Server", auth=auth_provider)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  ```
138
- </CodeGroup>
139
 
140
- ### Environment Variables
 
 
 
 
 
 
141
 
142
- For providers that support it, you can configure authentication entirely through environment variables.
143
 
144
- There are two steps to this process:
145
 
146
- 1. Set `FASTMCP_SERVER_AUTH` to the registered name of your provider. For example, `JWT` for the `JWTVerifier` or `AUTHKIT` for the `AuthKitProvider`.
147
- 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.
148
 
149
- For example, to configure a JWT verifier, you would set:
 
 
 
 
 
 
150
 
151
  ```bash
152
  export FASTMCP_SERVER_AUTH=JWT
153
- export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://your-idp.com/.well-known/jwks.json"
 
 
154
  ```
155
 
156
- And now your FastMCP server will automatically be configured with the JWT verifier:
157
 
158
  ```python
159
  from fastmcp import FastMCP
160
 
161
- # Assumes the environment variables are set as above
162
- mcp = FastMCP(name="My Protected Server")
163
-
164
- assert mcp.auth is not None
165
- assert mcp.auth.jwks_uri == "https://your-idp.com/.well-known/jwks.json"
166
  ```
167
 
168
- 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.
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  <VersionBadge version="2.11.0" />
12
 
13
+ 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.
14
+
15
  <Tip>
16
+ Authentication applies only to FastMCP's HTTP-based transports (`http` and `sse`). The STDIO transport inherits security from its local execution environment.
17
  </Tip>
18
 
 
 
19
  <Warning>
20
  **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.
21
  </Warning>
22
 
23
+ ## MCP Authentication Challenges
24
 
25
+ 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:
26
 
27
+ **Automatic Discovery**: MCP clients must discover authentication requirements by examining server metadata rather than encountering login redirects.
28
 
29
+ **Programmatic OAuth**: OAuth flows must work without human interaction, relying on pre-configured credentials or Dynamic Client Registration.
 
 
 
30
 
31
+ **Token Management**: Clients need to obtain, refresh, and manage tokens automatically across multiple MCP servers.
32
 
33
+ **Protocol Integration**: Authentication must integrate cleanly with MCP's transport mechanisms and error handling.
34
 
35
+ 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.
 
 
 
36
 
37
+ ## Understanding Authentication Responsibility
38
 
39
+ 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.
 
 
 
 
 
40
 
41
+ ### Token Validation
42
 
43
+ 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.
44
 
45
+ 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.
 
 
46
 
47
+ 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.
 
 
 
 
48
 
49
+ 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.
 
 
 
50
 
51
+ ### External Identity Providers
52
 
53
+ 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.
54
 
55
+ 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.
 
 
 
 
56
 
57
+ 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.
 
 
 
58
 
59
+ 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.
 
 
 
 
60
 
61
+ ### Full OAuth Implementation
62
 
63
+ 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.
 
 
64
 
65
+ 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.
66
 
67
+ 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.
 
 
 
 
68
 
69
+ ## FastMCP Implementation
 
 
 
 
 
 
70
 
71
+ FastMCP translates these authentication responsibility levels into three concrete classes that handle the complexities of MCP protocol integration.
72
 
73
+ ### TokenVerifier
74
 
75
+ `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.
76
 
77
+ 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.
78
 
79
+ ```python
80
  from fastmcp import FastMCP
81
  from fastmcp.server.auth.providers.jwt import JWTVerifier
82
 
83
+ auth = JWTVerifier(
84
+ jwks_uri="https://your-auth-system.com/.well-known/jwks.json",
85
+ issuer="https://your-auth-system.com",
86
+ audience="your-mcp-server"
87
+ )
88
 
89
+ mcp = FastMCP(name="Protected Server", auth=auth)
90
  ```
91
 
92
+ 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.
93
+
94
+ `TokenVerifier` works well when you control both the token issuer and your MCP server, or when integrating with existing JWT-based infrastructure.
95
+
96
+ → **Complete guide**: [Token Verification](/servers/auth/token-verification)
97
+
98
+ ### RemoteAuthProvider
99
+
100
+ `RemoteAuthProvider` combines token validation with OAuth discovery metadata, enabling MCP clients to automatically discover and authenticate with external identity providers.
101
+
102
+ 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.
103
+
104
+ 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.
105
+
106
+ ```python
107
  from fastmcp import FastMCP
108
  from fastmcp.server.auth.providers.workos import AuthKitProvider
109
 
110
+ auth = AuthKitProvider(
111
+ authkit_domain="https://your-project.authkit.app",
112
+ base_url="https://your-fastmcp-server.com"
113
+ )
114
+
115
+ mcp = FastMCP(name="Enterprise Server", auth=auth)
116
+ ```
117
+
118
+ 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.
119
+
120
+ `RemoteAuthProvider` excels for production applications that need professional identity management without implementation complexity.
121
+
122
+ → **Complete guide**: [Remote OAuth](/servers/auth/remote-oauth)
123
 
124
+ ### OAuthProvider
125
+
126
+ `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.
127
+
128
+ 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.
129
+
130
+ ```python
131
+ from fastmcp import FastMCP
132
+ from fastmcp.server.auth.providers.oauth import MyOAuthProvider
133
+
134
+ auth = MyOAuthProvider(
135
+ user_store=your_user_database,
136
+ client_store=your_client_registry,
137
+ # Additional configuration...
138
+ )
139
+
140
+ mcp = FastMCP(name="Auth Server", auth=auth)
141
  ```
 
142
 
143
+ 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.
144
+
145
+ `OAuthProvider` should be used only when you have specific requirements that external providers cannot meet and the expertise to implement OAuth securely.
146
+
147
+ → **Complete guide**: [Full OAuth Server](/servers/auth/full-oauth-server)
148
+
149
+ ## Configuration Approaches
150
 
151
+ FastMCP supports both programmatic configuration for maximum flexibility and environment-based configuration for deployment simplicity.
152
 
153
+ ### Programmatic Configuration
154
 
155
+ 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.
 
156
 
157
+ 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.
158
+
159
+ ### Environment Configuration
160
+
161
+ Environment-based configuration separates authentication settings from application code, enabling the same codebase to work across different deployment environments without modification.
162
+
163
+ 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.
164
 
165
  ```bash
166
  export FASTMCP_SERVER_AUTH=JWT
167
+ export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://auth.example.com/jwks"
168
+ export FASTMCP_SERVER_AUTH_JWT_ISSUER="https://auth.example.com"
169
+ export FASTMCP_SERVER_AUTH_JWT_AUDIENCE="mcp-server"
170
  ```
171
 
172
+ With these environment variables set, creating an authenticated FastMCP server requires no additional configuration:
173
 
174
  ```python
175
  from fastmcp import FastMCP
176
 
177
+ # Authentication automatically configured from environment
178
+ mcp = FastMCP(name="My Server")
 
 
 
179
  ```
180
 
181
+ This approach simplifies deployment pipelines and follows twelve-factor app principles for configuration management.
182
+
183
+ ## Choosing Your Implementation
184
+
185
+ The authentication approach you choose depends on your existing infrastructure, security requirements, and operational constraints.
186
+
187
+ **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.
188
+
189
+ **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.
190
+
191
+ **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.
192
+
193
+ 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.
docs/servers/auth/full-oauth-server.mdx CHANGED
@@ -11,119 +11,219 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
11
  <VersionBadge version="2.11.0" />
12
 
13
  <Warning>
14
- **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.
15
 
16
- 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.
17
  </Warning>
18
 
19
- 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:
20
-
21
- - **User Management**: Storing user credentials, profiles, and permissions
22
- - **Client Registration**: Managing MCP client applications and their credentials
23
- - **Authentication Flow**: Handling login pages, multi-factor authentication, and user consent
24
- - **Token Lifecycle**: Issuing, refreshing, and revoking access tokens
25
- - **Security Controls**: Rate limiting, audit logging, and threat detection
26
-
27
- 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.
28
-
29
- ## Building an OAuth Provider
30
-
31
- 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.
32
-
33
- ```python
34
- from fastmcp.server.auth.auth import OAuthProvider
35
- from mcp.server.auth.provider import (
36
- AccessToken, AuthorizationCode, RefreshToken, AuthorizationParams
37
- )
38
- from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
39
-
40
- class MyOAuthProvider(OAuthProvider):
41
- """
42
- A production OAuth provider implementation.
43
-
44
- WARNING: This is a simplified example. A real implementation
45
- requires extensive security considerations, persistent storage,
46
- proper error handling, and adherence to OAuth 2.1 security
47
- best practices.
48
- """
49
-
50
- def __init__(self, base_url: str):
51
- super().__init__(base_url=base_url)
52
- # Initialize your database connections, cryptographic keys, etc.
53
-
54
- # === Client Management ===
55
- async def get_client(self, client_id: str) -> OAuthClientInformationFull | None:
56
- """Retrieve client information by ID from your database."""
57
- # Query your client database and return client info or None if not found
58
- raise NotImplementedError
59
-
60
- async def register_client(self, client_info: OAuthClientInformationFull) -> None:
61
- """Store new client registration information."""
62
- # Validate and save client metadata to your database
63
- # May raise RegistrationError if client data is invalid
64
- raise NotImplementedError
65
-
66
- # === Authorization Flow ===
67
- async def authorize(
68
- self, client: OAuthClientInformationFull, params: AuthorizationParams
69
- ) -> str:
70
- """
71
- Handle authorization request and return redirect URL.
72
-
73
- Many implementations redirect to a third-party OAuth provider, creating
74
- a chain: Client -> MCP Server -> External IdP -> MCP Server -> Client.
75
- You must generate an authorization code with at least 128 bits of entropy.
76
- """
77
- # Authenticate user, get consent, generate auth code, return redirect URL
78
- raise NotImplementedError
79
-
80
- async def load_authorization_code(
81
- self, client: OAuthClientInformationFull, authorization_code: str
82
- ) -> AuthorizationCode | None:
83
- """Load authorization code from storage by code string."""
84
- # Look up stored authorization code, return None if not found or expired
85
- raise NotImplementedError
86
-
87
- # === Token Management ===
88
- async def exchange_authorization_code(
89
- self, client: OAuthClientInformationFull, authorization_code: AuthorizationCode
90
- ) -> OAuthToken:
91
- """Exchange authorization code for access and refresh tokens."""
92
- # Validate code, generate new token pair, invalidate the auth code
93
- raise NotImplementedError
94
-
95
- async def load_refresh_token(
96
- self, client: OAuthClientInformationFull, refresh_token: str
97
- ) -> RefreshToken | None:
98
- """Load refresh token from storage by token string."""
99
- # Look up refresh token, return None if not found or expired
100
- raise NotImplementedError
101
-
102
- async def exchange_refresh_token(
103
- self,
104
- client: OAuthClientInformationFull,
105
- refresh_token: RefreshToken,
106
- scopes: list[str]
107
- ) -> OAuthToken:
108
- """Exchange refresh token for new access/refresh token pair."""
109
- # Should rotate both tokens for security best practices
110
- raise NotImplementedError
111
-
112
- async def load_access_token(self, token: str) -> AccessToken | None:
113
- """Load and validate access token - called on every protected request."""
114
- # Look up token, check expiration, return None if invalid
115
- raise NotImplementedError
116
-
117
- async def revoke_token(self, token: AccessToken | RefreshToken) -> None:
118
- """Revoke access or refresh token."""
119
- # Should revoke both access and refresh tokens regardless of which is provided
120
- # Do nothing if token is already invalid or revoked
121
- raise NotImplementedError
122
-
123
- # === Token Verification (AuthProvider interface) ===
124
- async def verify_token(self, token: str) -> AccessToken | None:
125
- """Verify bearer token for incoming requests."""
126
- # This is called on every protected MCP request
127
- # Typically delegates to load_access_token
128
- return await self.load_access_token(token)
129
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  <VersionBadge version="2.11.0" />
12
 
13
  <Warning>
14
+ **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.
15
 
16
+ **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.
17
  </Warning>
18
 
19
+ 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.
20
+
21
+ This documentation exists for completeness - the vast majority of applications should use external identity providers instead.
22
+
23
+ ## OAuthProvider
24
+
25
+ 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.
26
+
27
+ <Note>
28
+ `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.
29
+ </Note>
30
+
31
+ ## Required Implementation
32
+
33
+ You must implement these abstract methods to create a functioning OAuth server:
34
+
35
+ ### Client Management
36
+
37
+ <Card icon="code" title="Client Management Methods">
38
+ <ParamField body="get_client" type="async method">
39
+ Retrieve client information by ID from your database.
40
+
41
+ <Expandable title="Parameters">
42
+ <ParamField body="client_id" type="str">
43
+ Client identifier to look up
44
+ </ParamField>
45
+ </Expandable>
46
+
47
+ <Expandable title="Returns">
48
+ <ParamField body="OAuthClientInformationFull | None" type="return type">
49
+ Client information object or `None` if client not found
50
+ </ParamField>
51
+ </Expandable>
52
+ </ParamField>
53
+
54
+ <ParamField body="register_client" type="async method">
55
+ Store new client registration information in your database.
56
+
57
+ <Expandable title="Parameters">
58
+ <ParamField body="client_info" type="OAuthClientInformationFull">
59
+ Complete client registration information to store
60
+ </ParamField>
61
+ </Expandable>
62
+
63
+ <Expandable title="Returns">
64
+ <ParamField body="None" type="return type">
65
+ No return value
66
+ </ParamField>
67
+ </Expandable>
68
+ </ParamField>
69
+ </Card>
70
+
71
+ ### Authorization Flow
72
+
73
+ <Card icon="code" title="Authorization Flow Methods">
74
+ <ParamField body="authorize" type="async method">
75
+ Handle authorization request and return redirect URL. Must implement user authentication and consent collection.
76
+
77
+ <Expandable title="Parameters">
78
+ <ParamField body="client" type="OAuthClientInformationFull">
79
+ OAuth client making the authorization request
80
+ </ParamField>
81
+ <ParamField body="params" type="AuthorizationParams">
82
+ Authorization request parameters from the client
83
+ </ParamField>
84
+ </Expandable>
85
+
86
+ <Expandable title="Returns">
87
+ <ParamField body="str" type="return type">
88
+ Redirect URL to send the client to
89
+ </ParamField>
90
+ </Expandable>
91
+ </ParamField>
92
+
93
+ <ParamField body="load_authorization_code" type="async method">
94
+ Load authorization code from storage by code string. Return `None` if code is invalid or expired.
95
+
96
+ <Expandable title="Parameters">
97
+ <ParamField body="client" type="OAuthClientInformationFull">
98
+ OAuth client attempting to use the authorization code
99
+ </ParamField>
100
+ <ParamField body="authorization_code" type="str">
101
+ Authorization code string to look up
102
+ </ParamField>
103
+ </Expandable>
104
+
105
+ <Expandable title="Returns">
106
+ <ParamField body="AuthorizationCode | None" type="return type">
107
+ Authorization code object or `None` if not found
108
+ </ParamField>
109
+ </Expandable>
110
+ </ParamField>
111
+ </Card>
112
+
113
+ ### Token Management
114
+
115
+ <Card icon="code" title="Token Management Methods">
116
+ <ParamField body="exchange_authorization_code" type="async method">
117
+ Exchange authorization code for access and refresh tokens. Must validate code and create new tokens.
118
+
119
+ <Expandable title="Parameters">
120
+ <ParamField body="client" type="OAuthClientInformationFull">
121
+ OAuth client exchanging the authorization code
122
+ </ParamField>
123
+ <ParamField body="authorization_code" type="AuthorizationCode">
124
+ Valid authorization code object to exchange
125
+ </ParamField>
126
+ </Expandable>
127
+
128
+ <Expandable title="Returns">
129
+ <ParamField body="OAuthToken" type="return type">
130
+ New OAuth token containing access and refresh tokens
131
+ </ParamField>
132
+ </Expandable>
133
+ </ParamField>
134
+
135
+ <ParamField body="load_refresh_token" type="async method">
136
+ Load refresh token from storage by token string. Return `None` if token is invalid or expired.
137
+
138
+ <Expandable title="Parameters">
139
+ <ParamField body="client" type="OAuthClientInformationFull">
140
+ OAuth client attempting to use the refresh token
141
+ </ParamField>
142
+ <ParamField body="refresh_token" type="str">
143
+ Refresh token string to look up
144
+ </ParamField>
145
+ </Expandable>
146
+
147
+ <Expandable title="Returns">
148
+ <ParamField body="RefreshToken | None" type="return type">
149
+ Refresh token object or `None` if not found
150
+ </ParamField>
151
+ </Expandable>
152
+ </ParamField>
153
+
154
+ <ParamField body="exchange_refresh_token" type="async method">
155
+ Exchange refresh token for new access/refresh token pair. Must validate scopes and token.
156
+
157
+ <Expandable title="Parameters">
158
+ <ParamField body="client" type="OAuthClientInformationFull">
159
+ OAuth client using the refresh token
160
+ </ParamField>
161
+ <ParamField body="refresh_token" type="RefreshToken">
162
+ Valid refresh token object to exchange
163
+ </ParamField>
164
+ <ParamField body="scopes" type="list[str]">
165
+ Requested scopes for the new access token
166
+ </ParamField>
167
+ </Expandable>
168
+
169
+ <Expandable title="Returns">
170
+ <ParamField body="OAuthToken" type="return type">
171
+ New OAuth token with updated access and refresh tokens
172
+ </ParamField>
173
+ </Expandable>
174
+ </ParamField>
175
+
176
+ <ParamField body="load_access_token" type="async method">
177
+ Load an access token by its token string.
178
+
179
+ <Expandable title="Parameters">
180
+ <ParamField body="token" type="str">
181
+ The access token to verify
182
+ </ParamField>
183
+ </Expandable>
184
+
185
+ <Expandable title="Returns">
186
+ <ParamField body="AccessToken | None" type="return type">
187
+ The access token object, or `None` if the token is invalid
188
+ </ParamField>
189
+ </Expandable>
190
+ </ParamField>
191
+
192
+ <ParamField body="revoke_token" type="async method">
193
+ Revoke access or refresh token, marking it as invalid in storage.
194
+
195
+ <Expandable title="Parameters">
196
+ <ParamField body="token" type="AccessToken | RefreshToken">
197
+ Token object to revoke and mark invalid
198
+ </ParamField>
199
+ </Expandable>
200
+
201
+ <Expandable title="Returns">
202
+ <ParamField body="None" type="return type">
203
+ No return value
204
+ </ParamField>
205
+ </Expandable>
206
+ </ParamField>
207
+
208
+ <ParamField body="verify_token" type="async method">
209
+ Verify bearer token for incoming requests. Return `AccessToken` if valid, `None` if invalid.
210
+
211
+ <Expandable title="Parameters">
212
+ <ParamField body="token" type="str">
213
+ Bearer token string from incoming request
214
+ </ParamField>
215
+ </Expandable>
216
+
217
+ <Expandable title="Returns">
218
+ <ParamField body="AccessToken | None" type="return type">
219
+ Access token object if valid, `None` if invalid or expired
220
+ </ParamField>
221
+ </Expandable>
222
+ </ParamField>
223
+ </Card>
224
+
225
+ 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.
226
+
227
+ <Warning>
228
+ **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.
229
+ </Warning>
docs/servers/auth/remote-oauth.mdx CHANGED
@@ -1,7 +1,7 @@
1
  ---
2
  title: Remote OAuth
3
  sidebarTitle: Remote OAuth
4
- description: Integrate with external identity providers like WorkOS, Auth0, or Okta by trusting them to handle user authentication.
5
  icon: camera-cctv
6
  tag: NEW
7
  ---
@@ -10,138 +10,180 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
10
 
11
  <VersionBadge version="2.11.0" />
12
 
13
- **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.
14
 
15
- 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.
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- ### How It Works
18
 
19
- 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.
20
 
21
- 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.
 
 
 
 
 
 
22
 
23
  ```mermaid
24
  sequenceDiagram
25
  participant Client
26
- participant FastMCPServer as FastMCP (RS)
27
- participant ExternalIdP as External IdP (AS)
28
 
29
  Client->>FastMCPServer: 1. GET /.well-known/oauth-protected-resource
30
  FastMCPServer-->>Client: 2. "Use https://my-idp.com for auth"
31
 
32
  note over Client, ExternalIdP: Client goes directly to the IdP
33
- Client->>ExternalIdP: 3. GET /.well-known/oauth-authorization-server
34
- ExternalIdP-->>Client: 4. OAuth endpoints & capabilities
35
 
36
- Client->>ExternalIdP: 5. User authenticates & gets token
37
- ExternalIdP-->>Client:
38
-
39
- Client->>FastMCPServer: 6. MCP request with Bearer token
40
- note right of FastMCPServer: Server verifies the token
41
- FastMCPServer->>FastMCPServer: 7. Verify token signature <br/> (using IdP's public keys)
42
- FastMCPServer-->>Client: 8. MCP Response
43
  ```
44
 
45
- ## Building a Custom Provider
 
 
 
 
46
 
47
- To connect to any identity provider, you create a custom `AuthProvider` subclass. This class has two main responsibilities:
48
 
49
- 1. **Verifying Tokens:** Validate tokens issued by the external provider.
50
- 2. **Forwarding Metadata:** Tell MCP clients where to find the external provider's login pages and token endpoints.
51
 
52
- ### Step 1: Verifying Tokens
53
 
54
- 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`.
 
 
 
 
 
 
55
 
56
  ```python
57
- from fastmcp.server.auth.auth import AuthProvider
 
58
  from fastmcp.server.auth.providers.jwt import JWTVerifier
59
- from mcp.server.auth.provider import AccessToken
60
 
61
- class MyIdPAuthProvider(AuthProvider):
62
- def __init__(self):
63
- super().__init__()
64
- # The verifier validates tokens from the upstream provider.
65
- self.token_verifier = JWTVerifier(
66
- jwks_uri="https://my-idp.com/.well-known/jwks.json",
67
- issuer="https://my-idp.com",
68
- audience="my-fastmcp-api"
69
- )
 
 
 
 
70
 
71
- async def verify_token(self, token: str) -> AccessToken | None:
72
- return await self.token_verifier.verify_token(token)
73
  ```
74
 
75
- ### Step 2: Adding Discovery Metadata
 
 
 
 
76
 
77
- 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.
78
 
79
  ```python
80
  import httpx
81
  from starlette.responses import JSONResponse
82
  from starlette.routing import Route
83
 
84
- class MyIdPAuthProvider(AuthProvider):
85
- # ... (init and verify_token from above) ...
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- def customize_auth_routes(self, routes: list[Route]) -> list[Route]:
88
- # Essential: Tell clients which authorization server to use
89
- async def protected_resource_metadata(request):
90
- return JSONResponse({
91
- "resource": "https://my-fastmcp-server.com",
92
- "authorization_servers": ["https://my-idp.com"],
93
- "bearer_methods_supported": ["header"],
94
- })
95
 
96
- routes.append(Route("/.well-known/oauth-protected-resource", protected_resource_metadata))
 
97
 
98
- # Optional: Forward the authorization server's metadata for convenience
99
- # (Clients can also fetch this directly from the IdP)
100
  async def authorization_server_metadata(request):
101
  async with httpx.AsyncClient() as client:
102
- resp = await client.get("https://my-idp.com/.well-known/oauth-authorization-server")
103
- resp.raise_for_status()
104
- return JSONResponse(resp.json())
105
-
106
- routes.append(Route("/.well-known/oauth-authorization-server", authorization_server_metadata))
 
 
 
 
 
107
  return routes
 
 
108
  ```
109
 
110
- ### Step 3: Using Your Provider
111
 
112
- 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:
 
 
113
 
114
  ```python
115
  from fastmcp import FastMCP
 
116
 
117
- mcp = FastMCP(name="My Secure Server", auth=MyIdPAuthProvider())
 
 
 
 
 
118
  ```
119
 
 
120
 
121
- ## Example: WorkOS AuthKit Provider
122
 
123
- 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.
124
 
125
- **Prerequisites:**
126
- 1. A WorkOS account with an AuthKit project.
127
- 2. **Dynamic Client Registration (DCR)** must be enabled in your WorkOS application settings.
128
- 3. Your FastMCP server's URL must be added as a **Redirect URI** in your WorkOS project (can be localhost for development).
129
 
130
- ```python
131
- from fastmcp import FastMCP
132
- from fastmcp.server.auth.providers.workos import AuthKitProvider
133
 
134
- # The AuthKitProvider implements both metadata forwarding and token validation.
135
- auth_provider = AuthKitProvider(
136
- # Your unique AuthKit domain from the WorkOS dashboard
137
- authkit_domain="https://your-project.authkit.app",
138
- # The URL of THIS FastMCP server (can be localhost for development)
139
- base_url="https://your-fastmcp-server.com"
140
- )
141
 
142
- mcp = FastMCP(name="My WorkOS-Protected Server", auth=auth_provider)
143
- ```
144
 
145
- <Tip>
146
- For a complete, step-by-step tutorial on using this provider, see the [**WorkOS AuthKit Integration Guide**](/integrations/authkit).
147
- </Tip>
 
 
 
1
  ---
2
  title: Remote OAuth
3
  sidebarTitle: Remote OAuth
4
+ description: Integrate your FastMCP server with external identity providers like WorkOS, Auth0, and corporate SSO systems.
5
  icon: camera-cctv
6
  tag: NEW
7
  ---
 
10
 
11
  <VersionBadge version="2.11.0" />
12
 
13
+ 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.
14
 
15
+ <Tip>
16
+ Remote OAuth requires identity providers that support **Dynamic Client Registration (DCR)**. This enables MCP clients to automatically register and authenticate without manual configuration steps.
17
+ </Tip>
18
+
19
+ ## The Remote OAuth Challenge
20
+
21
+ 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.
22
+
23
+ This creates several unique requirements that standard OAuth implementations don't address well:
24
+
25
+ **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.
26
+
27
+ **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.
28
 
29
+ **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.
30
 
31
+ **Protocol Integration**: The authentication process must integrate cleanly with MCP's JSON-RPC transport layer and error handling mechanisms.
32
 
33
+ 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.
34
+
35
+ ## MCP Authentication Discovery
36
+
37
+ 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.
38
+
39
+ 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.
40
 
41
  ```mermaid
42
  sequenceDiagram
43
  participant Client
44
+ participant FastMCPServer as FastMCP Server
45
+ participant ExternalIdP as Identity Provider
46
 
47
  Client->>FastMCPServer: 1. GET /.well-known/oauth-protected-resource
48
  FastMCPServer-->>Client: 2. "Use https://my-idp.com for auth"
49
 
50
  note over Client, ExternalIdP: Client goes directly to the IdP
51
+ Client->>ExternalIdP: 3. Authenticate & get token via DCR
52
+ ExternalIdP-->>Client: 4. Access token
53
 
54
+ Client->>FastMCPServer: 5. MCP request with Bearer token
55
+ FastMCPServer->>FastMCPServer: 6. Verify token signature
56
+ FastMCPServer-->>Client: 7. MCP response
 
 
 
 
57
  ```
58
 
59
+ 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.
60
+
61
+ ## FastMCP Remote Authentication
62
+
63
+ <VersionBadge version="2.11.1" />
64
 
65
+ 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.
66
 
67
+ ### RemoteAuthProvider
 
68
 
69
+ `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.
70
 
71
+ 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.
72
+
73
+ 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.
74
+
75
+ ### Basic Implementation
76
+
77
+ 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.
78
 
79
  ```python
80
+ from fastmcp import FastMCP
81
+ from fastmcp.server.auth import RemoteAuthProvider
82
  from fastmcp.server.auth.providers.jwt import JWTVerifier
83
+ from pydantic import AnyHttpUrl
84
 
85
+ # Configure token validation for your identity provider
86
+ token_verifier = JWTVerifier(
87
+ jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
88
+ issuer="https://auth.yourcompany.com",
89
+ audience="mcp-production-api"
90
+ )
91
+
92
+ # Create the remote auth provider
93
+ auth = RemoteAuthProvider(
94
+ token_verifier=token_verifier,
95
+ authorization_servers=[AnyHttpUrl("https://auth.yourcompany.com")],
96
+ resource_server_url="https://api.yourcompany.com"
97
+ )
98
 
99
+ mcp = FastMCP(name="Company API", auth=auth)
 
100
  ```
101
 
102
+ 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.
103
+
104
+ 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.
105
+
106
+ ### Custom Endpoints
107
 
108
+ 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.
109
 
110
  ```python
111
  import httpx
112
  from starlette.responses import JSONResponse
113
  from starlette.routing import Route
114
 
115
+ class CompanyAuthProvider(RemoteAuthProvider):
116
+ def __init__(self):
117
+ token_verifier = JWTVerifier(
118
+ jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
119
+ issuer="https://auth.yourcompany.com",
120
+ audience="mcp-production-api"
121
+ )
122
+
123
+ super().__init__(
124
+ token_verifier=token_verifier,
125
+ authorization_servers=[AnyHttpUrl("https://auth.yourcompany.com")],
126
+ resource_server_url="https://api.yourcompany.com"
127
+ )
128
 
129
+ def get_routes(self) -> list[Route]:
130
+ """Add custom endpoints to the standard protected resource routes."""
 
 
 
 
 
 
131
 
132
+ # Get the standard OAuth protected resource routes
133
+ routes = super().get_routes()
134
 
135
+ # Add authorization server metadata forwarding for client convenience
 
136
  async def authorization_server_metadata(request):
137
  async with httpx.AsyncClient() as client:
138
+ response = await client.get(
139
+ "https://auth.yourcompany.com/.well-known/oauth-authorization-server"
140
+ )
141
+ response.raise_for_status()
142
+ return JSONResponse(response.json())
143
+
144
+ routes.append(
145
+ Route("/.well-known/oauth-authorization-server", authorization_server_metadata)
146
+ )
147
+
148
  return routes
149
+
150
+ mcp = FastMCP(name="Company API", auth=CompanyAuthProvider())
151
  ```
152
 
153
+ 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.
154
 
155
+ ## WorkOS AuthKit Integration
156
+
157
+ 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.
158
 
159
  ```python
160
  from fastmcp import FastMCP
161
+ from fastmcp.server.auth.providers.workos import AuthKitProvider
162
 
163
+ auth = AuthKitProvider(
164
+ authkit_domain="https://your-project.authkit.app",
165
+ base_url="https://your-mcp-server.com"
166
+ )
167
+
168
+ mcp = FastMCP(name="Protected Application", auth=auth)
169
  ```
170
 
171
+ 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.
172
 
173
+ 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.
174
 
175
+ **Complete WorkOS tutorial**: [AuthKit Integration Guide](/integrations/authkit)
176
 
177
+ ## Implementation Considerations
 
 
 
178
 
179
+ Remote OAuth integration requires careful attention to several technical details that affect reliability and security.
 
 
180
 
181
+ **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.
 
 
 
 
 
 
182
 
183
+ **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.
 
184
 
185
+ **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.
186
+
187
+ **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.
188
+
189
+ 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.
docs/servers/auth/token-verification.mdx CHANGED
@@ -1,7 +1,7 @@
1
  ---
2
  title: Token Verification
3
  sidebarTitle: Token Verification
4
- description: Protect your server by validating bearer tokens.
5
  icon: key
6
  tag: NEW
7
  ---
@@ -10,202 +10,191 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
10
 
11
  <VersionBadge version="2.11.0" />
12
 
13
- 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.
14
 
15
- 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.
 
 
16
 
17
- ## JWT Verification
18
 
19
- 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.
20
 
21
- ### Using the JWTVerifier
22
 
23
- 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.
24
 
25
- <CodeGroup>
26
- ```python Using a JWKS Endpoint (Recommended)
27
- from fastmcp import FastMCP
28
- from fastmcp.server.auth.providers.jwt import JWTVerifier
29
 
30
- # The verifier will periodically fetch keys from this URL
31
- # to validate incoming tokens.
32
- verifier = JWTVerifier(
33
- jwks_uri="https://my-identity-provider.com/.well-known/jwks.json",
34
- issuer="https://my-identity-provider.com/",
35
- audience="my-mcp-server-identifier"
36
- )
37
 
38
- mcp = FastMCP(name="My Secure Server", auth=verifier)
39
- ```
40
 
41
- ```python Using a Static Public Key
42
- from fastmcp import FastMCP
43
- from fastmcp.server.auth.providers.jwt import JWTVerifier
44
 
45
- # This public key corresponds to the private key used by your token issuer.
46
- # Use this only if a JWKS endpoint is not available.
47
- public_key_pem = """-----BEGIN PUBLIC KEY-----
48
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy...
49
- -----END PUBLIC KEY-----"""
50
 
51
- verifier = JWTVerifier(
52
- public_key=public_key_pem,
53
- issuer="https://my-identity-provider.com/",
54
- audience="my-mcp-server-identifier"
55
- )
56
 
57
- mcp = FastMCP(name="My Secure Server", auth=verifier)
58
- ```
59
- </CodeGroup>
60
-
61
- <Card icon="code" title="JWTVerifier Constructor">
62
- <ResponseField name="JWTVerifier" type="class">
63
- <Expandable title="Parameters">
64
- <ResponseField name="public_key" type="str | None">
65
- PEM-encoded public key for JWT verification. Use this for static public key configuration. Cannot be used together with `jwks_uri`.
66
- </ResponseField>
67
-
68
- <ResponseField name="jwks_uri" type="str | None">
69
- URI to fetch JSON Web Key Set (JWKS) for automatic key rotation. Cannot be used together with `public_key`.
70
- </ResponseField>
71
-
72
- <ResponseField name="issuer" type="str | None">
73
- Expected JWT issuer claim (`iss`) for validation. If provided, tokens must have a matching issuer.
74
- </ResponseField>
75
-
76
- <ResponseField name="audience" type="str | list[str] | None">
77
- Expected JWT audience claim (`aud`) for validation. Can be a single string or list of accepted audiences.
78
- </ResponseField>
79
-
80
- <ResponseField name="algorithm" type="str | None" default="RS256">
81
- JWT signing algorithm (e.g., RS256, HS256, ES256, PS256)
82
- </ResponseField>
83
-
84
- <ResponseField name="required_scopes" type="list[str] | None">
85
- List of scopes that all tokens must have. Tokens missing any required scope will be rejected.
86
- </ResponseField>
87
-
88
- <ResponseField name="resource_server_url" type="str | None">
89
- Resource server URL for OAuth protocol compliance
90
- </ResponseField>
91
- </Expandable>
92
- </ResponseField>
93
- </Card>
94
-
95
- ### Environment Variable Configuration
96
-
97
- You can configure the `JWTVerifier` entirely through environment variables. Set `FASTMCP_SERVER_AUTH=JWT` to enable automatic configuration, then provide the verifier's settings.
98
-
99
- Example configuration:
100
- ```bash
101
- export FASTMCP_SERVER_AUTH=JWT
102
- export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://your-idp.com/.well-known/jwks.json"
103
- export FASTMCP_SERVER_AUTH_JWT_ISSUER="https://your-idp.com"
104
- export FASTMCP_SERVER_AUTH_JWT_AUDIENCE="your-server-id"
105
- ```
106
 
107
- Your FastMCP server will now be automatically configured with JWT verification:
108
  ```python
109
  from fastmcp import FastMCP
 
110
 
111
- # This server is automatically protected with JWT verification
112
- # based on the environment variables.
113
- mcp = FastMCP(name="My Protected Server")
114
- ```
 
 
115
 
116
- <Expandable title="JWTVerifier Environment Variables">
117
- <ParamField body="FASTMCP_SERVER_AUTH_JWT_PUBLIC_KEY" type="str">
118
- PEM-encoded public key for JWT verification. Use this for static public key configuration.
119
- </ParamField>
120
 
121
- <ParamField body="FASTMCP_SERVER_AUTH_JWT_JWKS_URI" type="str">
122
- URI to fetch JSON Web Key Set (JWKS). Use this for automatic key rotation support.
123
- </ParamField>
124
 
125
- <ParamField body="FASTMCP_SERVER_AUTH_JWT_ISSUER" type="str">
126
- Expected JWT issuer claim for validation
127
- </ParamField>
128
 
129
- <ParamField body="FASTMCP_SERVER_AUTH_JWT_AUDIENCE" type="str">
130
- Expected JWT audience claim for validation
131
- </ParamField>
132
 
133
- <ParamField body="FASTMCP_SERVER_AUTH_JWT_ALGORITHM" type="str" default="RS256">
134
- JWT signing algorithm (e.g., RS256, HS256, ES256)
135
- </ParamField>
136
 
137
- <ParamField body="FASTMCP_SERVER_AUTH_JWT_REQUIRED_SCOPES" type="str">
138
- Comma-separated list of required scopes that all tokens must have
139
- </ParamField>
140
 
141
- <ParamField body="FASTMCP_SERVER_AUTH_JWT_RESOURCE_SERVER_URL" type="str">
142
- Resource server URL for OAuth protocol compliance
143
- </ParamField>
144
- </Expandable>
145
 
146
- ## Development Tools
 
 
 
 
147
 
148
- For local development and testing, managing JWTs can be cumbersome. FastMCP provides simpler tools for these scenarios.
 
149
 
150
- ### Static Token Verification
151
 
152
- 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.
153
 
 
154
 
155
- <Warning>
156
- **Never use static token verification in production.** Tokens are stored as plain text strings, which is highly insecure.
157
- </Warning>
158
 
159
- 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:
160
 
161
  ```python
162
  from fastmcp import FastMCP
163
  from fastmcp.server.auth.providers.jwt import StaticTokenVerifier
164
 
 
165
  verifier = StaticTokenVerifier(
166
  tokens={
167
- "dev-token-for-alice": {
168
- "client_id": "alice@example.com",
169
- "scopes": ["read:data", "write:data"]
170
  },
171
- "readonly-token-for-guest": {
172
  "client_id": "guest-user",
173
  "scopes": ["read:data"]
174
  }
175
  },
176
- required_scopes=["read:data"] # Optionally enforce a base scope for all tokens.
177
  )
178
 
179
  mcp = FastMCP(name="Development Server", auth=verifier)
180
  ```
181
 
182
- ### Generating Test Tokens
 
 
 
 
 
 
183
 
184
- 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.
185
 
186
  ```python
187
- from fastmcp import FastMCP
188
  from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
189
 
190
- # 1. In a secure part of your test setup, generate a key pair.
191
  key_pair = RSAKeyPair.generate()
192
 
193
- # 2. Configure your FastMCP server's verifier with the PUBLIC key.
194
- auth_verifier = JWTVerifier(
195
  public_key=key_pair.public_key,
196
- issuer="https://dev.fastmcp.com",
197
- audience="test-server"
198
  )
199
- mcp = FastMCP(name="Test Server", auth=auth_verifier)
200
 
201
- # 3. Use the PRIVATE key to create a valid token for your client tests.
202
  test_token = key_pair.create_token(
203
  subject="test-user-123",
204
- issuer="https://dev.fastmcp.com",
205
- audience="test-server",
206
- scopes=["read", "write"]
207
  )
208
 
209
- print(f"Generated Test Token:\n{test_token}")
210
  ```
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Token Verification
3
  sidebarTitle: Token Verification
4
+ description: Protect your server by validating bearer tokens issued by external systems.
5
  icon: key
6
  tag: NEW
7
  ---
 
10
 
11
  <VersionBadge version="2.11.0" />
12
 
13
+ 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.
14
 
15
+ <Note>
16
+ 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.
17
+ </Note>
18
 
19
+ ## Understanding Token Verification
20
 
21
+ 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.
22
 
23
+ 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.
24
 
25
+ ### The Token Verification Model
26
 
27
+ 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.
 
 
 
28
 
29
+ **Token Issuance**: Another system (API gateway, authentication service, or identity provider) handles user authentication and creates signed tokens containing identity and permission information.
 
 
 
 
 
 
30
 
31
+ **Token Validation**: Your MCP server receives these tokens, verifies their authenticity using cryptographic signatures, and extracts authorization information from their claims.
 
32
 
33
+ **Access Control**: Based on token contents, your server determines what resources, tools, and prompts the client can access.
 
 
34
 
35
+ 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.
 
 
 
 
36
 
37
+ ### Token Security Considerations
 
 
 
 
38
 
39
+ 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.
40
+
41
+ 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.
42
+
43
+ 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.
44
+
45
+ ## FastMCP Token Verification
46
+
47
+ FastMCP provides the `TokenVerifier` class to handle token validation complexity while remaining flexible about token sources and validation strategies.
48
+
49
+ ### TokenVerifier Design
50
+
51
+ `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.
52
+
53
+ 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.
54
+
55
+ 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.
56
+
57
+ ### JWT Token Verification
58
+
59
+ 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.
60
+
61
+ #### JWKS Endpoint Integration
62
+
63
+ 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.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
 
65
  ```python
66
  from fastmcp import FastMCP
67
+ from fastmcp.server.auth.providers.jwt import JWTVerifier
68
 
69
+ # Configure JWT verification against your identity provider
70
+ verifier = JWTVerifier(
71
+ jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
72
+ issuer="https://auth.yourcompany.com",
73
+ audience="mcp-production-api"
74
+ )
75
 
76
+ mcp = FastMCP(name="Protected API", auth=verifier)
77
+ ```
 
 
78
 
79
+ 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.
 
 
80
 
81
+ 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.
 
 
82
 
83
+ #### Static Public Key Verification
 
 
84
 
85
+ 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.
 
 
86
 
87
+ ```python
88
+ from fastmcp import FastMCP
89
+ from fastmcp.server.auth.providers.jwt import JWTVerifier
90
 
91
+ # Use a static public key for token verification
92
+ public_key_pem = """-----BEGIN PUBLIC KEY-----
93
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
94
+ -----END PUBLIC KEY-----"""
95
 
96
+ verifier = JWTVerifier(
97
+ public_key=public_key_pem,
98
+ issuer="https://auth.yourcompany.com",
99
+ audience="mcp-production-api"
100
+ )
101
 
102
+ mcp = FastMCP(name="Protected API", auth=verifier)
103
+ ```
104
 
105
+ 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.
106
 
107
+ ### Development and Testing
108
 
109
+ Development environments often need simpler token management without the complexity of full JWT infrastructure. FastMCP provides tools specifically designed for these scenarios.
110
 
111
+ #### Static Token Verification
 
 
112
 
113
+ 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.
114
 
115
  ```python
116
  from fastmcp import FastMCP
117
  from fastmcp.server.auth.providers.jwt import StaticTokenVerifier
118
 
119
+ # Define development tokens and their associated claims
120
  verifier = StaticTokenVerifier(
121
  tokens={
122
+ "dev-alice-token": {
123
+ "client_id": "alice@company.com",
124
+ "scopes": ["read:data", "write:data", "admin:users"]
125
  },
126
+ "dev-guest-token": {
127
  "client_id": "guest-user",
128
  "scopes": ["read:data"]
129
  }
130
  },
131
+ required_scopes=["read:data"]
132
  )
133
 
134
  mcp = FastMCP(name="Development Server", auth=verifier)
135
  ```
136
 
137
+ 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.
138
+
139
+ <Warning>
140
+ 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.
141
+ </Warning>
142
+
143
+ #### Test Token Generation
144
 
145
+ 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.
146
 
147
  ```python
 
148
  from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
149
 
150
+ # Generate a key pair for testing
151
  key_pair = RSAKeyPair.generate()
152
 
153
+ # Configure your server with the public key
154
+ verifier = JWTVerifier(
155
  public_key=key_pair.public_key,
156
+ issuer="https://test.yourcompany.com",
157
+ audience="test-mcp-server"
158
  )
 
159
 
160
+ # Generate a test token using the private key
161
  test_token = key_pair.create_token(
162
  subject="test-user-123",
163
+ issuer="https://test.yourcompany.com",
164
+ audience="test-mcp-server",
165
+ scopes=["read", "write", "admin"]
166
  )
167
 
168
+ print(f"Test token: {test_token}")
169
  ```
170
 
171
+ 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.
172
+
173
+ ## Environment Configuration
174
+
175
+ FastMCP supports both programmatic and environment-based configuration for token verification, enabling flexible deployment across different environments.
176
+
177
+ Environment-based configuration separates authentication settings from application code, following twelve-factor app principles and simplifying deployment pipelines.
178
+
179
+ ```bash
180
+ # Enable JWT verification
181
+ export FASTMCP_SERVER_AUTH=JWT
182
+
183
+ # Configure JWT verification parameters
184
+ export FASTMCP_SERVER_AUTH_JWT_JWKS_URI="https://auth.company.com/.well-known/jwks.json"
185
+ export FASTMCP_SERVER_AUTH_JWT_ISSUER="https://auth.company.com"
186
+ export FASTMCP_SERVER_AUTH_JWT_AUDIENCE="mcp-production-api"
187
+ export FASTMCP_SERVER_AUTH_JWT_REQUIRED_SCOPES="read:data,write:data"
188
+ ```
189
+
190
+ With these environment variables configured, your FastMCP server automatically enables JWT verification:
191
+
192
+ ```python
193
+ from fastmcp import FastMCP
194
+
195
+ # Authentication automatically configured from environment
196
+ mcp = FastMCP(name="Production API")
197
+ ```
198
+
199
+ 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.
200
+
src/fastmcp/server/auth/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- from .auth import OAuthProvider, TokenVerifier
2
  from .providers.jwt import JWTVerifier, StaticTokenVerifier
3
 
4
 
@@ -7,6 +7,7 @@ __all__ = [
7
  "TokenVerifier",
8
  "JWTVerifier",
9
  "StaticTokenVerifier",
 
10
  ]
11
 
12
 
 
1
+ from .auth import OAuthProvider, TokenVerifier, RemoteAuthProvider
2
  from .providers.jwt import JWTVerifier, StaticTokenVerifier
3
 
4
 
 
7
  "TokenVerifier",
8
  "JWTVerifier",
9
  "StaticTokenVerifier",
10
+ "RemoteAuthProvider",
11
  ]
12
 
13
 
src/fastmcp/server/auth/auth.py CHANGED
@@ -1,7 +1,5 @@
1
  from __future__ import annotations
2
 
3
- from typing import TYPE_CHECKING
4
-
5
  from mcp.server.auth.provider import (
6
  AccessToken,
7
  AuthorizationCode,
@@ -11,6 +9,10 @@ from mcp.server.auth.provider import (
11
  from mcp.server.auth.provider import (
12
  TokenVerifier as TokenVerifierProtocol,
13
  )
 
 
 
 
14
  from mcp.server.auth.settings import (
15
  ClientRegistrationOptions,
16
  RevocationOptions,
@@ -18,11 +20,8 @@ from mcp.server.auth.settings import (
18
  from pydantic import AnyHttpUrl
19
  from starlette.routing import Route
20
 
21
- if TYPE_CHECKING:
22
- pass
23
-
24
 
25
- class AuthProvider:
26
  """Base class for all FastMCP authentication providers.
27
 
28
  This class provides a unified interface for all authentication providers,
@@ -31,9 +30,18 @@ class AuthProvider:
31
  custom authentication routes.
32
  """
33
 
34
- def __init__(self, required_scopes: list[str] | None = None):
35
- """Initialize the auth provider."""
36
- self.required_scopes: list[str] = required_scopes or []
 
 
 
 
 
 
 
 
 
37
 
38
  async def verify_token(self, token: str) -> AccessToken | None:
39
  """Verify a bearer token and return access info if valid.
@@ -48,22 +56,34 @@ class AuthProvider:
48
  """
49
  raise NotImplementedError("Subclasses must implement verify_token")
50
 
51
- def customize_auth_routes(self, routes: list[Route]) -> list[Route]:
52
- """Customize authentication routes after standard creation.
53
 
54
- This method allows providers to modify or add to the standard OAuth routes.
55
- The default implementation returns the routes unchanged.
56
-
57
- Args:
58
- routes: List of standard routes (may be empty for token-only providers)
59
 
60
  Returns:
61
- List of routes (potentially modified or extended)
62
  """
63
- return routes
 
 
 
 
 
64
 
 
 
 
 
 
 
65
 
66
- class TokenVerifier(AuthProvider, TokenVerifierProtocol):
 
67
  """Base class for token verifiers (Resource Servers).
68
 
69
  This class provides token verification capability without OAuth server functionality.
@@ -79,26 +99,71 @@ class TokenVerifier(AuthProvider, TokenVerifierProtocol):
79
  Initialize the token verifier.
80
 
81
  Args:
82
- resource_server_url: The URL of this resource server (for RFC 8707 resource indicators)
 
 
83
  required_scopes: Scopes that are required for all requests
84
  """
85
- # Initialize AuthProvider (no args needed)
86
- AuthProvider.__init__(self, required_scopes=required_scopes)
87
-
88
- # Handle our own resource_server_url and required_scopes
89
- self.resource_server_url: AnyHttpUrl | None
90
- if resource_server_url is None:
91
- self.resource_server_url = None
92
- elif isinstance(resource_server_url, str):
93
- self.resource_server_url = AnyHttpUrl(resource_server_url)
94
- else:
95
- self.resource_server_url = resource_server_url
96
 
97
  async def verify_token(self, token: str) -> AccessToken | None:
98
  """Verify a bearer token and return access info if valid."""
99
  raise NotImplementedError("Subclasses must implement verify_token")
100
 
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  class OAuthProvider(
103
  AuthProvider,
104
  OAuthAuthorizationServerProvider[AuthorizationCode, RefreshToken, AccessToken],
@@ -181,17 +246,33 @@ class OAuthProvider(
181
  """
182
  return await self.load_access_token(token)
183
 
184
- def customize_auth_routes(self, routes: list[Route]) -> list[Route]:
185
- """Customize OAuth authentication routes after standard creation.
186
 
187
- This method allows providers to modify the standard OAuth routes
188
- returned by create_auth_routes. The default implementation returns
189
- the routes unchanged.
190
-
191
- Args:
192
- routes: List of standard OAuth routes from create_auth_routes
193
 
194
  Returns:
195
- List of routes (potentially modified)
196
  """
197
- return routes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from __future__ import annotations
2
 
 
 
3
  from mcp.server.auth.provider import (
4
  AccessToken,
5
  AuthorizationCode,
 
9
  from mcp.server.auth.provider import (
10
  TokenVerifier as TokenVerifierProtocol,
11
  )
12
+ from mcp.server.auth.routes import (
13
+ create_auth_routes,
14
+ create_protected_resource_routes,
15
+ )
16
  from mcp.server.auth.settings import (
17
  ClientRegistrationOptions,
18
  RevocationOptions,
 
20
  from pydantic import AnyHttpUrl
21
  from starlette.routing import Route
22
 
 
 
 
23
 
24
+ class AuthProvider(TokenVerifierProtocol):
25
  """Base class for all FastMCP authentication providers.
26
 
27
  This class provides a unified interface for all authentication providers,
 
30
  custom authentication routes.
31
  """
32
 
33
+ def __init__(self, resource_server_url: AnyHttpUrl | str | None = None):
34
+ """
35
+ Initialize the auth provider.
36
+
37
+ Args:
38
+ resource_server_url: The URL of this resource server. This is used
39
+ for RFC 8707 resource indicators, including creating the WWW-Authenticate
40
+ header.
41
+ """
42
+ if isinstance(resource_server_url, str):
43
+ resource_server_url = AnyHttpUrl(resource_server_url)
44
+ self.resource_server_url = resource_server_url
45
 
46
  async def verify_token(self, token: str) -> AccessToken | None:
47
  """Verify a bearer token and return access info if valid.
 
56
  """
57
  raise NotImplementedError("Subclasses must implement verify_token")
58
 
59
+ def get_routes(self) -> list[Route]:
60
+ """Get the routes for this authentication provider.
61
 
62
+ Each provider is responsible for creating whatever routes it needs:
63
+ - TokenVerifier: typically no routes (default implementation)
64
+ - RemoteAuthProvider: protected resource metadata routes
65
+ - OAuthProvider: full OAuth authorization server routes
66
+ - Custom providers: whatever routes they need
67
 
68
  Returns:
69
+ List of routes for this provider
70
  """
71
+ return []
72
+
73
+ def get_resource_metadata_url(self) -> AnyHttpUrl | None:
74
+ """Get the resource metadata URL for RFC 9728 compliance."""
75
+ if self.resource_server_url is None:
76
+ return None
77
 
78
+ # Add .well-known path for RFC 9728 compliance
79
+ resource_metadata_url = AnyHttpUrl(
80
+ str(self.resource_server_url).rstrip("/")
81
+ + "/.well-known/oauth-protected-resource"
82
+ )
83
+ return resource_metadata_url
84
 
85
+
86
+ class TokenVerifier(AuthProvider):
87
  """Base class for token verifiers (Resource Servers).
88
 
89
  This class provides token verification capability without OAuth server functionality.
 
99
  Initialize the token verifier.
100
 
101
  Args:
102
+ resource_server_url: The URL of this resource server. This is used
103
+ for RFC 8707 resource indicators, including creating the WWW-Authenticate
104
+ header.
105
  required_scopes: Scopes that are required for all requests
106
  """
107
+ super().__init__(resource_server_url=resource_server_url)
108
+ self.required_scopes = required_scopes or []
 
 
 
 
 
 
 
 
 
109
 
110
  async def verify_token(self, token: str) -> AccessToken | None:
111
  """Verify a bearer token and return access info if valid."""
112
  raise NotImplementedError("Subclasses must implement verify_token")
113
 
114
 
115
+ class RemoteAuthProvider(AuthProvider):
116
+ """Authentication provider for resource servers that verify tokens from known authorization servers.
117
+
118
+ This provider composes a TokenVerifier with authorization server metadata to create
119
+ standardized OAuth 2.0 Protected Resource endpoints (RFC 9728). Perfect for:
120
+ - JWT verification with known issuers
121
+ - Remote token introspection services
122
+ - Any resource server that knows where its tokens come from
123
+
124
+ Use this when you have token verification logic and want to advertise
125
+ the authorization servers that issue valid tokens.
126
+ """
127
+
128
+ def __init__(
129
+ self,
130
+ token_verifier: TokenVerifier,
131
+ authorization_servers: list[AnyHttpUrl],
132
+ resource_server_url: AnyHttpUrl | str,
133
+ ):
134
+ """Initialize the remote auth provider.
135
+
136
+ Args:
137
+ token_verifier: TokenVerifier instance for token validation
138
+ authorization_servers: List of authorization servers that issue valid tokens
139
+ resource_server_url: URL of this resource server. This is used
140
+ for RFC 8707 resource indicators, including creating the WWW-Authenticate
141
+ header.
142
+ """
143
+ super().__init__(resource_server_url=resource_server_url)
144
+ self.token_verifier = token_verifier
145
+ self.authorization_servers = authorization_servers
146
+
147
+ async def verify_token(self, token: str) -> AccessToken | None:
148
+ """Verify token using the configured token verifier."""
149
+ return await self.token_verifier.verify_token(token)
150
+
151
+ def get_routes(self) -> list[Route]:
152
+ """Get OAuth routes for this provider.
153
+
154
+ By default, returns only the standardized OAuth 2.0 Protected Resource routes.
155
+ Subclasses can override this method to add additional routes by calling
156
+ super().get_routes() and extending the returned list.
157
+ """
158
+ assert self.resource_server_url is not None
159
+
160
+ return create_protected_resource_routes(
161
+ resource_url=self.resource_server_url,
162
+ authorization_servers=self.authorization_servers,
163
+ scopes_supported=self.token_verifier.required_scopes,
164
+ )
165
+
166
+
167
  class OAuthProvider(
168
  AuthProvider,
169
  OAuthAuthorizationServerProvider[AuthorizationCode, RefreshToken, AccessToken],
 
246
  """
247
  return await self.load_access_token(token)
248
 
249
+ def get_routes(self) -> list[Route]:
250
+ """Get OAuth authorization server routes and optional protected resource routes.
251
 
252
+ This method creates the full set of OAuth routes including:
253
+ - Standard OAuth authorization server routes (/.well-known/oauth-authorization-server, /authorize, /token, etc.)
254
+ - Optional protected resource routes if resource_server_url is configured
 
 
 
255
 
256
  Returns:
257
+ List of OAuth routes
258
  """
259
+
260
+ # Create standard OAuth authorization server routes
261
+ oauth_routes = create_auth_routes(
262
+ provider=self,
263
+ issuer_url=self.issuer_url,
264
+ service_documentation_url=self.service_documentation_url,
265
+ client_registration_options=self.client_registration_options,
266
+ revocation_options=self.revocation_options,
267
+ )
268
+
269
+ # Add protected resource routes if this server is also acting as a resource server
270
+ if self.resource_server_url:
271
+ protected_routes = create_protected_resource_routes(
272
+ resource_url=self.resource_server_url,
273
+ authorization_servers=[self.issuer_url],
274
+ scopes_supported=self.required_scopes,
275
+ )
276
+ oauth_routes.extend(protected_routes)
277
+
278
+ return oauth_routes
src/fastmcp/server/auth/providers/jwt.py CHANGED
@@ -16,7 +16,7 @@ from pydantic import AnyHttpUrl, SecretStr
16
  from pydantic_settings import BaseSettings, SettingsConfigDict
17
  from typing_extensions import TypedDict
18
 
19
- from fastmcp.server.auth.auth import TokenVerifier
20
  from fastmcp.server.auth.registry import register_provider
21
  from fastmcp.utilities.logging import get_logger
22
  from fastmcp.utilities.types import NotSet, NotSetT
 
16
  from pydantic_settings import BaseSettings, SettingsConfigDict
17
  from typing_extensions import TypedDict
18
 
19
+ from fastmcp.server.auth import TokenVerifier
20
  from fastmcp.server.auth.registry import register_provider
21
  from fastmcp.utilities.logging import get_logger
22
  from fastmcp.utilities.types import NotSet, NotSetT
src/fastmcp/server/auth/providers/workos.py CHANGED
@@ -1,15 +1,12 @@
1
  from __future__ import annotations
2
 
3
  import httpx
4
- from mcp.server.auth.provider import (
5
- AccessToken,
6
- )
7
  from pydantic import AnyHttpUrl
8
  from pydantic_settings import BaseSettings, SettingsConfigDict
9
  from starlette.responses import JSONResponse
10
- from starlette.routing import BaseRoute, Route
11
 
12
- from fastmcp.server.auth.auth import AuthProvider, TokenVerifier
13
  from fastmcp.server.auth.providers.jwt import JWTVerifier
14
  from fastmcp.server.auth.registry import register_provider
15
  from fastmcp.utilities.logging import get_logger
@@ -31,7 +28,7 @@ class AuthKitProviderSettings(BaseSettings):
31
 
32
 
33
  @register_provider("AUTHKIT")
34
- class AuthKitProvider(AuthProvider):
35
  """AuthKit metadata provider for DCR (Dynamic Client Registration).
36
 
37
  This provider implements AuthKit integration using metadata forwarding
@@ -83,8 +80,6 @@ class AuthKitProvider(AuthProvider):
83
  required_scopes: Optional list of scopes to require for all requests
84
  token_verifier: Optional token verifier. If None, creates JWT verifier for AuthKit
85
  """
86
- super().__init__()
87
-
88
  settings = AuthKitProviderSettings.model_validate(
89
  {
90
  k: v
@@ -109,19 +104,21 @@ class AuthKitProvider(AuthProvider):
109
  required_scopes=settings.required_scopes,
110
  )
111
 
112
- self.token_verifier = token_verifier
113
-
114
- async def verify_token(self, token: str) -> AccessToken | None:
115
- """Verify an AuthKit token using the configured token verifier."""
116
- return await self.token_verifier.verify_token(token)
 
117
 
118
- def customize_auth_routes(self, routes: list[BaseRoute]) -> list[BaseRoute]:
119
- """Add AuthKit metadata endpoints.
120
 
121
- This adds:
122
- - /.well-known/oauth-authorization-server (forwards AuthKit metadata)
123
- - /.well-known/oauth-protected-resource (returns FastMCP resource info)
124
  """
 
 
125
 
126
  async def oauth_authorization_server_metadata(request):
127
  """Forward AuthKit OAuth authorization server metadata with FastMCP customizations."""
@@ -142,29 +139,13 @@ class AuthKitProvider(AuthProvider):
142
  status_code=500,
143
  )
144
 
145
- async def oauth_protected_resource_metadata(request):
146
- """Return FastMCP resource server metadata."""
147
- return JSONResponse(
148
- {
149
- "resource": self.base_url,
150
- "authorization_servers": [self.authkit_domain],
151
- "bearer_methods_supported": ["header"],
152
- }
153
  )
154
-
155
- routes.extend(
156
- [
157
- Route(
158
- "/.well-known/oauth-authorization-server",
159
- endpoint=oauth_authorization_server_metadata,
160
- methods=["GET"],
161
- ),
162
- Route(
163
- "/.well-known/oauth-protected-resource",
164
- endpoint=oauth_protected_resource_metadata,
165
- methods=["GET"],
166
- ),
167
- ]
168
  )
169
 
170
  return routes
 
1
  from __future__ import annotations
2
 
3
  import httpx
 
 
 
4
  from pydantic import AnyHttpUrl
5
  from pydantic_settings import BaseSettings, SettingsConfigDict
6
  from starlette.responses import JSONResponse
7
+ from starlette.routing import Route
8
 
9
+ from fastmcp.server.auth import RemoteAuthProvider, TokenVerifier
10
  from fastmcp.server.auth.providers.jwt import JWTVerifier
11
  from fastmcp.server.auth.registry import register_provider
12
  from fastmcp.utilities.logging import get_logger
 
28
 
29
 
30
  @register_provider("AUTHKIT")
31
+ class AuthKitProvider(RemoteAuthProvider):
32
  """AuthKit metadata provider for DCR (Dynamic Client Registration).
33
 
34
  This provider implements AuthKit integration using metadata forwarding
 
80
  required_scopes: Optional list of scopes to require for all requests
81
  token_verifier: Optional token verifier. If None, creates JWT verifier for AuthKit
82
  """
 
 
83
  settings = AuthKitProviderSettings.model_validate(
84
  {
85
  k: v
 
104
  required_scopes=settings.required_scopes,
105
  )
106
 
107
+ # Initialize RemoteAuthProvider with AuthKit as the authorization server
108
+ super().__init__(
109
+ token_verifier=token_verifier,
110
+ authorization_servers=[AnyHttpUrl(self.authkit_domain)],
111
+ resource_server_url=self.base_url,
112
+ )
113
 
114
+ def get_routes(self) -> list[Route]:
115
+ """Get OAuth routes including AuthKit authorization server metadata forwarding.
116
 
117
+ This returns the standard protected resource routes plus an authorization server
118
+ metadata endpoint that forwards AuthKit's OAuth metadata to clients.
 
119
  """
120
+ # Get the standard protected resource routes from RemoteAuthProvider
121
+ routes = super().get_routes()
122
 
123
  async def oauth_authorization_server_metadata(request):
124
  """Forward AuthKit OAuth authorization server metadata with FastMCP customizations."""
 
139
  status_code=500,
140
  )
141
 
142
+ # Add AuthKit authorization server metadata forwarding
143
+ routes.append(
144
+ Route(
145
+ "/.well-known/oauth-authorization-server",
146
+ endpoint=oauth_authorization_server_metadata,
147
+ methods=["GET"],
 
 
148
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  )
150
 
151
  return routes
src/fastmcp/server/http.py CHANGED
@@ -11,12 +11,10 @@ from mcp.server.auth.middleware.bearer_auth import (
11
  RequireAuthMiddleware,
12
  )
13
  from mcp.server.auth.provider import TokenVerifier as TokenVerifierProtocol
14
- from mcp.server.auth.routes import create_auth_routes
15
  from mcp.server.lowlevel.server import LifespanResultT
16
  from mcp.server.sse import SseServerTransport
17
  from mcp.server.streamable_http import EventStore
18
  from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
19
- from pydantic import AnyHttpUrl
20
  from starlette.applications import Starlette
21
  from starlette.middleware import Middleware
22
  from starlette.middleware.authentication import AuthenticationMiddleware
@@ -25,7 +23,7 @@ from starlette.responses import Response
25
  from starlette.routing import BaseRoute, Mount, Route
26
  from starlette.types import Lifespan, Receive, Scope, Send
27
 
28
- from fastmcp.server.auth.auth import AuthProvider, OAuthProvider, TokenVerifier
29
  from fastmcp.utilities.logging import get_logger
30
 
31
  if TYPE_CHECKING:
@@ -71,51 +69,6 @@ class RequestContextMiddleware:
71
  await self.app(scope, receive, send)
72
 
73
 
74
- def setup_auth_middleware_and_routes(
75
- auth: AuthProvider,
76
- ) -> tuple[list[Middleware], list[Route], list[str]]:
77
- """Set up authentication middleware and routes if auth is enabled.
78
-
79
- Args:
80
- auth: An AuthProvider for authentication (TokenVerifier or OAuthProvider)
81
-
82
- Returns:
83
- Tuple of (middleware, auth_routes, required_scopes)
84
- """
85
- middleware: list[Middleware] = [
86
- Middleware(
87
- AuthenticationMiddleware,
88
- backend=BearerAuthBackend(cast(TokenVerifierProtocol, auth)),
89
- ),
90
- Middleware(AuthContextMiddleware),
91
- ]
92
-
93
- auth_routes: list[Route] = []
94
- required_scopes: list[str] = auth.required_scopes or []
95
-
96
- # Check if it's an OAuthProvider (has OAuth server capability)
97
- if isinstance(auth, OAuthProvider):
98
- # OAuthProvider: create standard OAuth routes first
99
- standard_routes = list(
100
- create_auth_routes(
101
- provider=auth,
102
- issuer_url=auth.issuer_url,
103
- service_documentation_url=auth.service_documentation_url,
104
- client_registration_options=auth.client_registration_options,
105
- revocation_options=auth.revocation_options,
106
- )
107
- )
108
-
109
- # Allow provider to customize routes (e.g., for proxy behavior or metadata endpoints)
110
- auth_routes = auth.customize_auth_routes(standard_routes)
111
- else:
112
- # Simple AuthProvider or TokenVerifier: start with empty routes
113
- # Allow provider to add custom routes (e.g., metadata endpoints)
114
- auth_routes = auth.customize_auth_routes([])
115
-
116
- return middleware, auth_routes, required_scopes
117
-
118
-
119
  def create_base_app(
120
  routes: list[BaseRoute],
121
  middleware: list[Middleware],
@@ -183,24 +136,27 @@ def create_sse_app(
183
  )
184
  return Response()
185
 
186
- # Get auth middleware and routes
187
  if auth:
188
- auth_middleware, auth_routes, required_scopes = (
189
- setup_auth_middleware_and_routes(auth)
190
- )
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  server_routes.extend(auth_routes)
193
  server_middleware.extend(auth_middleware)
194
 
195
- # Determine resource_metadata_url for TokenVerifier
196
- resource_metadata_url = None
197
- if isinstance(auth, TokenVerifier) and auth.resource_server_url:
198
- # Add .well-known path for RFC 9728 compliance
199
- resource_metadata_url = AnyHttpUrl(
200
- str(auth.resource_server_url).rstrip("/")
201
- + "/.well-known/oauth-protected-resource"
202
- )
203
-
204
  # Auth is enabled, wrap endpoints with RequireAuthMiddleware
205
  server_routes.append(
206
  Route(
@@ -328,22 +284,25 @@ def create_streamable_http_app(
328
 
329
  # Add StreamableHTTP routes with or without auth
330
  if auth:
331
- auth_middleware, auth_routes, required_scopes = (
332
- setup_auth_middleware_and_routes(auth)
333
- )
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  server_routes.extend(auth_routes)
336
  server_middleware.extend(auth_middleware)
337
 
338
- # Determine resource_metadata_url for TokenVerifier
339
- resource_metadata_url = None
340
- if isinstance(auth, TokenVerifier) and auth.resource_server_url:
341
- # Add .well-known path for RFC 9728 compliance
342
- resource_metadata_url = AnyHttpUrl(
343
- str(auth.resource_server_url).rstrip("/")
344
- + "/.well-known/oauth-protected-resource"
345
- )
346
-
347
  # Auth is enabled, wrap endpoint with RequireAuthMiddleware
348
  server_routes.append(
349
  Mount(
 
11
  RequireAuthMiddleware,
12
  )
13
  from mcp.server.auth.provider import TokenVerifier as TokenVerifierProtocol
 
14
  from mcp.server.lowlevel.server import LifespanResultT
15
  from mcp.server.sse import SseServerTransport
16
  from mcp.server.streamable_http import EventStore
17
  from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
 
18
  from starlette.applications import Starlette
19
  from starlette.middleware import Middleware
20
  from starlette.middleware.authentication import AuthenticationMiddleware
 
23
  from starlette.routing import BaseRoute, Mount, Route
24
  from starlette.types import Lifespan, Receive, Scope, Send
25
 
26
+ from fastmcp.server.auth.auth import AuthProvider
27
  from fastmcp.utilities.logging import get_logger
28
 
29
  if TYPE_CHECKING:
 
69
  await self.app(scope, receive, send)
70
 
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  def create_base_app(
73
  routes: list[BaseRoute],
74
  middleware: list[Middleware],
 
136
  )
137
  return Response()
138
 
139
+ # Set up auth if enabled
140
  if auth:
141
+ # Create auth middleware
142
+ auth_middleware = [
143
+ Middleware(
144
+ AuthenticationMiddleware,
145
+ backend=BearerAuthBackend(auth),
146
+ ),
147
+ Middleware(AuthContextMiddleware),
148
+ ]
149
+
150
+ # Get auth routes and scopes
151
+ auth_routes = auth.get_routes()
152
+ required_scopes = getattr(auth, "required_scopes", None) or []
153
+
154
+ # Get resource metadata URL for WWW-Authenticate header
155
+ resource_metadata_url = auth.get_resource_metadata_url()
156
 
157
  server_routes.extend(auth_routes)
158
  server_middleware.extend(auth_middleware)
159
 
 
 
 
 
 
 
 
 
 
160
  # Auth is enabled, wrap endpoints with RequireAuthMiddleware
161
  server_routes.append(
162
  Route(
 
284
 
285
  # Add StreamableHTTP routes with or without auth
286
  if auth:
287
+ # Create auth middleware
288
+ auth_middleware = [
289
+ Middleware(
290
+ AuthenticationMiddleware,
291
+ backend=BearerAuthBackend(cast(TokenVerifierProtocol, auth)),
292
+ ),
293
+ Middleware(AuthContextMiddleware),
294
+ ]
295
+
296
+ # Get auth routes and scopes
297
+ auth_routes = auth.get_routes()
298
+ required_scopes = getattr(auth, "required_scopes", None) or []
299
+
300
+ # Get resource metadata URL for WWW-Authenticate header
301
+ resource_metadata_url = auth.get_resource_metadata_url()
302
 
303
  server_routes.extend(auth_routes)
304
  server_middleware.extend(auth_middleware)
305
 
 
 
 
 
 
 
 
 
 
306
  # Auth is enabled, wrap endpoint with RequireAuthMiddleware
307
  server_routes.append(
308
  Mount(
tests/server/http/test_auth_setup.py DELETED
@@ -1,189 +0,0 @@
1
- """Tests for authentication setup in HTTP apps."""
2
-
3
- import pytest
4
- from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend
5
- from mcp.server.auth.provider import AccessToken
6
- from starlette.middleware import Middleware
7
- from starlette.middleware.authentication import AuthenticationMiddleware
8
-
9
- from fastmcp.server.auth.providers.in_memory import InMemoryOAuthProvider
10
- from fastmcp.server.auth.providers.jwt import JWTVerifier, RSAKeyPair
11
- from fastmcp.server.http import setup_auth_middleware_and_routes
12
-
13
-
14
- class TestSetupAuthMiddlewareAndRoutes:
15
- """Test setup_auth_middleware_and_routes with TokenVerifier providers."""
16
-
17
- @pytest.fixture
18
- def jwt_verifier(self) -> JWTVerifier:
19
- """Create JWTVerifier for testing."""
20
- key_pair = RSAKeyPair.generate()
21
- return JWTVerifier(
22
- public_key=key_pair.public_key,
23
- issuer="https://test.example.com",
24
- audience="https://api.example.com",
25
- required_scopes=["read", "write"],
26
- )
27
-
28
- @pytest.fixture
29
- def in_memory_provider(self) -> InMemoryOAuthProvider:
30
- """Create InMemoryOAuthProvider for testing."""
31
- return InMemoryOAuthProvider(
32
- base_url="https://test.example.com",
33
- required_scopes=["user"],
34
- )
35
-
36
- def test_setup_with_jwt_verifier(self, jwt_verifier: JWTVerifier):
37
- """Test that setup works with JWTVerifier as TokenVerifier."""
38
- middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
39
- jwt_verifier
40
- )
41
-
42
- # Should return middleware list
43
- assert isinstance(middleware, list)
44
- assert len(middleware) == 2 # AuthenticationMiddleware + AuthContextMiddleware
45
-
46
- # First middleware should be AuthenticationMiddleware with BearerAuthBackend
47
- auth_middleware = middleware[0]
48
- assert isinstance(auth_middleware, Middleware)
49
- assert auth_middleware.cls == AuthenticationMiddleware
50
- assert "backend" in auth_middleware.kwargs
51
-
52
- backend = auth_middleware.kwargs["backend"]
53
- assert isinstance(backend, BearerAuthBackend)
54
- assert backend.token_verifier is jwt_verifier # type: ignore[attr-defined]
55
-
56
- # Should return auth routes
57
- assert isinstance(auth_routes, list)
58
- assert len(auth_routes) == 0 # TokenVerifier should not have OAuth routes
59
-
60
- # Should return required scopes
61
- assert required_scopes == ["read", "write"]
62
-
63
- def test_setup_with_in_memory_provider(
64
- self, in_memory_provider: InMemoryOAuthProvider
65
- ):
66
- """Test that setup works with InMemoryOAuthProvider as TokenVerifier."""
67
- middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
68
- in_memory_provider
69
- )
70
-
71
- # Should return middleware list
72
- assert isinstance(middleware, list)
73
- assert len(middleware) == 2
74
-
75
- # Backend should use the provider as token verifier
76
- auth_middleware = middleware[0]
77
- backend = auth_middleware.kwargs["backend"]
78
- assert isinstance(backend, BearerAuthBackend)
79
- assert backend.token_verifier is in_memory_provider # type: ignore[attr-defined]
80
-
81
- # Should return required scopes
82
- assert required_scopes == ["user"]
83
-
84
- def test_setup_preserves_provider_functionality(self, jwt_verifier: JWTVerifier):
85
- """Test that setup doesn't break the provider's functionality."""
86
- # Setup should not modify the provider
87
- original_issuer = jwt_verifier.issuer
88
- original_scopes = jwt_verifier.required_scopes
89
-
90
- middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
91
- jwt_verifier
92
- )
93
-
94
- # Provider should be unchanged
95
- assert jwt_verifier.issuer == original_issuer
96
- assert jwt_verifier.required_scopes == original_scopes
97
-
98
- # Provider should still work as TokenVerifier
99
- assert hasattr(jwt_verifier, "verify_token")
100
- assert callable(jwt_verifier.verify_token)
101
-
102
-
103
- class MockOAuthProvider:
104
- """Mock OAuth provider that implements TokenVerifier."""
105
-
106
- def __init__(self, required_scopes=None, issuer_url="http://localhost:8000"):
107
- from pydantic import AnyHttpUrl
108
-
109
- from fastmcp.server.auth.auth import (
110
- ClientRegistrationOptions,
111
- RevocationOptions,
112
- )
113
-
114
- self.required_scopes = required_scopes or []
115
- self.issuer_url = AnyHttpUrl(issuer_url)
116
- self.service_documentation_url = None
117
- self.client_registration_options = ClientRegistrationOptions(enabled=False)
118
- self.revocation_options = RevocationOptions(enabled=False)
119
-
120
- async def verify_token(self, token: str) -> AccessToken | None:
121
- """Mock verify_token implementation."""
122
- if token == "valid-token":
123
- return AccessToken(
124
- token=token,
125
- client_id="mock-client",
126
- scopes=self.required_scopes,
127
- expires_at=None,
128
- )
129
- return None
130
-
131
- def customize_auth_routes(self, routes):
132
- """Mock customize_auth_routes implementation."""
133
- return routes
134
-
135
-
136
- class TestSetupWithMockProvider:
137
- """Test setup function with mock provider."""
138
-
139
- def test_setup_with_mock_token_verifier(self):
140
- """Test that setup works with any TokenVerifier implementation."""
141
- mock_provider = MockOAuthProvider(required_scopes=["mock-scope"])
142
-
143
- middleware, auth_routes, required_scopes = setup_auth_middleware_and_routes(
144
- mock_provider # type: ignore[arg-type]
145
- )
146
-
147
- # Should work with any TokenVerifier
148
- assert len(middleware) == 2
149
- auth_middleware = middleware[0]
150
- backend = auth_middleware.kwargs["backend"]
151
- assert isinstance(backend, BearerAuthBackend)
152
- assert backend.token_verifier is mock_provider # type: ignore[attr-defined]
153
-
154
- assert required_scopes == ["mock-scope"]
155
-
156
- async def test_setup_middleware_can_authenticate(self):
157
- """Test that the setup middleware can actually authenticate requests."""
158
- mock_provider = MockOAuthProvider()
159
-
160
- middleware, _, _ = setup_auth_middleware_and_routes(mock_provider) # type: ignore[arg-type]
161
-
162
- # Extract the BearerAuthBackend
163
- auth_middleware = middleware[0]
164
- backend = auth_middleware.kwargs["backend"]
165
-
166
- # Test authentication with valid token
167
- from starlette.requests import HTTPConnection
168
-
169
- scope = {
170
- "type": "http",
171
- "headers": [(b"authorization", b"Bearer valid-token")],
172
- }
173
- conn = HTTPConnection(scope)
174
-
175
- result = await backend.authenticate(conn) # type: ignore[attr-defined]
176
- assert result is not None
177
-
178
- credentials, user = result
179
- assert user.username == "mock-client"
180
-
181
- # Test authentication with invalid token
182
- scope = {
183
- "type": "http",
184
- "headers": [(b"authorization", b"Bearer invalid-token")],
185
- }
186
- conn = HTTPConnection(scope)
187
-
188
- result = await backend.authenticate(conn) # type: ignore[attr-defined]
189
- assert result is None