Skip to content

Latest commit

 

History

History
369 lines (278 loc) · 12.2 KB

File metadata and controls

369 lines (278 loc) · 12.2 KB

OAuth MCP proxy

OAuth 2.1 authentication library for Go MCP servers.

Supports both MCP SDKs:

  • mark3labs/mcp-go
  • modelcontextprotocol/go-sdk (official)

One-time setup: Configure provider + add WithOAuth() to your server. Result: All tools automatically protected with token validation and caching.

mark3labs/mcp-go

import "github.com/tuannvm/oauth-mcp-proxy/mark3labs"

oauthServer, oauthOption, _ := mark3labs.WithOAuth(mux, &oauth.Config{
    Provider: "okta",
    Issuer:   "https://your-company.okta.com",
    Audience: "api://your-mcp-server",
})

mcpServer := server.NewMCPServer("Server", "1.0.0", oauthOption)
streamable := server.NewStreamableHTTPServer(mcpServer, /*options*/)
mux.HandleFunc("/mcp", oauthServer.WrapMCPEndpoint(streamable))

Official SDK

import mcpoauth "github.com/tuannvm/oauth-mcp-proxy/mcp"

mcpServer := mcp.NewServer(&mcp.Implementation{...}, nil)
_, handler, _ := mcpoauth.WithOAuth(mux, cfg, mcpServer)
http.ListenAndServe(":8080", handler)

GitHub Workflow Status Go Version Go Report Card Go Reference GitHub Release License: MIT


Why Use This Library?

  • Dual SDK support - Works with both mark3labs and official SDKs
  • Simple integration - One WithOAuth() call protects all tools
  • Automatic 401 handling - RFC 6750 compliant error responses with OAuth discovery
  • Zero per-tool config - All tools automatically protected
  • Fast token caching - 5-min cache with JWT expiry awareness
  • Security hardened - State replay protection, DoS prevention, input validation
  • Built-in rate limiting - Token-based rate limiter included
  • CORS support - OPTIONS pass-through for browser clients
  • Multiple providers - HMAC, Okta, Google, Azure AD

How It Works

Request Flow

sequenceDiagram
    participant Client
    participant MCP Server
    box lightyellow oauth-mcp-proxy Library
    participant Middleware
    participant Cache
    participant Provider
    end
    participant Your Tool Handler

    Client->>MCP Server: Request + Bearer token
    MCP Server->>Middleware: WithOAuth() intercepts

    alt Token in cache and fresh
        Middleware->>Cache: Check token hash
        Cache-->>Middleware: Return cached user
    else Token not cached or expired
        Middleware->>Provider: Validate token (HMAC/OIDC)
        Provider-->>Middleware: User claims
        Middleware->>Cache: Store user for 5 minutes
    end

    Middleware->>Your Tool Handler: Pass request with user in context
    Your Tool Handler->>Your Tool Handler: GetUserFromContext(ctx)
    Your Tool Handler-->>Client: Send response
Loading

Token Validation Flow

flowchart TB
    Start([Your MCP Server receives request]) --> Extract[oauth-mcp-proxy: Extract Token]
    Extract --> Hash[oauth-mcp-proxy: SHA-256 Hash]
    Hash --> CheckCache{oauth-mcp-proxy: Token Cached?}

    CheckCache -->|Cache Hit| GetUser[oauth-mcp-proxy: Get Cached User]
    CheckCache -->|Cache Miss| Validate{oauth-mcp-proxy: Validate}

    Validate -->|Valid| Claims[oauth-mcp-proxy: Extract Claims]
    Validate -->|Invalid| Reject([Return 401])

    Claims --> Store[oauth-mcp-proxy: Cache]
    Store --> GetUser

    GetUser --> Context[oauth-mcp-proxy: Add User to Context]
    Context --> Tool[Your Tool Handler: GetUserFromContext]
    Tool --> Response([Your MCP Server: Return Response])

    style Start fill:#e8f5e9
    style Extract fill:#fff9c4
    style Hash fill:#fff9c4
    style CheckCache fill:#fff9c4
    style Validate fill:#fff9c4
    style Claims fill:#fff9c4
    style Store fill:#fff9c4
    style GetUser fill:#fff9c4
    style Context fill:#fff9c4
    style Tool fill:#e8f5e9
    style Response fill:#e8f5e9
    style Reject fill:#ffebee
Loading

What oauth-mcp-proxy does:

  1. Extracts Bearer tokens from HTTP requests
  2. Validates against your OAuth provider (with caching)
  3. Adds authenticated user to request context
  4. All your tools automatically protected

🔒 Security Features

Production-ready security hardening built-in:

State Replay Protection

  • Timestamp + nonce validation - States include timestamp and nonce for replay attack prevention
  • Automatic nonce cleanup - Expired nonces removed before replay check (prevents memory leaks)
  • Rolling deploy compatible - Accepts legacy states without timestamp/nonce for zero-downtime upgrades

Token Security

  • JWT expiry-aware caching - Cache respects token expiration time (uses min(token.expiry, now+5min))
  • Constant-time HMAC comparison - Timing attack prevention for signature verification
  • Secure nonce generation - Panics on crypto/rand failure (no weak fallback)

Input Validation & DoS Prevention

  • Parameter length limits - code, state, code_challenge validated to prevent abuse
  • Request body size limits - MaxBytesReader on token endpoint (1MB), registration (256KB)
  • Issuer URL validation - Enforced HTTPS for non-localhost OIDC providers

Session Management (Official SDK)

  • auth.TokenInfo population - Populates go-sdk auth context for session binding
  • User-based session tracking - Prevents session hijacking via user ID verification

