--- 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" 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. |