Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
OAuth MCP Proxy
https://github.com/tuannvm/oauth-mcp-proxy
Admin
OAuth MCP Proxy is an OAuth 2.1 authentication library for Go MCP servers that provides token
...
Tokens:
56,834
Snippets:
438
Trust Score:
9.4
Update:
2 months ago
Context
Skills
Chat
Benchmark
78
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# OAuth MCP Proxy OAuth MCP Proxy is an OAuth 2.1 authentication library designed for Go MCP (Model Context Protocol) servers. It provides server-side OAuth integration with minimal setup, requiring just a single `WithOAuth()` call to protect all MCP tools with token validation and caching. The library supports both the `mark3labs/mcp-go` SDK and the official `modelcontextprotocol/go-sdk`. The library implements comprehensive OAuth 2.0/2.1 features including OIDC discovery, PKCE support, token caching with 5-minute TTL, and automatic 401 handling with RFC 6750 compliant error responses. It supports multiple authentication providers including HMAC (for development/testing), Okta, Google, and Azure AD, all configurable through a fluent builder API or environment variables. ## mark3labs.WithOAuth - Enable OAuth for mark3labs/mcp-go SDK Creates an OAuth-protected MCP server using the mark3labs/mcp-go SDK. This function creates the OAuth server instance, registers all OAuth HTTP endpoints on the provided mux, and returns a server option that adds authentication middleware to all MCP tools automatically. ```go package main import ( "context" "fmt" "log" "net/http" "github.com/mark3labs/mcp-go/mcp" mcpserver "github.com/mark3labs/mcp-go/server" oauth "github.com/tuannvm/oauth-mcp-proxy" "github.com/tuannvm/oauth-mcp-proxy/mark3labs" ) func main() { mux := http.NewServeMux() // Enable OAuth with Okta provider oauthServer, oauthOption, err := mark3labs.WithOAuth(mux, &oauth.Config{ Provider: "okta", Issuer: "https://dev-12345.okta.com", Audience: "api://my-mcp-server", ServerURL: "http://localhost:8080", }) if err != nil { log.Fatalf("OAuth setup failed: %v", err) } // Create MCP server with OAuth middleware mcpServer := mcpserver.NewMCPServer("My Server", "1.0.0", oauthOption) // Add tools - all automatically OAuth-protected mcpServer.AddTool( mcp.Tool{Name: "greet", Description: "Greets the authenticated user"}, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { user, ok := oauth.GetUserFromContext(ctx) if !ok { return nil, fmt.Errorf("authentication required") } return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", user.Username)), nil }, ) // Setup MCP endpoint with automatic 401 handling streamableServer := mcpserver.NewStreamableHTTPServer( mcpServer, mcpserver.WithEndpointPath("/mcp"), mcpserver.WithHTTPContextFunc(oauth.CreateHTTPContextFunc()), ) mux.HandleFunc("/mcp", oauthServer.WrapMCPEndpoint(streamableServer)) log.Fatal(http.ListenAndServe(":8080", mux)) } ``` ## mcpoauth.WithOAuth - Enable OAuth for Official SDK Creates an OAuth-protected HTTP handler for the official `modelcontextprotocol/go-sdk`. This function automatically wraps the MCP StreamableHTTPHandler with OAuth token validation and returns a ready-to-use HTTP handler that includes automatic 401 handling. ```go package main import ( "context" "fmt" "log" "net/http" "github.com/modelcontextprotocol/go-sdk/mcp" oauth "github.com/tuannvm/oauth-mcp-proxy" mcpoauth "github.com/tuannvm/oauth-mcp-proxy/mcp" ) func main() { mux := http.NewServeMux() // Create MCP server using official SDK mcpServer := mcp.NewServer(&mcp.Implementation{ Name: "My OAuth Server", Version: "1.0.0", }, nil) // Add tools with authenticated user access type GreetParams struct{} mcp.AddTool(mcpServer, &mcp.Tool{ Name: "greet", Description: "Greets the authenticated user", }, func(ctx context.Context, req *mcp.CallToolRequest, params *GreetParams) (*mcp.CallToolResult, any, error) { user, ok := oauth.GetUserFromContext(ctx) if !ok { return nil, nil, fmt.Errorf("authentication required") } return &mcp.CallToolResult{ Content: []mcp.Content{ &mcp.TextContent{Text: fmt.Sprintf("Hello, %s! Email: %s", user.Username, user.Email)}, }, }, nil, nil }) // Enable OAuth protection - returns ready-to-use http.Handler oauthServer, handler, err := mcpoauth.WithOAuth(mux, &oauth.Config{ Provider: "okta", Issuer: "https://dev-12345.okta.com", Audience: "api://my-mcp-server", ServerURL: "http://localhost:8080", }, mcpServer) if err != nil { log.Fatalf("OAuth setup failed: %v", err) } oauthServer.LogStartup(false) // Log OAuth endpoints log.Fatal(http.ListenAndServe(":8080", handler)) } ``` ## oauth.NewConfigBuilder - Fluent Configuration Builder Provides a fluent API for constructing OAuth configuration with validation. The builder pattern allows for cleaner configuration setup and automatically constructs ServerURL from host/port if not explicitly set. ```go package main import ( "log" oauth "github.com/tuannvm/oauth-mcp-proxy" ) func main() { // Build configuration with fluent API cfg, err := oauth.NewConfigBuilder(). WithProvider("okta"). WithIssuer("https://company.okta.com"). WithAudience("api://my-mcp-server"). WithClientID("0oaxxxxx"). WithClientSecret("client-secret-here"). WithScopes([]string{"openid", "profile", "email"}). WithHost("localhost"). WithPort("8080"). WithTLS(false). Build() if err != nil { log.Fatalf("Config error: %v", err) } log.Printf("Config built: Provider=%s, Mode=%s, ServerURL=%s", cfg.Provider, cfg.Mode, cfg.ServerURL) // Output: Config built: Provider=okta, Mode=proxy, ServerURL=http://localhost:8080 } ``` ## oauth.FromEnv - Environment Variable Configuration Creates OAuth configuration from environment variables. This is useful for production deployments where configuration should be externalized. ```go package main import ( "log" "os" oauth "github.com/tuannvm/oauth-mcp-proxy" ) func main() { // Set environment variables os.Setenv("OAUTH_PROVIDER", "okta") os.Setenv("OIDC_ISSUER", "https://dev-12345.okta.com") os.Setenv("OIDC_AUDIENCE", "api://my-mcp-server") os.Setenv("OIDC_CLIENT_ID", "0oaxxxxx") os.Setenv("MCP_URL", "https://mcp.example.com") // Create config from environment cfg, err := oauth.FromEnv() if err != nil { log.Fatalf("Config error: %v", err) } log.Printf("Loaded config: Provider=%s, Issuer=%s", cfg.Provider, cfg.Issuer) } // Supported environment variables: // OAUTH_MODE - "native" or "proxy" (auto-detected if not set) // OAUTH_PROVIDER - "hmac", "okta", "google", "azure" // OAUTH_REDIRECT_URIS - Comma-separated redirect URI allowlist // OAUTH_FIXED_REDIRECT_URI - Fixed redirect URI for proxy mode // OIDC_ISSUER - OAuth provider issuer URL // OIDC_AUDIENCE - Expected audience claim // OIDC_CLIENT_ID - OAuth client ID // OIDC_CLIENT_SECRET - OAuth client secret // OIDC_SCOPES - Space-separated scopes // JWT_SECRET - Secret for HMAC provider // MCP_URL - Full server URL // MCP_HOST - Server host (default: localhost) // MCP_PORT - Server port (default: 8080) ``` ## oauth.GetUserFromContext - Access Authenticated User Extracts the authenticated user from the request context. This is the primary way to access user information in MCP tool handlers after successful authentication. ```go package main import ( "context" "fmt" "github.com/mark3labs/mcp-go/mcp" oauth "github.com/tuannvm/oauth-mcp-proxy" ) func myToolHandler(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract authenticated user from context user, ok := oauth.GetUserFromContext(ctx) if !ok { return nil, fmt.Errorf("authentication required") } // Access user properties // user.Subject - Unique user identifier (sub claim) // user.Username - Preferred username (preferred_username claim) // user.Email - User's email address (email claim) result := fmt.Sprintf("User: %s\nEmail: %s\nSubject: %s", user.Username, user.Email, user.Subject) return mcp.NewToolResultText(result), nil } ``` ## oauth.CreateHTTPContextFunc - Token Extraction Middleware Creates an HTTP context function that extracts OAuth Bearer tokens from Authorization headers and adds them to the request context. This must be used with `mcpserver.WithHTTPContextFunc()` when setting up the StreamableHTTPServer. ```go package main import ( "net/http" mcpserver "github.com/mark3labs/mcp-go/server" oauth "github.com/tuannvm/oauth-mcp-proxy" ) func setupServer(mcpServer *mcpserver.MCPServer) http.Handler { // Create HTTP context function for token extraction contextFunc := oauth.CreateHTTPContextFunc() // Use with StreamableHTTPServer streamableServer := mcpserver.NewStreamableHTTPServer( mcpServer, mcpserver.WithEndpointPath("/mcp"), mcpserver.WithHTTPContextFunc(contextFunc), ) return streamableServer } // Token extraction flow: // 1. HTTP request with "Authorization: Bearer <token>" header // 2. CreateHTTPContextFunc() extracts token from header // 3. Token is added to context via oauth.WithOAuthToken() // 4. OAuth middleware retrieves token via oauth.GetOAuthToken() // 5. Token is validated and cached // 6. Authenticated User is added to context // 7. Tool handler accesses user via oauth.GetUserFromContext() ``` ## Server.WrapMCPEndpoint - Automatic 401 Handling Wraps an MCP endpoint handler with automatic 401 handling. Returns proper WWW-Authenticate headers with OAuth discovery information when Bearer token is missing or invalid. ```go package main import ( "net/http" mcpserver "github.com/mark3labs/mcp-go/server" oauth "github.com/tuannvm/oauth-mcp-proxy" "github.com/tuannvm/oauth-mcp-proxy/mark3labs" ) func main() { mux := http.NewServeMux() oauthServer, oauthOption, _ := mark3labs.WithOAuth(mux, &oauth.Config{ Provider: "okta", Issuer: "https://dev-12345.okta.com", Audience: "api://my-mcp-server", ServerURL: "http://localhost:8080", }) mcpServer := mcpserver.NewMCPServer("Server", "1.0.0", oauthOption) streamableServer := mcpserver.NewStreamableHTTPServer( mcpServer, mcpserver.WithHTTPContextFunc(oauth.CreateHTTPContextFunc()), ) // WrapMCPEndpoint provides automatic 401 handling: // - Passes through OPTIONS requests (CORS pre-flight) // - Returns 401 with WWW-Authenticate if Bearer token missing // - Rejects non-Bearer auth schemes // - Extracts token to context for downstream processing mux.HandleFunc("/mcp", oauthServer.WrapMCPEndpoint(streamableServer)) http.ListenAndServe(":8080", mux) } // Example 401 response: // HTTP/1.1 401 Unauthorized // WWW-Authenticate: Bearer realm="OAuth", error="invalid_request", // error_description="Bearer token required", // resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource" // Content-Type: application/json // // {"error":"invalid_request","error_description":"Bearer token required"} ``` ## Server.ValidateTokenCached - Token Validation with Caching Validates a Bearer token with built-in caching support. Tokens are cached for 5 minutes using SHA-256 hash as the cache key. This method is used internally by the middleware but can also be called directly for custom validation scenarios. ```go package main import ( "context" "fmt" "net/http" oauth "github.com/tuannvm/oauth-mcp-proxy" ) func customAuthHandler(oauthServer *oauth.Server) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Extract token from Authorization header authHeader := r.Header.Get("Authorization") if len(authHeader) < 8 || authHeader[:7] != "Bearer " { http.Error(w, "Missing Bearer token", http.StatusUnauthorized) return } token := authHeader[7:] // Validate token with caching // First call: validates against provider, caches result // Subsequent calls within 5 minutes: returns cached result (<5ms) user, err := oauthServer.ValidateTokenCached(r.Context(), token) if err != nil { http.Error(w, fmt.Sprintf("Authentication failed: %v", err), http.StatusUnauthorized) return } // Use authenticated user fmt.Fprintf(w, "Authenticated: %s (%s)", user.Username, user.Email) } } ``` ## HMAC Provider Configuration - Development/Testing Configures the HMAC provider for development and testing environments. Uses HMAC-SHA256 for JWT token validation with a shared secret, suitable for local development and service-to-service authentication. ```go package main import ( "context" "fmt" "log" "net/http" "time" "github.com/golang-jwt/jwt/v5" "github.com/mark3labs/mcp-go/mcp" mcpserver "github.com/mark3labs/mcp-go/server" oauth "github.com/tuannvm/oauth-mcp-proxy" "github.com/tuannvm/oauth-mcp-proxy/mark3labs" ) func main() { mux := http.NewServeMux() jwtSecret := []byte("my-secret-key-at-least-32-bytes-long") oauthServer, oauthOption, _ := mark3labs.WithOAuth(mux, &oauth.Config{ Provider: "hmac", JWTSecret: jwtSecret, Audience: "api://my-mcp-server", ServerURL: "http://localhost:8080", }) mcpServer := mcpserver.NewMCPServer("HMAC Server", "1.0.0", oauthOption) mcpServer.AddTool( mcp.Tool{Name: "whoami", Description: "Returns current user info"}, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { user, _ := oauth.GetUserFromContext(ctx) return mcp.NewToolResultText(fmt.Sprintf("User: %s", user.Username)), nil }, ) streamableServer := mcpserver.NewStreamableHTTPServer(mcpServer, mcpserver.WithHTTPContextFunc(oauth.CreateHTTPContextFunc())) mux.HandleFunc("/mcp", oauthServer.WrapMCPEndpoint(streamableServer)) log.Fatal(http.ListenAndServe(":8080", mux)) } // Generate test token for HMAC provider: func generateTestToken(secret []byte, audience string) string { token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "sub": "user123", "preferred_username": "testuser", "email": "test@example.com", "aud": audience, "exp": time.Now().Add(time.Hour).Unix(), "iat": time.Now().Unix(), }) signed, _ := token.SignedString(secret) return signed } ``` ## OAuth Discovery Endpoints - RFC 8414/9728 Compliant The library automatically registers OAuth discovery endpoints for client compatibility. These endpoints provide metadata for MCP clients to discover OAuth configuration. ```go package main import ( "log" "net/http" oauth "github.com/tuannvm/oauth-mcp-proxy" "github.com/tuannvm/oauth-mcp-proxy/mark3labs" ) func main() { mux := http.NewServeMux() oauthServer, _, _ := mark3labs.WithOAuth(mux, &oauth.Config{ Provider: "okta", Issuer: "https://dev-12345.okta.com", Audience: "api://my-mcp-server", ServerURL: "http://localhost:8080", }) // Registered endpoints (automatically by WithOAuth): // GET /.well-known/oauth-authorization-server - OAuth 2.0 metadata (RFC 8414) // GET /.well-known/oauth-protected-resource - Resource metadata (RFC 9728) // GET /.well-known/openid-configuration - OIDC discovery // GET /.well-known/jwks.json - JWKS keys (proxy mode) // GET /oauth/authorize - Authorization endpoint (proxy mode) // GET /oauth/callback - Callback handler (proxy mode) // POST /oauth/token - Token exchange (proxy mode) // POST /oauth/register - Dynamic client registration // Get all endpoint URLs endpoints := oauthServer.GetAllEndpoints() for _, ep := range endpoints { log.Printf("%s: %s", ep.Description, ep.Path) } // Log startup info oauthServer.LogStartup(false) // false = HTTP, true = HTTPS http.ListenAndServe(":8080", mux) } // curl http://localhost:8080/.well-known/oauth-authorization-server // Response (proxy mode): // { // "issuer": "http://localhost:8080", // "authorization_endpoint": "http://localhost:8080/oauth/authorize", // "token_endpoint": "http://localhost:8080/oauth/token", // "registration_endpoint": "http://localhost:8080/oauth/register", // "jwks_uri": "http://localhost:8080/.well-known/jwks.json", // "response_types_supported": ["code"], // "grant_types_supported": ["authorization_code"], // "code_challenge_methods_supported": ["plain", "S256"] // } ``` ## Custom Logger Implementation Integrates with your application's logging system by implementing the Logger interface. The library supports pluggable logging for production environments. ```go package main import ( "log" oauth "github.com/tuannvm/oauth-mcp-proxy" ) // CustomLogger implements oauth.Logger interface type CustomLogger struct { prefix string } func (l *CustomLogger) Debug(msg string, args ...interface{}) { log.Printf("[DEBUG] %s: "+msg, append([]interface{}{l.prefix}, args...)...) } func (l *CustomLogger) Info(msg string, args ...interface{}) { log.Printf("[INFO] %s: "+msg, append([]interface{}{l.prefix}, args...)...) } func (l *CustomLogger) Warn(msg string, args ...interface{}) { log.Printf("[WARN] %s: "+msg, append([]interface{}{l.prefix}, args...)...) } func (l *CustomLogger) Error(msg string, args ...interface{}) { log.Printf("[ERROR] %s: "+msg, append([]interface{}{l.prefix}, args...)...) } func main() { cfg, _ := oauth.NewConfigBuilder(). WithProvider("okta"). WithIssuer("https://dev-12345.okta.com"). WithAudience("api://my-mcp-server"). WithLogger(&CustomLogger{prefix: "oauth"}). Build() // Logger will be used for all OAuth operations _ = cfg } ``` ## Proxy Mode with Fixed Redirect URI Configures proxy mode for scenarios where the MCP server acts as an OAuth proxy, handling the OAuth flow and redirecting callbacks to client applications. This is useful for supporting tools like MCP Inspector. ```go package main import ( "log" "net/http" oauth "github.com/tuannvm/oauth-mcp-proxy" "github.com/tuannvm/oauth-mcp-proxy/mark3labs" ) func main() { mux := http.NewServeMux() // Proxy mode: MCP server handles OAuth flow and proxies to client oauthServer, oauthOption, _ := mark3labs.WithOAuth(mux, &oauth.Config{ Provider: "okta", Issuer: "https://dev-12345.okta.com", Audience: "api://my-mcp-server", ClientID: "0oaxxxxx", // Required for proxy mode ClientSecret: "client-secret", // Optional for public clients ServerURL: "http://localhost:8080", // Fixed redirect URI registered with OAuth provider FixedRedirectURI: "http://localhost:8080/oauth/callback", // Allow localhost and specific domains for client redirects AllowedClientRedirectDomains: "example.com,myapp.io", // Or use explicit allowlist // RedirectURIs: "http://localhost:3000/callback,https://app.example.com/callback", }) // Proxy mode automatically enables: // - /oauth/authorize - Proxies auth requests to provider // - /oauth/callback - Handles callback and proxies to client // - /oauth/token - Proxies token exchange with PKCE support // - /oauth/register - Dynamic client registration oauthServer.LogStartup(false) log.Printf("Authorization URL: %s", oauthServer.GetAuthorizeURL()) log.Printf("Callback URL: %s", oauthServer.GetCallbackURL()) log.Printf("Token URL: %s", oauthServer.GetTokenURL()) _ = oauthOption // Use with MCP server http.ListenAndServe(":8080", mux) } ``` ## Summary OAuth MCP Proxy provides a production-ready OAuth 2.1 authentication solution for Go MCP servers with support for both major MCP SDKs. The library's primary use cases include protecting MCP tools with enterprise SSO providers (Okta, Google, Azure AD), enabling secure token validation with automatic caching for high-performance applications, and supporting development workflows with HMAC-based authentication. The automatic 401 handling with RFC 6750 compliant responses ensures proper OAuth discovery for MCP clients. Integration follows a simple pattern: configure the provider, call `WithOAuth()` to get the middleware/handler, and access authenticated users via `GetUserFromContext()` in tool handlers. The library handles all OAuth complexity including OIDC discovery, JWKS fetching, token validation, caching, and proper error responses. For production deployments, use environment variables via `FromEnv()` or the `ConfigBuilder` for type-safe configuration, and implement the `Logger` interface to integrate with your observability stack.