Skip to main content
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.
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 mapping or 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:
Without a server, no OAuth routes or metadata are served. This is appropriate for internal systems where clients already know how to obtain tokens.

Challenge Scopes

Clients use the WWW-Authenticate challenge to decide which scopes to request. When you omit required_scopes or pass None, MultiAuth delegates default challenge selection to its server, or to its sole verifier when no server is configured. This preserves custom get_challenge_scopes(None) hooks. With multiple verifiers and no server, it does not select or combine their defaults; no scope parameter is advertised unless you configure required_scopes. An explicit required_scopes list is passed to the delegated challenge hook and also sets the scopes required by MultiAuth’s HTTP middleware. An empty list removes that outer scope requirement. Each verifier still enforces its own token validation requirements, so required_scopes=[] does not make invalid or under-scoped tokens valid. When the server is a RemoteAuthProvider, its configured challenge_scopes applies when the effective scopes equal that provider’s required_scopes, including an explicit list with the same values. A different explicit list is passed to the verifier’s hook for translation. Without configured challenge_scopes, the verifier receives the supplied list, or None to select its default.

Source Names

Each source has a stable identity for sessions, background tasks, and continued requests. Use an ordered mapping to give verifiers explicit names; pass server_source_id to name the delegated auth server. These names follow the source across restarts and changes to verification order. MultiAuth qualifies the returned token’s client_id with this source identity and the verified issuer and subject, when present. Its original_client_id field contains the identifier returned by the verifier; bearer token bytes, claims, subject, and scopes stay the same.
List and single-verifier forms remain available when their derived source identities are distinct. JWT verifiers derive identities from configured issuers and JWKS URLs; introspection verifiers use their endpoint, remote providers use their wrapped verifier, OAuth proxies use their configured issuer, upstream endpoints, and verifier, other OAuth servers use their configured issuer, and other providers use their Python type. Explicit names are recommended when configuration or provider types may change.
This changes get_access_token().client_id and ownership for existing MultiAuth sessions, tasks, and continued requests. Update authorization checks that compare the verifier’s original client ID to read get_access_token().original_client_id instead. Recreate sessions and resubmit tasks after upgrading. Configurations with duplicate derived identities now need explicit names, including multiple static verifiers or custom verifiers of the same type. Changing a source name starts a new ownership scope.

API Reference

MultiAuth