Skip to content

v2.18.0 - OAuth 2.1 Implementation with Token Refresh

Choose a tag to compare

@ckaraca ckaraca released this 10 Nov 07:13

v2.18.0 - OAuth 2.1 Implementation with Token Refresh 🔐

This release implements full OAuth 2.1 compliance for MCP server authentication, including automatic token refresh, RFC 9728 discovery, and dynamic client registration.

🎯 Major Features

OAuth 2.1 Full Implementation

  • OAuth 2.1 Specification Compliance: Complete implementation of OAuth 2.1 standards
  • Automatic Token Refresh: Tokens are automatically refreshed before expiration
  • Token Rotation: OAuth 2.1 refresh token rotation for enhanced security
  • PKCE (Proof Key for Code Exchange): SHA-256 code challenge support for secure flows
  • State Parameter Binding: HMAC integrity hash to prevent tampering
  • Short-lived States: 5-minute state expiration (OAuth 2.1 requirement)

RFC 9728 OAuth Server Metadata Discovery

  • Automatic discovery of OAuth endpoints from MCP servers
  • Supports WWW-Authenticate header-based discovery
  • Falls back to .well-known/oauth-authorization-server discovery
  • Stores discovered configuration for future use
  • Files: lib/oauth/rfc9728-discovery.ts

RFC 7591 Dynamic Client Registration

  • Automatic client registration with OAuth providers
  • Reuses existing client credentials when available
  • Supports confidential and public client types
  • Stores client credentials securely (encrypted)
  • Files: lib/oauth/dynamic-client-registration.ts

Token Management

  • Encrypted Token Storage: All tokens encrypted at rest (AES-256-GCM)
  • Token Expiration Tracking: Automatic detection of expired tokens
  • Refresh Token Support: Long-lived refresh tokens for continuous access
  • Scope Management: Tracks and validates OAuth scopes
  • Database Tables:
    • mcp_server_oauth_tokens - Encrypted token storage
    • mcp_server_oauth_config - OAuth configuration per server
    • oauth_pkce_states - PKCE state management (5-minute TTL)

🔐 Security Enhancements

SSRF Protection

  • Comprehensive validation of all OAuth URLs
  • Blocks private IP ranges and localhost
  • Prevents access to cloud metadata endpoints
  • Rate-limited endpoint probing (max 2 attempts)
  • Files: lib/oauth/ssrf-protection.ts

Race Condition Protection

  • Optimistic locking for token updates with version field
  • Prevents concurrent token refresh conflicts
  • Detects and reports race conditions
  • Migration: drizzle/0074_gifted_fallen_one.sql

Error Sanitization

  • Production-safe error messages
  • Prevents information disclosure
  • Development mode preserves debugging details
  • Files: lib/oauth/error-sanitization.ts

Reduced Attack Surface

  • Timeouts reduced to 3-5 seconds
  • HTTP Basic Auth for confidential clients (RFC 6749)
  • User-scoped PKCE states (prevents OAuth hijacking)
  • Integrity hash binding for state parameters

📊 Database Schema Changes

New Tables

-- OAuth tokens (encrypted storage)
CREATE TABLE mcp_server_oauth_tokens (
  uuid UUID PRIMARY KEY,
  server_uuid UUID REFERENCES mcp_servers(uuid),
  access_token_encrypted TEXT NOT NULL,
  refresh_token_encrypted TEXT,
  token_type TEXT DEFAULT 'Bearer',
  expires_at TIMESTAMP,
  scopes TEXT[],
  version INTEGER DEFAULT 1,  -- NEW: Optimistic locking
  created_at TIMESTAMP,
  updated_at TIMESTAMP
);

-- OAuth configuration (per server)
CREATE TABLE mcp_server_oauth_config (
  uuid UUID PRIMARY KEY,
  server_uuid UUID REFERENCES mcp_servers(uuid),
  authorization_endpoint TEXT NOT NULL,
  token_endpoint TEXT NOT NULL,
  registration_endpoint TEXT,
  client_id TEXT,
  client_secret_encrypted TEXT,
  scopes TEXT[],
  supports_pkce BOOLEAN DEFAULT true,
  discovery_method TEXT
);

