Central Authentication Server for MCP
Central Authentication Server for MCP (Model Context Protocol)
A lightweight, production-ready Central Authentication Server built specifically for Model Context Protocol (MCP) ecosystems, migrated to Python + FastAPI and backed by Supabase (PostgreSQL).
Migration Overview: What Changed & What Did NOT Change
What Changed:
Backend Framework: Migrated from Node.js (Express) to Python 3.12+ (FastAPI + Pydantic + Uvicorn).
Database Engine: Migrated from single-file SQLite to Supabase (PostgreSQL) using the official
supabase-pyclient.Row Level Security (RLS): RLS is enabled on all tables (
mcp_clients,token_events,auth_codes,revoked_tokens,admin_users), completely blocking publicanonaccess while allowing privileged backend operations through the Supabaseservice_rolekey.Offline / Local Fallback: The database layer seamlessly supports both live Supabase and local/offline fallback, ensuring automated tests and development work out-of-the-box.
What Did NOT Change (100% Contract Preservation):
Zero Route / Contract Changes: All endpoint paths (
/.well-known/...,/register,/authorize,/token,/introspect,/revocations,/admin/...) and HTTP methods remain identical.Identical JSON Shapes & Status Codes: Request and response payloads are 100% identical. No MCP server or LLM client configuration requires changes.
RS256 JWT Token Structure: Tokens use the exact same claims (
iss,sub,aud,client_id,auth_mode,jti,iat,exp) and deterministickidsigning.Dual Auth Modes:
Mode 1: Full OAuth 2.1 flow (RFC 8414 Discovery, RFC 7591 Dynamic Client Registration, mandatory SHA-256 PKCE authorization code exchange, client credentials).
Mode 2: Static long-lived API key fallback for header-based LLM configs (
headers: { "x-api-key": "..." }).
Admin Control Center UI: The responsive dark glassmorphic dashboard in
public/admin/operates identically against the FastAPI backend.Verification Middleware: The drop-in MCP verification middleware in Python (
sdk/python/mcp_auth_middleware.py) and Node.js (sdk/node/mcp-auth-middleware.js) remain 100% drop-in compatible.
Architecture Diagram
+-------------------------------------------------------------+
| CENTRAL AUTH SERVER (FastAPI + Python) |
| |
| [Mode 1: OAuth 2.1] [Discovery & Keys] |
| - /.well-known/oauth-... - /.well-known/jwks.json |
| - /register (RFC 7591) - /revocations /introspect |
| - /authorize (PKCE S256) |
| - /token (auth_code & m2m) [Admin Dashboard] |
| - MCP Client Management |
| [Key Management & Crypto] - Static Token Generation |
| - RS256 Private Key Signing - Audit Logging |
| - bcrypt Secret Hashing - Supabase (Postgres with RLS)|
+-------------------------------------------------------------+
▲ ▲
│ (1. Auth / Token) │ (2. Fetch JWKS /
│ │ Revocations)
+-------------------+------+ +----------+-------------------+
| LLM CLIENTS | | ANY MCP SERVER |
| | | |
| Claude Desktop / Cursor | | Drops in 1 Middleware: |
| ChatGPT / Gemini / Agents| Token | McpAuthMiddleware( |
| Mode 1: OAuth 2.1 (PKCE) | =======> | jwks_uri=".../jwks.json", |
| Mode 2: Static Token | (Header) | audience="mcp-invoicing" |
| (Both emit RS256 JWT) | | ) |
+--------------------------+ | -> ZERO LLM-SPECIFIC CODE! |
+------------------------------+Supabase Setup Guide
1. Create a Supabase Project
Log in to Supabase and create a new project.
Under Project Settings -> API, copy:
Project URL: e.g.,
https://xyzcompany.supabase.coService Role Key (
secret):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...(never expose this key in client-side code).
2. Execute SQL Schema
Navigate to the SQL Editor in your Supabase dashboard and run the contents of supabase/schema.sql:
Creates
mcp_clients,token_events,auth_codes,revoked_tokens, andadmin_users.Configures indexes and unique constraints.
Enables Row Level Security (RLS) on all tables and revokes direct table access from
anon.
3. Configure Environment Variables
Update .env with your Supabase credentials:
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...4. (Optional) Migrate Existing SQLite Data
If you had an existing SQLite database at data/auth.db, import all existing records directly into Supabase:
python scripts/migrate_sqlite_to_supabase.pyEnvironment Variables Reference
Variable | Default | Description | Status |
|
| Supabase project URL ( | Added |
|
| Supabase service role key with RLS bypass | Added |
|
| Alias fallback for service role key | Added |
|
| Port for FastAPI / Uvicorn server | Retained |
|
| Public URL for token issuance and discovery | Retained |
|
| Enforce HTTPS across all endpoints | Retained |
|
| SQLite migration source / offline fallback | Retained |
|
| Directory storing generated RSA keypair | Retained |
|
| Optional RSA private key in PEM format | Retained |
|
| Optional RSA public key in PEM format | Retained |
|
| Initial admin username | Retained |
|
| Initial admin password | Retained |
|
| Secret used to sign admin session tokens | Retained |
|
| Mode 1 OAuth access token lifespan (seconds) | Retained |
|
| Mode 2 static token lifespan (days) | Retained |
|
| PKCE authorization code lifespan (seconds) | Retained |
Adding a new MCP Server (Developer Workflow)
Step 1: Register MCP in the Admin Console
Open
http://localhost:3000/admin.Click "Register New MCP".
Provide the Server Name (e.g.
Invoicing Service) and Audience (e.g.mcp-invoicing).Copy the auto-generated Mode 2 Static Token or note the client credentials for Mode 1.
Step 2: Drop Middleware into Your MCP Server
In Python (FastAPI / Starlette)
Copy sdk/python/mcp_auth_middleware.py into your project:
from fastapi import FastAPI, Request
from mcp_auth_middleware import McpAuthMiddleware
app = FastAPI()
# -------------------------------------------------------------
# THE ONLY AUTH CODE YOU EVER WRITE IN ANY MCP SERVER:
# -------------------------------------------------------------
app.add_middleware(
McpAuthMiddleware,
jwks_uri="http://localhost:3000/.well-known/jwks.json",
audience="mcp-invoicing"
)
# -------------------------------------------------------------
@app.post("/tools/list")
async def list_tools(request: Request):
# request.state.auth contains verified JWT claims
# request.state.mcp_client_id contains client identifier
return {"tools": [{"name": "generate_invoice"}]}In Node.js (Express)
Copy sdk/node/mcp-auth-middleware.js into your project (zero external dependencies):
const express = require('express');
const { createMcpAuthMiddleware } = require('./mcp-auth-middleware');
const app = express();
app.use(express.json());
app.use(createMcpAuthMiddleware({
jwksUri: 'http://localhost:3000/.well-known/jwks.json',
audience: 'mcp-invoicing'
}));
app.post('/tools/list', (req, res) => {
res.json({ tools: [{ name: 'generate_invoice' }] });
});Migration Verification Checklist
The migrated system was tested against the exact same test cases as the original server:
Endpoint / Feature | Method | Status | Verification Detail |
Server Metadata |
| Verified | RFC 8414 metadata, mandates |
Resource Metadata |
| Verified | Protected Resource Metadata (PRM) response |
JWKS Endpoint |
| Verified | RFC 7517 public keys in RS256 format |
Admin Login |
| Verified | Bcrypt verification, issues Admin JWT session |
Admin Stats |
| Verified | Total clients, active clients, event count |
Create MCP Client |
| Verified | Client ID & Secret (shown ONCE) + Mode 2 Token |
Dynamic Registration |
| Verified | RFC 7591 DCR, returns 201 with client metadata |
OAuth 2.1 Authorize |
| Verified | Enforces PKCE S256, redirects with 302 and code |
Token Exchange (PKCE) |
| Verified | Validates |
PKCE Verifier Check |
| Verified | Rejects invalid code verifier (400 Bad Request) |
Code Replay Protection |
| Verified | Rejects reused authorization code (400 Bad Request) |
Client Credentials |
| Verified | Machine-to-machine M2M RS256 token issuance |
Token Introspection |
| Verified | RFC 7662 returns active: true/false |
Revocation Check |
| Verified | Returns live list of revoked client IDs |
Admin Revoke |
| Verified | Immediately invalidates Mode 1 and Mode 2 tokens |
Static Token Gen |
| Verified | Issues fresh 90-day RS256 JWT |
Audit Logging |
| Verified | Records all issuances, revocations, and failures |
Middleware (Python) | ASGI Dispatch | Verified | Validates JWKS signature, aud, exp, revocation |
Middleware (Node) | Express Handler | Verified | Compatible with tokens from migrated server |
Running the Application
1. Set up Virtual Environment
uv venv .venv
uv pip install -r requirements.txt2. Run Automated Test Suite
.venv\Scripts\pytest tests/test_auth.py
# or
.venv\Scripts\python tests/test_auth.pyExecutes all 8 test suites validating 100% feature parity with 100% pass rate.
3. Start the FastAPI Central Auth Server
.venv\Scripts\uvicorn src_py.main:app --port 3000 --reloadOpen http://localhost:3000/admin to access the Control Center.