# OAuth: Python vs TypeScript Implementation This document compares the OAuth implementations between Python FastMCP and TypeScript FastMCP, covering **both server-side (OAuth Proxy) and client-side (OAuth Client)** functionality. ## Executive Summary This comparison covers two distinct areas: ### Part A: OAuth Proxy (Server-Side) - ✅ Full Parity The TypeScript implementation is a **comprehensive and faithful port** of the Python FastMCP OAuth proxy for **protecting MCP servers**. Both versions provide: - OAuth 2.1 proxy functionality - Dynamic Client Registration (DCR) - Two-tier PKCE security - User consent flow - Token swap pattern - Pre-configured providers - Flexible storage backends **For MCP server development, TypeScript and Python have complete feature parity.** ### Part B: OAuth Client (Client-Side) - ℹ️ Python Only Python FastMCP includes an **OAuth Client** component for building client applications (CLI tools, desktop apps) that connect to OAuth-protected servers. TypeScript does not include this client-side tooling. **Key distinction:** OAuth Proxy (server) and OAuth Client (client) serve different purposes: - **Server-Side (OAuthProxy)**: Protects your MCP server with OAuth authentication - **Client-Side (OAuthClient)**: Helps client apps authenticate to protected servers The main differences lie in **dependency management** and **client-side tooling availability**. --- # Part A: OAuth Proxy (Server-Side) Comparison This section compares server-side OAuth Proxy functionality for **protecting MCP servers**. ## Server-Side Feature Comparison Matrix | Feature | Python FastMCP | TypeScript FastMCP | Notes | | -------------------------------- | ------------------- | ----------------------------- | --------------------------------------- | | **Core Proxy** | ✅ | ✅ | Identical functionality | | **Dynamic Client Registration** | ✅ | ✅ | RFC 7591 compliant | | **Authorization Code Flow** | ✅ | ✅ | Full OAuth 2.1 support | | **PKCE Support** | ✅ (S256 only) | ✅ (S256 + plain) | TypeScript supports both methods | | **Refresh Token Flow** | ✅ | ✅ | Identical | | **Token Swap Pattern** | ✅ (default) | ✅ (default) | Both enabled by default | | **Consent Screen** | ✅ | ✅ | Both have full HTML UI | | **Pre-configured Providers** | ✅ Multiple | ✅ Google, GitHub, Azure | Similar approach | | **Storage Interface** | ✅ `AsyncKeyValue` | ✅ `TokenStorage` | Different interface names, same concept | | **In-Memory Storage** | ✅ | ✅ | Available in both | | **Disk Storage** | ✅ `DiskStore` | ✅ `DiskStore` | Similar implementation | | **Encrypted Storage** | ✅ Fernet (default) | ✅ AES-256-GCM (default) | Both encrypt by default | | **JWT Issuer** | ✅ python-jose | ✅ Custom HS256 | Different libraries, same functionality | | **JWT Algorithms** | ✅ HS256, RS256 | ✅ HS256 (RS256 via jose) | Python built-in, TypeScript optional | | **JWKS Support** | ✅ Built-in | ✅ Optional (requires `jose`) | Both supported, TypeScript opt-in | | **Automatic Route Registration** | ✅ | ✅ | Seamless integration in both | | **Discovery Metadata** | ✅ | ✅ | RFC 8414 compliant | | **Error Handling** | ✅ authlib errors | ✅ `OAuthProxyError` | Similar standardized errors | | **Token Rotation Tracking** | ✅ Advanced | ⚠️ Basic | Python has more sophisticated tracking | **Result:** TypeScript has **complete server-side parity** with Python for MCP server development. ## Architecture Comparison ### Python Implementation ``` fastmcp-python/src/fastmcp/server/auth/ ├── oauth_proxy.py # Main proxy class ├── jwt_issuer.py # JWT token handling ├── providers/ # Pre-configured providers │ ├── google.py │ ├── github.py │ └── ... └── storage/ # Storage backends ├── disk_store.py └── ... Dependencies: - authlib (OAuth client mechanics) - httpx (HTTP requests) - cryptography (Fernet encryption) - pydantic (data validation) - py-key-value-aio (storage abstraction) ``` ### TypeScript Implementation ``` fastmcp/src/auth/ ├── OAuthProxy.ts # Main proxy class ├── types.ts # Type definitions ├── utils/ │ ├── pkce.ts # PKCE utilities │ ├── tokenStore.ts # Storage implementations │ ├── diskStore.ts # Disk storage │ ├── jwtIssuer.ts # JWT handling │ └── consent.ts # Consent management └── providers/ # Pre-configured providers ├── GoogleProvider.ts ├── GitHubProvider.ts └── AzureProvider.ts Dependencies: - crypto (Node.js built-in) - fs/promises (Node.js built-in) - undici (HTTP, already in dependencies) ``` **Key Difference:** TypeScript uses mostly built-in Node.js modules, while Python relies on external battle-tested libraries. ## API Comparison ### Creating an OAuth Server **Python:** ```python from fastmcp import FastMCP from fastmcp.server.auth import OAuthProxy auth = OAuthProxy( upstream_authorization_endpoint="https://provider.com/oauth/authorize", upstream_token_endpoint="https://provider.com/oauth/token", upstream_client_id="client-id", upstream_client_secret="client-secret", base_url="https://your-server.com" ) mcp = FastMCP(name="My Server", auth=auth) ``` **TypeScript:** ```typescript import { FastMCP, OAuthProvider } from "fastmcp"; const server = new FastMCP({ auth: new OAuthProvider({ authorizationEndpoint: "https://provider.com/oauth/authorize", baseUrl: "https://your-server.com", clientId: "client-id", clientSecret: "client-secret", tokenEndpoint: "https://provider.com/oauth/token", }), name: "My Server", version: "1.0.0", }); ``` **Differences:** Minimal - both use a simple `auth` option with camelCase vs snake_case naming. ### Using Pre-configured Providers **Python:** ```python from fastmcp.server.auth import GoogleProvider auth = GoogleProvider( client_id="xxx.apps.googleusercontent.com", client_secret="secret", base_url="https://your-server.com", scopes=["openid", "profile", "email"] ) mcp = FastMCP(name="My Server", auth=auth) ``` **TypeScript:** ```typescript import { FastMCP, GoogleProvider } from "fastmcp"; const server = new FastMCP({ auth: new GoogleProvider({ baseUrl: "https://your-server.com", clientId: "xxx.apps.googleusercontent.com", clientSecret: "secret", scopes: ["openid", "profile", "email"], }), name: "My Server", version: "1.0.0", }); ``` **Differences:** Minimal - both use `auth` option with camelCase vs snake_case naming. ### Token Swap Pattern **Python (Default Behavior):** ```python auth = OAuthProxy( # ... config # Token swap enabled by default! ) # Upstream tokens automatically stored # Clients receive FastMCP JWTs ``` **TypeScript (Same - Enabled by Default):** ```typescript const auth = new OAuthProxy({ // ... config // Token swap enabled by default! // jwtSigningKey auto-generated if not provided (recommended to provide your own) jwtSigningKey: "signing-key", // Optional but recommended }); // Clients receive FastMCP JWTs // Load upstream tokens when needed: const upstreamTokens = await auth.loadUpstreamTokens(clientToken); ``` **Parity:** Both Python and TypeScript now enable token swap by default for enhanced security. ### Storage Configuration **Python (Encrypted by Default):** ```python from py_key_value.providers import DiskStore from py_key_value.middlewares import FernetEncryptionWrapper # Encrypted disk storage is default auth = OAuthProxy( # ... config storage=DiskStore(directory="/var/lib/fastmcp") ) ``` **TypeScript (Encrypted by Default):** ```typescript import { DiskStore } from "fastmcp/auth"; // Encryption is automatic! Just provide storage const auth = new OAuthProxy({ // ... config tokenStorage: new DiskStore({ directory: "/var/lib/fastmcp" }), // encryptionKey: "custom-key", // Optional - auto-generated if not provided }); // Or use in-memory with encryption (default) const auth2 = new OAuthProxy({ // ... config // Storage defaults to encrypted MemoryTokenStorage }); ``` **Parity:** Both Python and TypeScript now encrypt storage by default. TypeScript auto-generates encryption keys. ### JWT Key Derivation **Python:** ```python from fastmcp.server.auth import derive_key jwt_key = derive_key(secret, iterations=100000) ``` **TypeScript:** ```typescript import { JWTIssuer } from "fastmcp/auth"; const jwtKey = await JWTIssuer.deriveKey(secret, 100000); ``` **Similarity:** Same PBKDF2 key derivation approach. ## Default Behaviors | Aspect | Python | TypeScript | Recommendation | | -------------------- | ---------------- | --------------------- | ---------------------------- | | **Token Swap** | Enabled | Enabled | ✅ Secure by default | | **Storage** | Encrypted Disk | In-Memory (encrypted) | Use DiskStore for production | | **Encryption** | Enabled (Fernet) | Enabled (AES-256-GCM) | ✅ Secure by default | | **Consent Screen** | Required | Required | ✅ Keep enabled | | **PKCE** | S256 only | S256 + plain | Use S256 | | **Cleanup Interval** | 60s | 60s | ✅ Same default | ## Migration Guide: Python to TypeScript ### Step 1: Install Dependencies **Python:** ```bash pip install fastmcp ``` **TypeScript:** ```bash npm install fastmcp ``` ### Step 2: Update Imports **Python:** ```python from fastmcp import FastMCP from fastmcp.server.auth import OAuthProxy, GoogleProvider ``` **TypeScript:** ```typescript import { FastMCP, OAuthProvider, GoogleProvider, requireAuth } from "fastmcp"; ``` ### Step 3: Convert Configuration **Python:** ```python auth = GoogleProvider( client_id=os.environ["GOOGLE_CLIENT_ID"], client_secret=os.environ["GOOGLE_CLIENT_SECRET"], base_url="https://example.com", scopes=["openid", "profile"] ) mcp = FastMCP(name="My Server", auth=auth) ``` **TypeScript:** ```typescript const server = new FastMCP({ auth: new GoogleProvider({ baseUrl: "https://example.com", clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, scopes: ["openid", "profile"], }), name: "My Server", version: "1.0.0", }); ``` ### Step 4: Update Token Access **Python:** ```python @mcp.tool() async def protected_tool(session: Session): # Access user token token = session.auth_token # Use token to call upstream API ``` **TypeScript:** ```typescript import { requireAuth, getAuthSession } from "fastmcp"; server.addTool({ canAccess: requireAuth, name: "protected-tool", execute: async (_args, { session }) => { const { accessToken } = getAuthSession(session); const response = await fetch("https://api.provider.com/user", { headers: { Authorization: `Bearer ${accessToken}` }, }); return JSON.stringify(await response.json()); }, }); ``` ### Step 5: Adjust Storage (If Using Disk) **Python:** ```python from py_key_value.providers import DiskStore auth = OAuthProxy( # ... config storage=DiskStore(directory="/var/lib/fastmcp") ) ``` **TypeScript:** ```typescript import { OAuthProvider, DiskStore } from "fastmcp/auth"; const server = new FastMCP({ auth: new OAuthProvider({ // ... config tokenStorage: new DiskStore({ directory: "/var/lib/fastmcp" }), }), name: "My Server", version: "1.0.0", }); ``` --- # Part B: OAuth Client (Client-Side) Comparison This section compares client-side OAuth functionality for **building applications that connect to OAuth-protected MCP servers** (CLI tools, desktop apps, etc.). ## Client-Side Feature Comparison Matrix | Feature | Python FastMCP | TypeScript FastMCP | Notes | | ------------------------- | ---------------- | ------------------ | ------------------------------------------------ | | **OAuth Client Class** | ✅ `OAuthClient` | ❌ | Python includes complete client implementation | | **Browser Launching** | ✅ Automatic | ❌ | Opens browser for authorization | | **Local Callback Server** | ✅ Automatic | ❌ | Handles OAuth redirects with auto port selection | | **Token Management** | ✅ Automatic | ❌ | Storage and retrieval | | **Token Refresh** | ✅ Automatic | ❌ | Background refresh handling | | **PKCE Flow** | ✅ Client-side | ❌ | Automatic verifier generation | | **Timeout Handling** | ✅ 5 min default | ❌ | Callback timeout management | **Result:** Python provides client-side OAuth tooling. TypeScript does not (client developers must implement OAuth manually). ## Python Client-Side Implementation **Python includes a complete OAuth client for connecting to protected servers:** ```python from fastmcp.client.auth import OAuthClient client = OAuthClient( authorization_endpoint="https://server.com/oauth/authorize", token_endpoint="https://server.com/oauth/token" ) # Automatically handles: # - Browser launching for user authorization # - Local callback server (auto port selection) # - PKCE generation and validation # - Token storage and refresh tokens = await client.authenticate() ``` **TypeScript:** Not available. Client developers must implement their own OAuth flow or use third-party libraries. ## Server-Side Features (Python-Specific) These features are server-side but Python-specific: ### 1. Multiple JWT Algorithms (Built-in) **Python:** Supports HS256, RS256, ES256, etc. via python-jose (built-in) **TypeScript:** HS256 built-in, RS256/ES256 available via optional `jose` package ### 2. More Storage Backends Out-of-the-Box **Python:** Via py-key-value: Redis, DynamoDB, Elasticsearch, etc. **TypeScript:** Memory and Disk only (custom implementations required for others) ## What TypeScript Has That Python Doesn't ### 1. Plain PKCE Method **TypeScript:** Supports both S256 and plain PKCE challenges **Python:** S256 only ### 2. More Granular Token Swap Control **TypeScript:** Opt-in token swap with explicit configuration **Python:** Token swap is always enabled ### 3. TypeScript Type Safety **TypeScript:** Full compile-time type checking **Python:** Runtime validation with Pydantic ### 4. Minimal Dependencies **TypeScript:** Built on Node.js core modules **Python:** Requires authlib, httpx, cryptography, etc. ## Performance Characteristics | Metric | Python | TypeScript | Notes | | ----------------------- | ------------------ | ------------------ | --------------------------- | | **Startup Time** | Fast | Instant | Both are quick | | **Memory (Base)** | ~15MB | ~10MB | TypeScript slightly lighter | | **OAuth Flow** | <100ms | <100ms | Similar performance | | **Storage Overhead** | Depends on backend | Depends on backend | Similar | | **Encryption Overhead** | Fernet (~5%) | AES-GCM (~3%) | Negligible difference | ## Security Comparison Both implementations provide equivalent security: | Security Feature | Python | TypeScript | | --------------------------- | ------------ | ------------ | | **Two-tier PKCE** | ✅ | ✅ | | **User Consent** | ✅ | ✅ | | **Encrypted Storage** | ✅ (default) | ✅ (default) | | **HMAC-signed Cookies** | ✅ | ✅ | | **State Validation** | ✅ | ✅ | | **Redirect URI Validation** | ✅ | ✅ | | **One-time Auth Codes** | ✅ | ✅ | | **OAuth 2.1 Compliance** | ✅ | ✅ | **Parity:** Both implementations provide equivalent security with encryption enabled by default. ## Testing Coverage **Python:** - pytest-based test suite - Comprehensive unit and integration tests - Mock-based OAuth provider testing **TypeScript:** - Vitest-based test suite - 29+ tests covering all core functionality - PKCE, storage, JWT, consent, and integration tests Both have solid test coverage. ## Conclusion ### Server-Side (OAuth Proxy): ✅ Full Parity Both Python and TypeScript implementations are **production-ready** and provide **complete feature parity** for OAuth proxy functionality. For **MCP server development**, choose based on: 1. **Language/Runtime Preference** - Python vs Node.js/TypeScript 2. **Dependency Philosophy** - Python uses external libraries (authlib), TypeScript uses built-in modules 3. **Storage Defaults** - Python defaults to disk, TypeScript defaults to in-memory (both support both) 4. **JWT Algorithms** - Python has all built-in, TypeScript has HS256 built-in (RS256/ES256 via optional jose) **Both implementations encrypt by default and enable token swap by default.** ### Client-Side (OAuth Client): ℹ️ Python Only Python FastMCP includes an **OAuth Client** component for building client applications (CLI tools, desktop apps) that connect to OAuth-protected servers. TypeScript does not include this client-side tooling. **For client application development:** Choose Python if you want automatic browser-based OAuth flow handling, or implement your own OAuth client in TypeScript using third-party libraries. ### Migration from Python to TypeScript For **server-side OAuth proxy**, migration is straightforward with minimal code changes, primarily adjusting for camelCase naming. Both implementations now have matching defaults (token swap enabled, encryption enabled). ## Resources ### Python FastMCP - Repository: [jlowin/fastmcp](https://github.com/jlowin/fastmcp) - Documentation: [fastmcp.io](https://fastmcp.io) ### TypeScript FastMCP - Repository: [FastMCP TypeScript](https://github.com/your-org/fastmcp) - Documentation: See `docs/` directory - [OAuth Proxy Features](oauth-proxy-features.md) - [OAuth Proxy Guide](oauth-proxy-guide.md)