MCP OAuth Dynamic Client Registration Server - OAuth 2.1 and RFC 7591 compliant
Project description
mcp-oauth-dynamicclient
A production-ready OAuth 2.1 library with Dynamic Client Registration (RFC 7591/7592) support, designed for Model Context Protocol (MCP) integration. This package provides the core OAuth implementation used by the MCP OAuth Gateway.
Overview
This library implements:
- OAuth 2.1: Full authorization server implementation with PKCE
- RFC 7591: Dynamic client registration for self-service OAuth clients
- RFC 7592: Client configuration management endpoints
- GitHub Integration: Built-in GitHub OAuth provider support
- JWT Tokens: Secure token generation with customizable claims
- Redis Backend: Scalable storage for tokens and client data
Key Features
- 🔐 Complete OAuth 2.1 Implementation: Authorization code flow with PKCE
- 📝 Dynamic Client Registration: Self-service client onboarding via RFC 7591
- 🔧 Client Management API: Full CRUD operations via RFC 7592
- 🎫 JWT Token Generation: HS256-signed tokens with configurable lifetime
- 🌐 GitHub OAuth Integration: Pre-configured GitHub provider support
- 📦 Redis Storage: Production-ready persistence layer
- 🔒 ForwardAuth Support: Token validation for reverse proxies
- 📚 Metadata Discovery: RFC 8414 compliant server metadata
Installation
# Via pixi (recommended)
pixi add mcp-oauth-dynamicclient
# Or from source
cd mcp-oauth-dynamicclient
pixi install -e .
Quick Start
1. Environment Configuration
Create a .env file with required settings:
# GitHub OAuth App credentials
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
# JWT signing secret (minimum 32 characters)
GATEWAY_JWT_SECRET=your-secret-key-at-least-32-chars
# Redis connection
REDIS_URL=redis://redis:6379/0
# Optional: Access control
ALLOWED_GITHUB_USERS=user1,user2,user3
# Optional: Client lifetime (seconds, 0 = eternal)
CLIENT_LIFETIME=7776000 # 90 days default
2. Using as a Library
from mcp_oauth_dynamicclient import create_app, Settings
# Create FastAPI application
settings = Settings() # Loads from .env
app = create_app(settings)
# Run with uvicorn
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
3. Using the CLI
# Start OAuth server
pixi run mcp-oauth-server
# Generate GitHub token (for gateway's own use)
pixi run mcp-oauth-token
Architecture
Module Structure
mcp_oauth_dynamicclient/
├── config.py # Pydantic settings management
├── models.py # Data models and schemas
├── keys.py # RSA key generation utilities
├── routes.py # FastAPI route definitions
├── auth_authlib.py # Authlib OAuth server setup
├── resource_protector.py # Token validation
├── async_resource_protector.py # Async token validation
├── redis_client.py # Redis storage operations
├── rfc7592.py # Client management endpoints
├── server.py # FastAPI app factory
└── cli.py # Command-line interface
Core Components
-
OAuth Server (Authlib)
- Authorization code grant with PKCE
- Refresh token support
- Token introspection
-
Dynamic Registration
- Public
/registerendpoint - Automatic client_id/secret generation
- Optional expiration support
- Public
-
Client Management (RFC 7592)
- Bearer token protected endpoints
- GET/PUT/DELETE operations
- Registration access tokens
-
Storage Layer
- Redis for all persistent data
- Configurable TTLs
- Atomic operations
API Reference
OAuth 2.1 Endpoints
POST /register - Dynamic Client Registration
curl -X POST https://auth.example.com/register \
-H "Content-Type: application/json" \
-d '{
"redirect_uris": ["https://app.example.com/callback"],
"client_name": "My MCP Client",
"scope": "read write"
}'
Response includes:
client_id: Unique client identifierclient_secret: Client authentication secretregistration_access_token: Token for managing this registrationregistration_client_uri: URI for client management
GET /authorize - Authorization Endpoint
Initiates OAuth flow with PKCE:
https://auth.example.com/authorize?
client_id=client_xxxxx&
redirect_uri=https://app.example.com/callback&
response_type=code&
state=random_state&
code_challenge=challenge&
code_challenge_method=S256
POST /token - Token Exchange
curl -X POST https://auth.example.com/token \
-d "grant_type=authorization_code" \
-d "code=auth_code" \
-d "client_id=client_xxxxx" \
-d "client_secret=secret_xxxxx" \
-d "code_verifier=verifier"
Client Management (RFC 7592)
GET /register/{client_id}
curl -H "Authorization: Bearer {registration_access_token}" \
https://auth.example.com/register/{client_id}
PUT /register/{client_id}
curl -X PUT https://auth.example.com/register/{client_id} \
-H "Authorization: Bearer {registration_access_token}" \
-H "Content-Type: application/json" \
-d '{"client_name": "Updated Name"}'
DELETE /register/{client_id}
curl -X DELETE https://auth.example.com/register/{client_id} \
-H "Authorization: Bearer {registration_access_token}"
Metadata Discovery
GET /.well-known/oauth-authorization-server
Returns server capabilities and endpoint URLs following RFC 8414.
Token Validation
GET /verify - ForwardAuth Endpoint
Used by reverse proxies to validate tokens:
curl -H "Authorization: Bearer {access_token}" \
https://auth.example.com/verify
Returns headers:
X-User-Id: GitHub user IDX-User-Name: GitHub usernameX-Auth-Token: Token identifier
Integration Examples
With FastAPI Services
from mcp_oauth_dynamicclient import verify_token, get_current_user
from fastapi import Depends
@app.get("/protected")
async def protected_route(user = Depends(get_current_user)):
return {"user": user.name}
With Traefik
labels:
- "traefik.http.middlewares.auth.forwardauth.address=http://auth:8000/verify"
- "traefik.http.middlewares.auth.forwardauth.authResponseHeaders=X-User-Id,X-User-Name"
Security Considerations
Token Security
- JWT tokens signed with HS256
- Configurable expiration (default: 30 days)
- Unique JTI for tracking/revocation
- Stored in Redis with TTL
Client Security
- Cryptographically secure client secrets
- Optional client expiration
- Registration access tokens for management
- Redirect URI validation
PKCE Implementation
- S256 challenge method required
- 43-128 character verifiers
- One-time authorization codes
- 5-minute code expiration
Configuration Reference
| Variable | Description | Default | Required |
|---|---|---|---|
GITHUB_CLIENT_ID |
GitHub OAuth App ID | - | Yes |
GITHUB_CLIENT_SECRET |
GitHub OAuth App Secret | - | Yes |
GATEWAY_JWT_SECRET |
JWT signing secret | - | Yes |
GATEWAY_JWT_ALGORITHM |
JWT algorithm | HS256 | No |
GATEWAY_JWT_EXPIRE_MINUTES |
Token lifetime | 43200 | No |
REDIS_URL |
Redis connection URL | redis://redis:6379/0 | No |
ALLOWED_GITHUB_USERS |
Whitelist (comma-separated) | - | No |
CLIENT_LIFETIME |
Client registration lifetime | 7776000 | No |
MCP_PROTOCOL_VERSION |
MCP protocol version | 2025-06-18 | No |
Development
# Clone repository
git clone https://github.com/atrawog/mcp-oauth-gateway
cd mcp-oauth-gateway/mcp-oauth-dynamicclient
# Install with dev dependencies
pixi install -e .
# Run tests
pixi run pytest tests/ -v
# Run with auto-reload
pixi run uvicorn mcp_oauth_dynamicclient.server:create_app --factory --reload
Testing
The package includes comprehensive tests:
# Unit tests
pixi run pytest tests/test_models.py -v
# Integration tests (requires Redis)
pixi run pytest tests/test_oauth_flow.py -v
# RFC compliance tests
pixi run pytest tests/test_rfc7592.py -v
Redis Key Structure
oauth:state:{state} # OAuth state (5 min TTL)
oauth:code:{code} # Auth codes (5 min TTL)
oauth:token:{jti} # Access tokens (30 day TTL)
oauth:refresh:{token} # Refresh tokens (1 year TTL)
oauth:client:{client_id} # Client registrations
oauth:registration_token:{token} # Registration access tokens
oauth:user_tokens:{username} # User token index
License
Apache License 2.0 - see LICENSE file for details.
Author
Andreas Trawoeger
Links
Project details
Release history Release notifications | RSS feed
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 mcp_oauth_dynamicclient-0.1.2.tar.gz.
File metadata
- Download URL: mcp_oauth_dynamicclient-0.1.2.tar.gz
- Upload date:
- Size: 29.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50ffee3ea8515b8df1a1debc77c65072f68734de36b76e87905776b64a2cc100
|
|
| MD5 |
f9cb90e70f531e4718f04715629d40d1
|
|
| BLAKE2b-256 |
a55311cb629ff8569852c85cca7c0de6799ecfe1f3d2b61426cfbb19aee1416c
|
File details
Details for the file mcp_oauth_dynamicclient-0.1.2-py3-none-any.whl.
File metadata
- Download URL: mcp_oauth_dynamicclient-0.1.2-py3-none-any.whl
- Upload date:
- Size: 30.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8282b0a71aadd15a7894bd32479ec006835bfd164b58aadf871945d8656cbc6d
|
|
| MD5 |
9bccebc7b04944b5504c151553a8cc6a
|
|
| BLAKE2b-256 |
5228b0d0c5995c1d7d03da09e70be42908c388c998ff74319b523edb41a5b2b7
|