60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
96 lines
3.9 KiB
Plaintext
96 lines
3.9 KiB
Plaintext
---
|
|
title: Multiple Auth Sources
|
|
sidebarTitle: Multiple Auth Sources
|
|
description: Accept tokens from multiple authentication sources with a single server.
|
|
icon: layer-group
|
|
---
|
|
|
|
import { VersionBadge } from "/snippets/version-badge.mdx"
|
|
|
|
<VersionBadge version="3.1.0" />
|
|
|
|
Production servers often need to accept tokens from multiple authentication sources. An interactive application might authenticate through an OAuth proxy, while a backend service sends machine-to-machine JWT tokens directly. `MultiAuth` composes these sources into a single `auth` provider so every valid token is accepted regardless of where it was issued.
|
|
|
|
## Understanding MultiAuth
|
|
|
|
`MultiAuth` wraps an optional auth server (like `OAuthProxy`) together with one or more token verifiers (like `JWTVerifier`). When a request arrives with a bearer token, `MultiAuth` tries each source in order and accepts the first successful verification.
|
|
|
|
The auth server, if provided, is tried first. It owns all OAuth routes and metadata — the verifiers contribute only token verification logic. This keeps the MCP discovery surface clean: one set of routes, one set of metadata, multiple verification paths.
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import MultiAuth, OAuthProxy
|
|
from fastmcp.server.auth.providers.jwt import JWTVerifier
|
|
|
|
auth = MultiAuth(
|
|
server=OAuthProxy(
|
|
issuer_url="https://login.example.com/...",
|
|
client_id="my-app",
|
|
client_secret="secret",
|
|
base_url="https://my-server.com",
|
|
),
|
|
verifiers=[
|
|
JWTVerifier(
|
|
jwks_uri="https://internal-issuer.example.com/.well-known/jwks.json",
|
|
issuer="https://internal-issuer.example.com",
|
|
audience="my-mcp-server",
|
|
),
|
|
],
|
|
)
|
|
|
|
mcp = FastMCP("My Server", auth=auth)
|
|
```
|
|
|
|
Interactive MCP clients authenticate through the OAuth proxy as usual. Backend services skip OAuth entirely and send a JWT signed by the internal issuer. Both paths are validated, and the first match wins.
|
|
|
|
## Verification Order
|
|
|
|
`MultiAuth` checks sources in a deterministic order:
|
|
|
|
1. **Server** (if provided) — the full auth provider's `verify_token` runs first
|
|
2. **Verifiers** — each `TokenVerifier` is tried in list order
|
|
|
|
The first source that returns a valid `AccessToken` wins. If every source returns `None`, the request receives a 401 response.
|
|
|
|
This ordering means the server acts as the "primary" authentication path, with verifiers as fallbacks for tokens the server doesn't recognize.
|
|
|
|
## Verifiers Only
|
|
|
|
You don't always need a full OAuth server. If your server only needs to accept tokens from multiple issuers, pass verifiers without a server:
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.server.auth import MultiAuth
|
|
from fastmcp.server.auth.providers.jwt import JWTVerifier, StaticTokenVerifier
|
|
|
|
auth = MultiAuth(
|
|
verifiers=[
|
|
JWTVerifier(
|
|
jwks_uri="https://issuer-a.example.com/.well-known/jwks.json",
|
|
issuer="https://issuer-a.example.com",
|
|
audience="my-server",
|
|
),
|
|
JWTVerifier(
|
|
jwks_uri="https://issuer-b.example.com/.well-known/jwks.json",
|
|
issuer="https://issuer-b.example.com",
|
|
audience="my-server",
|
|
),
|
|
],
|
|
)
|
|
|
|
mcp = FastMCP("Multi-Issuer Server", auth=auth)
|
|
```
|
|
|
|
Without a server, no OAuth routes or metadata are served. This is appropriate for internal systems where clients already know how to obtain tokens.
|
|
|
|
## API Reference
|
|
|
|
### MultiAuth
|
|
|
|
| Parameter | Type | Description |
|
|
| --- | --- | --- |
|
|
| `server` | `AuthProvider \| None` | Optional auth provider that owns routes and OAuth metadata. Also tried first for token verification. |
|
|
| `verifiers` | `list[TokenVerifier] \| TokenVerifier` | One or more token verifiers tried after the server. |
|
|
| `base_url` | `str \| None` | Override the base URL. Defaults to the server's `base_url`. |
|
|
| `required_scopes` | `list[str] \| None` | Override required scopes. Defaults to the server's scopes. |
|