Spaces:
Running
Running
Jeremiah Lowin commited on
Commit ·
9103a0f
1
Parent(s): 20a8c4e
Add auth docs
Browse files- docs/clients/auth/bearer.mdx +24 -31
- docs/clients/auth/oauth.mdx +116 -0
- docs/deployment/authentication.mdx +0 -15
- docs/docs.json +24 -10
- docs/servers/auth/bearer.mdx +183 -0
- src/fastmcp/client/__init__.py +2 -1
- src/fastmcp/client/transports.py +1 -1
- src/fastmcp/server/auth/__init__.py +4 -0
- tests/auth/test_oauth_client.py +2 -2
docs/clients/auth/bearer.mdx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: Bearer Authentication
|
| 3 |
sidebarTitle: Bearer Auth
|
| 4 |
description: Authenticate your FastMCP client using pre-existing OAuth 2.0 Bearer tokens.
|
| 5 |
icon: key
|
|
@@ -9,8 +9,11 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
|
|
| 9 |
|
| 10 |
<VersionBadge version="2.6.0" />
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
| 13 |
|
|
|
|
| 14 |
|
| 15 |
A Bearer token is a JSON Web Token (JWT) that is used to authenticate a request. It is most commonly used in the `Authorization` header of an HTTP request, using the `Bearer` scheme:
|
| 16 |
|
|
@@ -27,62 +30,52 @@ The most straightforward way to use a pre-existing Bearer token is to provide it
|
|
| 27 |
If you're using a string token, do not include the `Bearer` prefix. FastMCP will add it for you.
|
| 28 |
</Tip>
|
| 29 |
|
| 30 |
-
```python {
|
| 31 |
from fastmcp import Client
|
| 32 |
|
| 33 |
-
async
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
) as client:
|
| 38 |
-
await client.ping()
|
| 39 |
```
|
| 40 |
|
| 41 |
You can also supply a Bearer token to a transport instance, such as `StreamableHttpTransport` or `SSETransport`:
|
| 42 |
|
| 43 |
-
```python {
|
| 44 |
from fastmcp import Client
|
| 45 |
from fastmcp.client.transports import StreamableHttpTransport
|
| 46 |
|
| 47 |
transport = StreamableHttpTransport(
|
| 48 |
-
"http://fastmcp.cloud/mcp",
|
| 49 |
-
auth="<your-token>",
|
| 50 |
)
|
| 51 |
|
| 52 |
-
async
|
| 53 |
-
|
| 54 |
-
await client.ping()
|
| 55 |
```
|
| 56 |
|
| 57 |
## `BearerAuth` Helper
|
| 58 |
|
| 59 |
If you prefer to be more explicit and not rely on FastMCP to transform your string token, you can use the `BearerAuth` class yourself, which implements the `httpx.Auth` interface.
|
| 60 |
|
| 61 |
-
```python {
|
| 62 |
from fastmcp import Client
|
| 63 |
from fastmcp.client.auth import BearerAuth
|
| 64 |
|
| 65 |
-
async
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
) as client:
|
| 70 |
-
await client.ping()
|
| 71 |
```
|
| 72 |
|
| 73 |
## Custom Headers
|
| 74 |
|
| 75 |
If the MCP server expects a custom header or token scheme, you can manually set the client's `headers` instead of using the `auth` parameter:
|
| 76 |
|
| 77 |
-
```python {
|
| 78 |
from fastmcp import Client
|
| 79 |
|
| 80 |
-
async
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
'X-API-Key': '<your-token>',
|
| 85 |
-
},
|
| 86 |
-
) as client:
|
| 87 |
-
await client.ping()
|
| 88 |
```
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Bearer Token Authentication
|
| 3 |
sidebarTitle: Bearer Auth
|
| 4 |
description: Authenticate your FastMCP client using pre-existing OAuth 2.0 Bearer tokens.
|
| 5 |
icon: key
|
|
|
|
| 9 |
|
| 10 |
<VersionBadge version="2.6.0" />
|
| 11 |
|
| 12 |
+
<Tip>
|
| 13 |
+
Bearer Token authentication is only relevant for HTTP-based transports.
|
| 14 |
+
</Tip>
|
| 15 |
|
| 16 |
+
You can configure your FastMCP client to use **bearer authentication** by supplying a valid access token. This is most appropriate for service accounts, long-lived API keys, CI/CD, applications where authentication is managed separately, or other non-interactive authentication methods.
|
| 17 |
|
| 18 |
A Bearer token is a JSON Web Token (JWT) that is used to authenticate a request. It is most commonly used in the `Authorization` header of an HTTP request, using the `Bearer` scheme:
|
| 19 |
|
|
|
|
| 30 |
If you're using a string token, do not include the `Bearer` prefix. FastMCP will add it for you.
|
| 31 |
</Tip>
|
| 32 |
|
| 33 |
+
```python {4}
|
| 34 |
from fastmcp import Client
|
| 35 |
|
| 36 |
+
async with Client(
|
| 37 |
+
"https://fastmcp.cloud/mcp", auth="<your-token>"
|
| 38 |
+
) as client:
|
| 39 |
+
await client.ping()
|
|
|
|
|
|
|
| 40 |
```
|
| 41 |
|
| 42 |
You can also supply a Bearer token to a transport instance, such as `StreamableHttpTransport` or `SSETransport`:
|
| 43 |
|
| 44 |
+
```python {5}
|
| 45 |
from fastmcp import Client
|
| 46 |
from fastmcp.client.transports import StreamableHttpTransport
|
| 47 |
|
| 48 |
transport = StreamableHttpTransport(
|
| 49 |
+
"http://fastmcp.cloud/mcp", auth="<your-token>"
|
|
|
|
| 50 |
)
|
| 51 |
|
| 52 |
+
async with Client(transport) as client:
|
| 53 |
+
await client.ping()
|
|
|
|
| 54 |
```
|
| 55 |
|
| 56 |
## `BearerAuth` Helper
|
| 57 |
|
| 58 |
If you prefer to be more explicit and not rely on FastMCP to transform your string token, you can use the `BearerAuth` class yourself, which implements the `httpx.Auth` interface.
|
| 59 |
|
| 60 |
+
```python {5}
|
| 61 |
from fastmcp import Client
|
| 62 |
from fastmcp.client.auth import BearerAuth
|
| 63 |
|
| 64 |
+
async with Client(
|
| 65 |
+
"https://fastmcp.cloud/mcp", auth=BearerAuth(token="<your-token>")
|
| 66 |
+
) as client:
|
| 67 |
+
await client.ping()
|
|
|
|
|
|
|
| 68 |
```
|
| 69 |
|
| 70 |
## Custom Headers
|
| 71 |
|
| 72 |
If the MCP server expects a custom header or token scheme, you can manually set the client's `headers` instead of using the `auth` parameter:
|
| 73 |
|
| 74 |
+
```python {4}
|
| 75 |
from fastmcp import Client
|
| 76 |
|
| 77 |
+
async with Client(
|
| 78 |
+
"https://fastmcp.cloud/mcp", headers={"X-API-Key": "<your-token>"}
|
| 79 |
+
) as client:
|
| 80 |
+
await client.ping()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
```
|
docs/clients/auth/oauth.mdx
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: OAuth Authentication
|
| 3 |
+
sidebarTitle: OAuth
|
| 4 |
+
description: Authenticate your FastMCP client with servers using the OAuth 2.0 Authorization Code Grant, including user interaction via a web browser.
|
| 5 |
+
icon: window
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from "/snippets/version-badge.mdx"
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.6.0" />
|
| 11 |
+
|
| 12 |
+
<Tip>
|
| 13 |
+
OAuth authentication is only relevant for HTTP-based transports and requires user interaction via a web browser.
|
| 14 |
+
</Tip>
|
| 15 |
+
|
| 16 |
+
When your FastMCP client needs to access an MCP server protected by OAuth 2.0, and the process requires user interaction (like logging in and granting consent), you should use the Authorization Code Flow. FastMCP provides the `fastmcp.client.auth.OAuth` helper to simplify this entire process.
|
| 17 |
+
|
| 18 |
+
This flow is common for user-facing applications where the application acts on behalf of the user.
|
| 19 |
+
|
| 20 |
+
## Client Usage
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
### Default Configuration
|
| 24 |
+
|
| 25 |
+
The simplest way to use OAuth is to pass the string `"oauth"` to the `auth` parameter of the `Client` or transport instance. FastMCP will automatically configure the client to use OAuth with default settings:
|
| 26 |
+
|
| 27 |
+
```python {4}
|
| 28 |
+
from fastmcp import Client
|
| 29 |
+
|
| 30 |
+
# Uses default OAuth settings
|
| 31 |
+
async with Client("https://fastmcp.cloud/mcp", auth="oauth") as client:
|
| 32 |
+
await client.ping()
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
### `OAuth` Helper
|
| 37 |
+
|
| 38 |
+
To fully configure the OAuth flow, use the `OAuth` helper and pass it to the `auth` parameter of the `Client` or transport instance. `OAuth` manages the complexities of the OAuth 2.0 Authorization Code Grant with PKCE (Proof Key for Code Exchange) for enhanced security, and implements the full `httpx.Auth` interface.
|
| 39 |
+
|
| 40 |
+
```python {2, 4, 6}
|
| 41 |
+
from fastmcp import Client
|
| 42 |
+
from fastmcp.client.auth import OAuth
|
| 43 |
+
|
| 44 |
+
oauth = OAuth(mcp_url="https://fastmcp.cloud/mcp")
|
| 45 |
+
|
| 46 |
+
async with Client("https://fastmcp.cloud/mcp", auth=oauth) as client:
|
| 47 |
+
await client.ping()
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
#### `OAuth` Parameters
|
| 51 |
+
|
| 52 |
+
- **`mcp_url`** (`str`): The full URL of the target MCP server endpoint. Used to discover OAuth server metadata
|
| 53 |
+
- **`scopes`** (`str | list[str]`, optional): OAuth scopes to request. Can be space-separated string or list of strings
|
| 54 |
+
- **`client_name`** (`str`, optional): Client name for dynamic registration. Defaults to `"FastMCP Client"`
|
| 55 |
+
- **`token_storage_cache_dir`** (`Path`, optional): Token cache directory. Defaults to `~/.fastmcp/oauth-mcp-client-cache/`
|
| 56 |
+
- **`additional_client_metadata`** (`dict[str, Any]`, optional): Extra metadata for client registration
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
## OAuth Flow
|
| 60 |
+
|
| 61 |
+
The OAuth flow is triggered when you use a FastMCP `Client` configured to use OAuth.
|
| 62 |
+
|
| 63 |
+
<Steps>
|
| 64 |
+
<Step title="Token Check">
|
| 65 |
+
The client first checks the `token_storage_cache_dir` for existing, valid tokens for the target server. If one is found, it will be used to authenticate the client.
|
| 66 |
+
</Step>
|
| 67 |
+
<Step title="OAuth Server Discovery">
|
| 68 |
+
If no valid tokens exist, the client attempts to discover the OAuth server's endpoints using a well-known URI (e.g., `/.well-known/oauth-authorization-server`) based on the `mcp_url`.
|
| 69 |
+
</Step>
|
| 70 |
+
<Step title="Dynamic Client Registration">
|
| 71 |
+
If the OAuth server supports it and the client isn't already registered (or credentials aren't cached), the client performs dynamic client registration according to RFC 7591.
|
| 72 |
+
</Step>
|
| 73 |
+
<Step title="Local Callback Server">
|
| 74 |
+
A temporary local HTTP server is started on an available port. This server's address (e.g., `http://127.0.0.1:<port>/callback`) acts as the `redirect_uri` for the OAuth flow.
|
| 75 |
+
</Step>
|
| 76 |
+
<Step title="Browser Interaction">
|
| 77 |
+
The user's default web browser is automatically opened, directing them to the OAuth server's authorization endpoint. The user logs in and grants (or denies) the requested `scopes`.
|
| 78 |
+
</Step>
|
| 79 |
+
<Step title="Authorization Code & Token Exchange">
|
| 80 |
+
Upon approval, the OAuth server redirects the user's browser to the local callback server with an `authorization_code`. The client captures this code and exchanges it with the OAuth server's token endpoint for an `access_token` (and often a `refresh_token`) using PKCE for security.
|
| 81 |
+
</Step>
|
| 82 |
+
<Step title="Token Caching">
|
| 83 |
+
The obtained tokens are saved to the `token_storage_cache_dir` for future use, eliminating the need for repeated browser interactions.
|
| 84 |
+
</Step>
|
| 85 |
+
<Step title="Authenticated Requests">
|
| 86 |
+
The access token is automatically included in the `Authorization` header for requests to the MCP server.
|
| 87 |
+
</Step>
|
| 88 |
+
<Step title="Refresh Token">
|
| 89 |
+
If the access token expires, the client will automatically use the refresh token to get a new access token.
|
| 90 |
+
</Step>
|
| 91 |
+
</Steps>
|
| 92 |
+
|
| 93 |
+
## Token Management
|
| 94 |
+
|
| 95 |
+
### Token Storage
|
| 96 |
+
|
| 97 |
+
OAuth access tokens are automatically cached in `~/.fastmcp/oauth-mcp-client-cache/` and persist between application runs. Files are keyed by the OAuth server's base URL.
|
| 98 |
+
|
| 99 |
+
### Managing Cache
|
| 100 |
+
|
| 101 |
+
To clear the tokens for a specific server, instantiate a `FileTokenStorage` instance and call the `clear` method:
|
| 102 |
+
|
| 103 |
+
```python
|
| 104 |
+
from fastmcp.client.auth import FileTokenStorage
|
| 105 |
+
|
| 106 |
+
storage = FileTokenStorage(server_url="https://fastmcp.cloud/mcp")
|
| 107 |
+
await storage.clear()
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
To clear *all* tokens for all servers, call the `clear_all` method on the `FileTokenStorage` class:
|
| 111 |
+
|
| 112 |
+
```python
|
| 113 |
+
from fastmcp.client.auth import FileTokenStorage
|
| 114 |
+
|
| 115 |
+
FileTokenStorage.clear_all()
|
| 116 |
+
```
|
docs/deployment/authentication.mdx
DELETED
|
@@ -1,15 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Authentication
|
| 3 |
-
sidebarTitle: Authentication
|
| 4 |
-
description: Secure your FastMCP server with authentication.
|
| 5 |
-
icon: lock
|
| 6 |
-
---
|
| 7 |
-
import { VersionBadge } from '/snippets/version-badge.mdx'
|
| 8 |
-
|
| 9 |
-
<VersionBadge version="2.2.7" />
|
| 10 |
-
|
| 11 |
-
This document will cover how to implement authentication for your FastMCP servers.
|
| 12 |
-
|
| 13 |
-
FastMCP leverages the OAuth 2.0 support provided by the underlying Model Context Protocol (MCP) SDK.
|
| 14 |
-
|
| 15 |
-
For now, refer to the [MCP Server Authentication documentation](/servers/fastmcp#authentication) for initial details and the [official MCP SDK documentation](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for more.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/docs.json
CHANGED
|
@@ -56,18 +56,24 @@
|
|
| 56 |
"servers/context"
|
| 57 |
]
|
| 58 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
"servers/openapi",
|
| 60 |
"servers/proxy",
|
| 61 |
-
"servers/composition"
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
"deployment/cli"
|
| 71 |
]
|
| 72 |
},
|
| 73 |
{
|
|
@@ -75,6 +81,14 @@
|
|
| 75 |
"pages": [
|
| 76 |
"clients/client",
|
| 77 |
"clients/transports",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
"clients/advanced-features"
|
| 79 |
]
|
| 80 |
},
|
|
|
|
| 56 |
"servers/context"
|
| 57 |
]
|
| 58 |
},
|
| 59 |
+
{
|
| 60 |
+
"group": "Authentication",
|
| 61 |
+
"icon": "shield-check",
|
| 62 |
+
"pages": [
|
| 63 |
+
"servers/auth/bearer"
|
| 64 |
+
]
|
| 65 |
+
},
|
| 66 |
"servers/openapi",
|
| 67 |
"servers/proxy",
|
| 68 |
+
"servers/composition",
|
| 69 |
+
{
|
| 70 |
+
"group": "Deployment",
|
| 71 |
+
"pages": [
|
| 72 |
+
"deployment/running-server",
|
| 73 |
+
"deployment/asgi",
|
| 74 |
+
"deployment/cli"
|
| 75 |
+
]
|
| 76 |
+
}
|
|
|
|
| 77 |
]
|
| 78 |
},
|
| 79 |
{
|
|
|
|
| 81 |
"pages": [
|
| 82 |
"clients/client",
|
| 83 |
"clients/transports",
|
| 84 |
+
{
|
| 85 |
+
"group": "Authentication",
|
| 86 |
+
"icon": "user-shield",
|
| 87 |
+
"pages": [
|
| 88 |
+
"clients/auth/bearer",
|
| 89 |
+
"clients/auth/oauth"
|
| 90 |
+
]
|
| 91 |
+
},
|
| 92 |
"clients/advanced-features"
|
| 93 |
]
|
| 94 |
},
|
docs/servers/auth/bearer.mdx
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Bearer Token Authentication
|
| 3 |
+
sidebarTitle: Bearer Auth
|
| 4 |
+
description: Secure your FastMCP server's HTTP endpoints by validating JWT Bearer tokens.
|
| 5 |
+
icon: key
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
import { VersionBadge } from "/snippets/version-badge.mdx"
|
| 9 |
+
|
| 10 |
+
<VersionBadge version="2.6.0" />
|
| 11 |
+
<Tip>
|
| 12 |
+
Authentication and authorization are only relevant for HTTP-based transports.
|
| 13 |
+
</Tip>
|
| 14 |
+
|
| 15 |
+
Bearer Token authentication is a common way to secure HTTP-based APIs. In this model, the client sends a token (usually a JSON Web Token or JWT) in the `Authorization` header with the "Bearer" scheme. The server then validates this token to grant or deny access.
|
| 16 |
+
|
| 17 |
+
FastMCP supports Bearer Token authentication for its HTTP-based transports (`streamable-http` and `sse`), allowing you to protect your server from unauthorized access.
|
| 18 |
+
|
| 19 |
+
## Authentication Strategy
|
| 20 |
+
|
| 21 |
+
FastMCP uses **asymmetric encryption** for token validation, which provides a clean security separation between token issuers and FastMCP servers. This approach means:
|
| 22 |
+
|
| 23 |
+
- **No shared secrets**: Your FastMCP server never needs access to private keys or client secrets
|
| 24 |
+
- **Public key verification**: The server only needs a public key (or JWKS endpoint) to verify token signatures
|
| 25 |
+
- **Secure token issuance**: Tokens are signed by an external service using a private key that never leaves the issuer
|
| 26 |
+
- **Scalable architecture**: Multiple FastMCP servers can validate tokens without coordinating secrets
|
| 27 |
+
|
| 28 |
+
This design allows you to integrate FastMCP servers into existing authentication infrastructures without compromising security boundaries.
|
| 29 |
+
|
| 30 |
+
## Configuration
|
| 31 |
+
|
| 32 |
+
To enable Bearer Token validation on your FastMCP server, use the `BearerAuthProvider` class. This provider validates incoming JWTs by verifying signatures, checking expiration, and optionally validating claims.
|
| 33 |
+
|
| 34 |
+
<Warning>
|
| 35 |
+
The `BearerAuthProvider` validates tokens; it does **not** issue them (or implement any part of an OAuth flow). You'll need to generate tokens separately, either using FastMCP utilities or an external Identity Provider (IdP) or OAuth 2.0 Authorization Server.
|
| 36 |
+
</Warning>
|
| 37 |
+
|
| 38 |
+
### Basic Setup
|
| 39 |
+
|
| 40 |
+
To configure bearer token authentication, instantiate a `BearerAuthProvider` instance and pass it to the `auth` parameter of the `FastMCP` instance.
|
| 41 |
+
|
| 42 |
+
The `BearerAuthProvider` requires either a static public key or a JWKS URI (but not both!) in order to verify the token's signature. All other parameters are optional -- if they are provided, they will be used as additional validation criteria.
|
| 43 |
+
|
| 44 |
+
```python {2, 10}
|
| 45 |
+
from fastmcp import FastMCP
|
| 46 |
+
from fastmcp.server.auth import BearerAuthProvider
|
| 47 |
+
|
| 48 |
+
auth = BearerAuthProvider(
|
| 49 |
+
jwks_uri="https://my-identity-provider.com/.well-known/jwks.json",
|
| 50 |
+
issuer="https://my-identity-provider.com/",
|
| 51 |
+
audience="my-mcp-server"
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
mcp = FastMCP(name="My MCP Server", auth=auth)
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
### Configuration Parameters
|
| 58 |
+
|
| 59 |
+
| Parameter | Type | Required | Description |
|
| 60 |
+
|-----------|------|----------|-------------|
|
| 61 |
+
| `public_key` | `str` | If `jwks_uri` is not provided | RSA public key in PEM format for static key validation |
|
| 62 |
+
| `jwks_uri` | `str` | If `public_key` is not provided | URL for JSON Web Key Set endpoint |
|
| 63 |
+
| `issuer` | `str` | No | Expected JWT `iss` claim value |
|
| 64 |
+
| `audience` | `str` | No | Expected JWT `aud` claim value |
|
| 65 |
+
| `required_scopes` | `list[str]` | No | Global scopes required for all requests |
|
| 66 |
+
|
| 67 |
+
#### Public Key
|
| 68 |
+
|
| 69 |
+
If you have a public key in PEM format, you can provide it to the `BearerAuthProvider` as a string.
|
| 70 |
+
|
| 71 |
+
```python {12}
|
| 72 |
+
from fastmcp.server.auth import BearerAuthProvider
|
| 73 |
+
import inspect
|
| 74 |
+
|
| 75 |
+
public_key_pem = inspect.cleandoc(
|
| 76 |
+
"""
|
| 77 |
+
-----BEGIN PUBLIC KEY-----
|
| 78 |
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy...
|
| 79 |
+
-----END PUBLIC KEY-----
|
| 80 |
+
"""
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
auth = BearerAuthProvider(public_key=public_key_pem)
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
#### JWKS URI
|
| 87 |
+
|
| 88 |
+
```python
|
| 89 |
+
provider = BearerAuthProvider(
|
| 90 |
+
jwks_uri="https://idp.example.com/.well-known/jwks.json"
|
| 91 |
+
)
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
<Note>
|
| 95 |
+
JWKS is recommended for production as it supports automatic key rotation and multiple signing keys.
|
| 96 |
+
</Note>
|
| 97 |
+
|
| 98 |
+
## Generating Tokens
|
| 99 |
+
|
| 100 |
+
For development and testing, FastMCP provides the `RSAKeyPair` utility class to generate tokens without needing an external OAuth provider.
|
| 101 |
+
|
| 102 |
+
<Warning>
|
| 103 |
+
The `RSAKeyPair` utility is intended for development and testing only. For production, use a proper OAuth 2.0 Authorization Server or Identity Provider.
|
| 104 |
+
</Warning>
|
| 105 |
+
### Basic Token Generation
|
| 106 |
+
|
| 107 |
+
```python
|
| 108 |
+
from fastmcp import FastMCP
|
| 109 |
+
from fastmcp.server.auth import BearerAuthProvider
|
| 110 |
+
from fastmcp.server.auth.providers.bearer import RSAKeyPair
|
| 111 |
+
|
| 112 |
+
# Generate a new key pair
|
| 113 |
+
key_pair = RSAKeyPair.generate()
|
| 114 |
+
|
| 115 |
+
# Configure the auth provider with the public key
|
| 116 |
+
auth = BearerAuthProvider(
|
| 117 |
+
public_key=key_pair.public_key,
|
| 118 |
+
issuer="https://dev.example.com",
|
| 119 |
+
audience="my-dev-server"
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
mcp = FastMCP(name="Development Server", auth=auth)
|
| 123 |
+
|
| 124 |
+
# Generate a token for testing
|
| 125 |
+
token = key_pair.create_token(
|
| 126 |
+
subject="dev-user",
|
| 127 |
+
issuer="https://dev.example.com",
|
| 128 |
+
audience="my-dev-server",
|
| 129 |
+
scopes=["read", "write"]
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
print(f"Test token: {token}")
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
### Token Creation Parameters
|
| 136 |
+
|
| 137 |
+
The `create_token()` method accepts these parameters:
|
| 138 |
+
|
| 139 |
+
| Parameter | Type | Default | Description |
|
| 140 |
+
|-----------|------|---------|-------------|
|
| 141 |
+
| `subject` | `str` | `"fastmcp-user"` | JWT subject claim (usually user ID) |
|
| 142 |
+
| `issuer` | `str` | `"https://fastmcp.example.com"` | JWT issuer claim |
|
| 143 |
+
| `audience` | `str` | `None` | JWT audience claim |
|
| 144 |
+
| `scopes` | `list[str]` | `None` | OAuth scopes to include |
|
| 145 |
+
| `expires_in_seconds` | `int` | `3600` | Token expiration time |
|
| 146 |
+
| `additional_claims` | `dict` | `None` | Extra claims to include |
|
| 147 |
+
| `kid` | `str` | `None` | Key ID for JWKS lookup |
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
## Accessing Token Claims
|
| 151 |
+
|
| 152 |
+
Once authenticated, your tools, resources, or prompts can access token information using the `get_access_token()` dependency function:
|
| 153 |
+
|
| 154 |
+
```python
|
| 155 |
+
from fastmcp import FastMCP, Context, ToolError
|
| 156 |
+
from fastmcp.server.dependencies import get_access_token, AccessToken
|
| 157 |
+
|
| 158 |
+
@mcp.tool()
|
| 159 |
+
async def get_my_data(ctx: Context) -> dict:
|
| 160 |
+
access_token: AccessToken = get_access_token()
|
| 161 |
+
|
| 162 |
+
user_id = access_token.client_id # From JWT 'sub' or 'client_id' claim
|
| 163 |
+
user_scopes = access_token.scopes
|
| 164 |
+
|
| 165 |
+
if "data:read_sensitive" not in user_scopes:
|
| 166 |
+
raise ToolError("Insufficient permissions: 'data:read_sensitive' scope required.")
|
| 167 |
+
|
| 168 |
+
return {
|
| 169 |
+
"user": user_id,
|
| 170 |
+
"sensitive_data": f"Private data for {user_id}",
|
| 171 |
+
"granted_scopes": user_scopes
|
| 172 |
+
}
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
### AccessToken Properties
|
| 176 |
+
|
| 177 |
+
| Property | Type | Description |
|
| 178 |
+
|----------|------|-------------|
|
| 179 |
+
| `token` | `str` | The raw JWT string |
|
| 180 |
+
| `client_id` | `str` | Authenticated principal identifier |
|
| 181 |
+
| `scopes` | `list[str]` | Granted scopes |
|
| 182 |
+
| `expires_at` | `datetime \| None` | Token expiration timestamp |
|
| 183 |
+
|
src/fastmcp/client/__init__.py
CHANGED
|
@@ -11,7 +11,7 @@ from .transports import (
|
|
| 11 |
FastMCPTransport,
|
| 12 |
StreamableHttpTransport,
|
| 13 |
)
|
| 14 |
-
from .auth import OAuth
|
| 15 |
|
| 16 |
__all__ = [
|
| 17 |
"Client",
|
|
@@ -26,4 +26,5 @@ __all__ = [
|
|
| 26 |
"FastMCPTransport",
|
| 27 |
"StreamableHttpTransport",
|
| 28 |
"OAuth",
|
|
|
|
| 29 |
]
|
|
|
|
| 11 |
FastMCPTransport,
|
| 12 |
StreamableHttpTransport,
|
| 13 |
)
|
| 14 |
+
from .auth import OAuth, BearerAuth
|
| 15 |
|
| 16 |
__all__ = [
|
| 17 |
"Client",
|
|
|
|
| 26 |
"FastMCPTransport",
|
| 27 |
"StreamableHttpTransport",
|
| 28 |
"OAuth",
|
| 29 |
+
"BearerAuth",
|
| 30 |
]
|
src/fastmcp/client/transports.py
CHANGED
|
@@ -36,7 +36,7 @@ from mcp.shared.memory import create_connected_server_and_client_session
|
|
| 36 |
from pydantic import AnyUrl
|
| 37 |
from typing_extensions import Unpack
|
| 38 |
|
| 39 |
-
from fastmcp.client.auth import OAuth
|
| 40 |
from fastmcp.server.dependencies import get_http_headers
|
| 41 |
from fastmcp.server.server import FastMCP
|
| 42 |
from fastmcp.utilities.logging import get_logger
|
|
|
|
| 36 |
from pydantic import AnyUrl
|
| 37 |
from typing_extensions import Unpack
|
| 38 |
|
| 39 |
+
from fastmcp.client.auth.oauth import OAuth
|
| 40 |
from fastmcp.server.dependencies import get_http_headers
|
| 41 |
from fastmcp.server.server import FastMCP
|
| 42 |
from fastmcp.utilities.logging import get_logger
|
src/fastmcp/server/auth/__init__.py
CHANGED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .providers.bearer import BearerAuthProvider
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
__all__ = ["BearerAuthProvider"]
|
tests/auth/test_oauth_client.py
CHANGED
|
@@ -5,7 +5,7 @@ from urllib.parse import parse_qs, urlparse
|
|
| 5 |
import httpx
|
| 6 |
import pytest
|
| 7 |
|
| 8 |
-
import fastmcp.client.auth # Import module, not the function directly
|
| 9 |
from fastmcp.client import Client
|
| 10 |
from fastmcp.client.transports import StreamableHttpTransport
|
| 11 |
from fastmcp.server.auth.auth import ClientRegistrationOptions
|
|
@@ -196,7 +196,7 @@ def client_with_headless_oauth(
|
|
| 196 |
with patch("fastmcp.client.auth.OAuth", side_effect=headless_oauth):
|
| 197 |
client = Client(
|
| 198 |
transport=StreamableHttpTransport(streamable_http_server),
|
| 199 |
-
auth=fastmcp.client.auth.OAuth(mcp_url=streamable_http_server),
|
| 200 |
)
|
| 201 |
yield client
|
| 202 |
|
|
|
|
| 5 |
import httpx
|
| 6 |
import pytest
|
| 7 |
|
| 8 |
+
import fastmcp.client.auth.oauth # Import module, not the function directly
|
| 9 |
from fastmcp.client import Client
|
| 10 |
from fastmcp.client.transports import StreamableHttpTransport
|
| 11 |
from fastmcp.server.auth.auth import ClientRegistrationOptions
|
|
|
|
| 196 |
with patch("fastmcp.client.auth.OAuth", side_effect=headless_oauth):
|
| 197 |
client = Client(
|
| 198 |
transport=StreamableHttpTransport(streamable_http_server),
|
| 199 |
+
auth=fastmcp.client.auth.oauth.OAuth(mcp_url=streamable_http_server),
|
| 200 |
)
|
| 201 |
yield client
|
| 202 |
|