Drop-in OAuth 2.1 provider for servers. Developed originally for custom/private MCP servers. Works with FastMCP, FastAPI, and the raw MCP SDK.
Project description
origo
Implements the OAuth2.1 + PKCE flow as a drop-in Starlette based middleware layer, with public and private registration modes.
Drop-in OAuth 2.1 provider, originally developed for use in custom/private 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 origo
Quickstart
FastMCP
from fastmcp import FastMCP
from origo import OAuthProvider, OAuthMiddleware
from starlette.routing import Mount
from starlette.applications import Starlette
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
root = Starlette(routes=[
Mount("/oauth", app=auth.asgi_app()),
Mount("/", app=app),
])
FastAPI
from fastapi import FastAPI
from origo import OAuthProvider, OAuthMiddleware
import os
auth = OAuthProvider(
base_url="https://api.yourdomain.com",
clients={os.getenv("OAUTH_CLIENT_ID"): os.getenv("OAUTH_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 server (ex. MCP server) with simple OAuth requirements
- 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 servers (ex. 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file origo-0.1.5.tar.gz.
File metadata
- Download URL: origo-0.1.5.tar.gz
- Upload date:
- Size: 9.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bf8399a5872806b97d3b912943dfb1947932b3734b759f6c8e24d822f4ac059
|
|
| MD5 |
e68dd52e704f29df7804a57634a1421d
|
|
| BLAKE2b-256 |
25312c4360c7c722c68630132596eb0defea0d70298a84af59c526ce461766b8
|
File details
Details for the file origo-0.1.5-py3-none-any.whl.
File metadata
- Download URL: origo-0.1.5-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb07b47583a0b4ed910bcd044212b0f9ce0336c5da7a8bdb05d7ed197a21da74
|
|
| MD5 |
60f4c3ac6e2ff1f80bef6454661d3b7e
|
|
| BLAKE2b-256 |
a50f68e4bedfbaf673f5c3482f738f2fb0f9ab99142c3c6d5e585d751c8bc04c
|