HTTP Security

  • Security headers - CSP, X-Frame-Options, X-Content-Type-Options, Cache-Control
  • CORS support - OPTIONS pass-through for browser clients
  • RFC 6750 compliant - Proper WWW-Authenticate headers with resource_metadata

Built-in Rate Limiting

// Simple token-based rate limiter included
limiter := oauth.NewRateLimiter(time.Minute, 100)
if !limiter.Allow("client-ip") {
    http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
}

Breaking Changes (Security Hardening)

v1.0.0 → v1.1.0

The following security improvements introduce breaking changes:

1. Issuer URL Validation (CRITICAL)

  • Change: OIDC providers now enforce HTTPS validation for issuer URLs
  • Impact: Invalid issuer URLs will cause NewServer() to fail
  • Migration: Ensure your Issuer config uses HTTPS (or localhost for testing)
    // ✅ Valid
    Issuer: "https://company.okta.com"
    Issuer: "http://localhost:8080"  // Testing only
    
    // ❌ Invalid - will fail validation
    Issuer: "http://company.okta.com"  // Not localhost
    Issuer: "company.okta.com"         // Missing scheme

2. State Signing Key Initialization

  • Change: NewServer() now panics if state signing key cannot be generated
  • Impact: Server startup will fail if crypto/rand fails (should never happen on healthy systems)
  • Migration: Ensure your system has a working CSPRNG. No code changes needed.

3. Nonce Generation Failure Behavior

  • Change: generateSecureNonce() now panics instead of falling back to weak timestamp-based nonces
  • Impact: OAuth authorization requests will fail if crypto/rand fails
  • Migration: Ensure your system has a working CSPRNG. No code changes needed.

4. Error Message Simplification

  • Change: Security-sensitive error messages are less verbose to prevent information leakage
  • Impact: Debugging authentication failures may require checking logs
  • Migration: Use server logs for detailed debugging; client errors are intentionally generic

No Migration Needed For

  • Token cache expiry fix - Fully backwards compatible
  • State replay protection - Legacy states without timestamp/nonce still accepted
  • Input validation - Only affects malformed requests
  • go-sdk adapter fixes - Fully backwards compatible

Quick Start

Using mark3labs/mcp-go

1. Install

go get github.com/tuannvm/oauth-mcp-proxy

2. Add to Your Server

import (
    oauth "github.com/tuannvm/oauth-mcp-proxy"
    "github.com/tuannvm/oauth-mcp-proxy/mark3labs"
)

mux := http.NewServeMux()

// Enable OAuth (one time setup)
oauthServer, oauthOption, _ := mark3labs.WithOAuth(mux, &oauth.Config{
    Provider: "okta",                    // or "hmac", "google", "azure"
    Issuer:   "https://your-company.okta.com",
    Audience: "api://your-mcp-server",
    ServerURL: "https://your-server.com",
})

// Create MCP server with OAuth
mcpServer := mcpserver.NewMCPServer("Server", "1.0.0", oauthOption)

// Add tools - all automatically protected
mcpServer.AddTool(myTool, myHandler)

// Setup endpoint with automatic 401 handling
streamable := mcpserver.NewStreamableHTTPServer(
    mcpServer,
    mcpserver.WithHTTPContextFunc(oauth.CreateHTTPContextFunc()),
)
mux.HandleFunc("/mcp", oauthServer.WrapMCPEndpoint(streamable))

3. Access Authenticated User

func myHandler(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    user, ok := oauth.GetUserFromContext(ctx)
    if !ok {
        return nil, fmt.Errorf("authentication required")
    }
    // Use user.Username, user.Email, user.Subject
}

Using Official SDK

1. Install

go get github.com/modelcontextprotocol/go-sdk
go get github.com/tuannvm/oauth-mcp-proxy

2. Add to Your Server

import (
    "github.com/modelcontextprotocol/go-sdk/mcp"
    oauth "github.com/tuannvm/oauth-mcp-proxy"
    mcpoauth "github.com/tuannvm/oauth-mcp-proxy/mcp"
)

mux := http.NewServeMux()

// Create MCP server
mcpServer := mcp.NewServer(&mcp.Implementation{
    Name:    "my-server",
    Version: "1.0.0",
}, nil)

// Add tools
mcp.AddTool(mcpServer, &mcp.Tool{
    Name: "greet",
    Description: "Greet user",
}, func(ctx context.Context, req *mcp.CallToolRequest, params *struct{}) (*mcp.CallToolResult, any, error) {
    user, _ := oauth.GetUserFromContext(ctx)
    return &mcp.CallToolResult{
        Content: []mcp.Content{
            &mcp.TextContent{Text: "Hello, " + user.Username},
        },
    }, nil, nil
})

// Add OAuth protection
_, handler, _ := mcpoauth.WithOAuth(mux, &oauth.Config{
    Provider: "okta",
    Issuer:   "https://your-company.okta.com",
    Audience: "api://your-mcp-server",
}, mcpServer)

http.ListenAndServe(":8080", handler)

Your MCP server now requires OAuth authentication.


Examples

See examples/README.md for detailed setup guide including Okta configuration.

SDK Example Description
mark3labs Simple Minimal setup - copy/paste ready
mark3labs Advanced ConfigBuilder, multiple tools, logging
Official Simple Minimal setup - copy/paste ready
Official Advanced ConfigBuilder, multiple tools, logging

Supported Providers

Provider Best For Setup Guide
HMAC Testing, development docs/providers/HMAC.md
Okta Enterprise SSO docs/providers/OKTA.md
Google Google Workspace docs/providers/GOOGLE.md
Azure AD Microsoft 365 docs/providers/AZURE.md

Documentation

Getting Started:

Advanced:


License

MIT License - See LICENSE