Skip to main content
Glama
Muhammad-Usman-Tariq

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-py client.

  • Row Level Security (RLS): RLS is enabled on all tables (mcp_clients, token_events, auth_codes, revoked_tokens, admin_users), completely blocking public anon access while allowing privileged backend operations through the Supabase service_role key.

  • 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 deterministic kid signing.

  • 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

  1. Log in to Supabase and create a new project.

  2. Under Project Settings -> API, copy:

    • Project URL: e.g., https://xyzcompany.supabase.co

    • Service 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, and admin_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.py

Environment Variables Reference

Variable

Default

Description

Status

SUPABASE_URL

None

Supabase project URL (https://xyz.supabase.co)

Added

SUPABASE_SERVICE_ROLE_KEY

None

Supabase service role key with RLS bypass

Added

SUPABASE_KEY

None

Alias fallback for service role key

Added

PORT

3000

Port for FastAPI / Uvicorn server

Retained

ISSUER_URL

http://localhost:3000

Public URL for token issuance and discovery

Retained

REQUIRE_HTTPS

false

Enforce HTTPS across all endpoints

Retained

DB_PATH

./data/auth.db

SQLite migration source / offline fallback

Retained

KEYS_DIR

./.keys

Directory storing generated RSA keypair

Retained

PRIVATE_KEY_PEM

None

Optional RSA private key in PEM format

Retained

PUBLIC_KEY_PEM

None

Optional RSA public key in PEM format

Retained

ADMIN_USERNAME

admin

Initial admin username

Retained

ADMIN_PASSWORD

admin-mcp-secret-2026

Initial admin password

Retained

ADMIN_JWT_SECRET

central-mcp-...

Secret used to sign admin session tokens

Retained

ACCESS_TOKEN_EXPIRY

3600

Mode 1 OAuth access token lifespan (seconds)

Retained

STATIC_TOKEN_EXPIRY_DAYS

90

Mode 2 static token lifespan (days)

Retained

AUTH_CODE_EXPIRY_SECONDS

300

PKCE authorization code lifespan (seconds)

Retained


Adding a new MCP Server (Developer Workflow)

Step 1: Register MCP in the Admin Console

  1. Open http://localhost:3000/admin.

  2. Click "Register New MCP".

  3. Provide the Server Name (e.g. Invoicing Service) and Audience (e.g. mcp-invoicing).

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

GET /.well-known/oauth-authorization-server

Verified

RFC 8414 metadata, mandates S256 PKCE

Resource Metadata

GET /.well-known/oauth-protected-resource

Verified

Protected Resource Metadata (PRM) response

JWKS Endpoint

GET /.well-known/jwks.json

Verified

RFC 7517 public keys in RS256 format

Admin Login

POST /admin/login

Verified

Bcrypt verification, issues Admin JWT session

Admin Stats

GET /admin/api/stats

Verified

Total clients, active clients, event count

Create MCP Client

POST /admin/api/clients

Verified

Client ID & Secret (shown ONCE) + Mode 2 Token

Dynamic Registration

POST /register

Verified

RFC 7591 DCR, returns 201 with client metadata

OAuth 2.1 Authorize

GET /authorize

Verified

Enforces PKCE S256, redirects with 302 and code

Token Exchange (PKCE)

POST /token

Verified

Validates code_verifier, issues RS256 Bearer token

PKCE Verifier Check

POST /token

Verified

Rejects invalid code verifier (400 Bad Request)

Code Replay Protection

POST /token

Verified

Rejects reused authorization code (400 Bad Request)

Client Credentials

POST /token

Verified

Machine-to-machine M2M RS256 token issuance

Token Introspection

POST /introspect

Verified

RFC 7662 returns active: true/false

Revocation Check

GET /revocations

Verified

Returns live list of revoked client IDs

Admin Revoke

POST /admin/api/clients/{id}/revoke

Verified

Immediately invalidates Mode 1 and Mode 2 tokens

Static Token Gen

POST /admin/api/clients/{id}/static-token

Verified

Issues fresh 90-day RS256 JWT

Audit Logging

GET /admin/api/audit

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

2. Run Automated Test Suite

.venv\Scripts\pytest tests/test_auth.py
# or
.venv\Scripts\python tests/test_auth.py

Executes 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 --reload

Open http://localhost:3000/admin to access the Control Center.