-- PKCE states (5-minute TTL)
CREATE TABLE oauth_pkce_states (
  state TEXT PRIMARY KEY,
  server_uuid UUID REFERENCES mcp_servers(uuid),
  user_id TEXT NOT NULL,  -- Binds to user
  code_verifier TEXT NOT NULL,
  redirect_uri TEXT NOT NULL,
  integrity_hash TEXT NOT NULL,  -- HMAC binding
  expires_at TIMESTAMP NOT NULL
);

Migration Required

pnpm db:migrate

🔄 OAuth Flow

For STREAMABLE_HTTP/SSE Servers:

  1. Discovery: RFC 9728 metadata discovery from server
  2. Registration: Dynamic client registration (if supported)
  3. Authorization: User redirected to authorization endpoint
  4. PKCE: SHA-256 code challenge generated and verified
  5. Token Exchange: Authorization code exchanged for tokens
  6. Storage: Tokens encrypted and stored in database
  7. Refresh: Automatic token refresh before expiration

Supported Discovery Methods:

  • rfc9728 - RFC 9728 OAuth Server Metadata
  • www-authenticate - WWW-Authenticate header parsing
  • manual - Manual configuration

📁 New Files

OAuth Core:

  • lib/oauth/rfc9728-discovery.ts - RFC 9728 metadata discovery
  • lib/oauth/dynamic-client-registration.ts - RFC 7591 registration
  • lib/oauth/oauth-config-store.ts - Configuration management (5-min cache)
  • lib/oauth/integrity.ts - HMAC integrity hash generation
  • lib/oauth/pkce-cleanup.ts - Expired state cleanup

Security:

  • lib/oauth/ssrf-protection.ts - SSRF validation
  • lib/oauth/error-sanitization.ts - Error sanitization

API Routes:

  • app/api/oauth/callback/route.ts - OAuth callback handler

Database:

  • drizzle/0074_gifted_fallen_one.sql - Version field migration

🎯 Server Type Support

Server Type OAuth 2.1 Token Refresh PKCE Discovery
STREAMABLE_HTTP ✅ RFC 9728
SSE ✅ RFC 9728
mcp-remote ⚠️ Handled by mcp-remote ⚠️
STDIO

🔄 Upgrade Instructions

  1. Pull latest changes:

    git pull origin main
  2. Install dependencies:

    pnpm install
  3. Apply database migration:

    pnpm db:migrate
  4. Restart application:

    pnpm build
    pnpm start
  5. Configure OAuth (optional):

    OAUTH_CLIENT_ID=your-client-id  # Fallback if registration fails
    NEXTAUTH_URL=https://your-domain.com  # Required for redirects

🔍 Testing OAuth Flow

  1. Add an MCP server that requires OAuth (e.g., Linear, GitHub)
  2. Click "Authenticate" button when server returns 401
  3. Complete OAuth flow in browser
  4. Tokens are automatically refreshed
  5. Check logs for [OAuth] entries

📊 Monitoring

Monitor these log patterns:

  • [OAuth] RFC 9728 discovery successful
  • [OAuth] PKCE enabled for OAuth flow
  • [OAuth] Using HTTP Basic Authentication
  • [OAuth SSRF] - Blocked SSRF attempts
  • Token was updated by another request - Race condition detected

⚠️ Breaking Changes

None. All changes are backward compatible. Existing mcp-remote OAuth flows continue to work.

🙏 Credits

OAuth 2.1 implementation follows:

  • RFC 6749: OAuth 2.0 Authorization Framework
  • RFC 7636: Proof Key for Code Exchange (PKCE)
  • RFC 7591: OAuth 2.0 Dynamic Client Registration
  • RFC 8707: Resource Indicators for OAuth 2.0
  • RFC 9728: OAuth 2.0 Authorization Server Metadata
  • OAuth 2.1 Draft: Latest security best practices

Full Changelog: v2.17.0...v2.18.0