Skip to main content

Drop-in OAuth 2.1 provider for MCP servers. Works with FastMCP, FastAPI, and the raw MCP SDK.

Project description

origo

Implements the OAuth flow for a MCP server as a Starlette based middleware layer, with public and private registeration modes.

Drop-in OAuth 2.1 provider for MCP servers. Handles the full Authorization Code + PKCE flow with no external identity provider required.

Works with FastMCP, FastAPI, and the raw MCP SDK.

Install

pip install oauth-mcp

Quickstart

FastMCP

from fastmcp import FastMCP
from oauth_mcp import OAuthProvider, OAuthMiddleware
import os

auth = OAuthProvider(
    base_url="https://mcp.yourdomain.com",
    clients={os.getenv("MCP_CLIENT_ID"): os.getenv("MCP_CLIENT_SECRET")},
)

mcp = FastMCP("my-server")

# ... define tools ...

app = mcp.streamable_http_app()
app.add_middleware(OAuthMiddleware, provider=auth)

# Mount OAuth endpoints at root
from starlette.routing import Mount
from starlette.applications import Starlette
root = Starlette(routes=[
    Mount("/oauth", app=auth.asgi_app()),
    Mount("/", app=app),
])

FastAPI

from fastapi import FastAPI
from oauth_mcp import OAuthProvider, OAuthMiddleware
import os

auth = OAuthProvider(
    base_url="https://api.yourdomain.com",
    clients={os.getenv("MCP_CLIENT_ID"): os.getenv("MCP_CLIENT_SECRET")},
)

app = FastAPI()
app.add_middleware(OAuthMiddleware, provider=auth)
app.mount("/oauth", auth.asgi_app())

How this differs from enterprise OAuth

Traditional OAuth deployments separate the authorization server from the resource server — the MCP server asks a dedicated auth service "is this token valid?" on every request (RFC 7662 token introspection). This is correct for multi-tenant systems where tokens need to be revoked instantly across many services.

origo collapses this into a single process. Token validation is an in-memory lookup. Fast, zero network overhead, no second service to run. The tradeoff is that token revocation requires a server restart, and there's no centralized auth service to share across multiple resource servers. This also introduce a single point of failure and security relies on the shared memory with the application it is authenticating for.

Use this when:

  • You're running a personal or private MCP server
  • You control who gets client credentials
  • Operational simplicity matters more than enterprise auth guarantees

Use a proper auth server (Keycloak, Auth0, etc.) when:

  • Multiple users need independent identities
  • You need instant token revocation
  • You're sharing one auth service across many MCP servers
  • Compliance requirements mandate it

Two Modes

Private (default)

Only pre-registered clients can authenticate. Pass a clients dict:

auth = OAuthProvider(
    base_url="https://mcp.yourdomain.com",
    clients={"my-client-id": "my-client-secret"},
    public_registration=False,  # default
)

Public

Anyone can register as a client dynamically (DCR). A consent page is shown before access is granted:

auth = OAuthProvider(
    base_url="https://mcp.yourdomain.com",
    public_registration=True,
)

Options

Parameter Type Default Description
base_url str required Public base URL, no trailing slash
clients dict None Pre-registered {client_id: client_secret}
public_registration bool False Allow dynamic client registration
auto_approve bool False Skip consent page, auto-approve all valid clients
token_ttl int 3600 Access token lifetime in seconds
mcp_path str "/mcp" Path where MCP endpoint is mounted

OAuth Endpoints

Endpoint Description
GET /.well-known/oauth-authorization-server Discovery
GET /.well-known/oauth-protected-resource Resource metadata
POST /register Dynamic client registration (public mode only)
GET /authorize Authorization + consent
POST /token Token exchange

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

origo-0.1.0.tar.gz (9.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

origo-0.1.0-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file origo-0.1.0.tar.gz.

File metadata

  • Download URL: origo-0.1.0.tar.gz
  • Upload date:
  • Size: 9.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for origo-0.1.0.tar.gz
Algorithm Hash digest
SHA256 77651f4b4113960ce73bf9d6e993b2de464ad937e76e1c3ee109654915160240
MD5 58e18373e21a2bb3d7e14e71c8c27c89
BLAKE2b-256 a063b34805eae9937aa25526ef4ecd41a20d4616623654b9a129da80916f1866

See more details on using hashes here.

File details

Details for the file origo-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: origo-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for origo-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c762259f35202c10b09018a81f203c53d3e8aa42b3f720acd5d8a0c91d4958e9
MD5 94e944cc06424073bd40ac70eaae4140
BLAKE2b-256 7cd88a1581eaa772af22c2b4a637091f3ae9bc80c8f19250113bd1c4bea869ac

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page