An open API service indexing awesome lists of open source software.

https://github.com/tuannvm/oauth-mcp-proxy

OAuth 2.1 authentication library for Go MCP servers
https://github.com/tuannvm/oauth-mcp-proxy

authentication authorization claude codex go-sdk mcp mcp-go oauth

Last synced: 3 months ago
JSON representation

OAuth 2.1 authentication library for Go MCP servers

Awesome Lists containing this project

README

          

# 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
```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
```go
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](https://img.shields.io/github/actions/workflow/status/tuannvm/oauth-mcp-proxy/test.yml?branch=main&label=Tests&logo=github)](https://github.com/tuannvm/oauth-mcp-proxy/actions/workflows/test.yml)
[![Go Version](https://img.shields.io/github/go-mod/go-version/tuannvm/oauth-mcp-proxy?logo=go)](https://github.com/tuannvm/oauth-mcp-proxy/blob/main/go.mod)
[![Go Report Card](https://goreportcard.com/badge/github.com/tuannvm/oauth-mcp-proxy)](https://goreportcard.com/report/github.com/tuannvm/oauth-mcp-proxy)
[![Go Reference](https://pkg.go.dev/badge/github.com/tuannvm/oauth-mcp-proxy.svg)](https://pkg.go.dev/github.com/tuannvm/oauth-mcp-proxy)
[![GitHub Release](https://img.shields.io/github/v/release/tuannvm/oauth-mcp-proxy?sort=semver)](https://github.com/tuannvm/oauth-mcp-proxy/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/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

```mermaid
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
```

### Token Validation Flow

```mermaid
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
```

**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
```go
// 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)
```go
// ✅ 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

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

#### 2. Add to Your Server

```go
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

```go
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

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

#### 2. Add to Your Server

```go
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](examples/README.md) for detailed setup guide including Okta configuration.

| SDK | Example | Description |
|-----|---------|-------------|
| **mark3labs** | [Simple](examples/mark3labs/simple/) | Minimal setup - copy/paste ready |
| **mark3labs** | [Advanced](examples/mark3labs/advanced/) | ConfigBuilder, multiple tools, logging |
| **Official** | [Simple](examples/official/simple/) | Minimal setup - copy/paste ready |
| **Official** | [Advanced](examples/official/advanced/) | ConfigBuilder, multiple tools, logging |

---

## Supported Providers

| Provider | Best For | Setup Guide |
|----------|----------|-------------|
| **HMAC** | Testing, development | [docs/providers/HMAC.md](docs/providers/HMAC.md) |
| **Okta** | Enterprise SSO | [docs/providers/OKTA.md](docs/providers/OKTA.md) |
| **Google** | Google Workspace | [docs/providers/GOOGLE.md](docs/providers/GOOGLE.md) |
| **Azure AD** | Microsoft 365 | [docs/providers/AZURE.md](docs/providers/AZURE.md) |

---

## Documentation

**Getting Started:**

- [Setup Guide](docs/CLIENT-SETUP.md) - Complete server integration and client configuration
- [Configuration Guide](docs/CONFIGURATION.md) - All config options
- [Provider Setup](docs/providers/) - OAuth provider guides

**Advanced:**

- [Security Guide](docs/SECURITY.md) - Production best practices
- [Troubleshooting](docs/TROUBLESHOOTING.md) - Common issues

---

## License

MIT License - See [LICENSE](LICENSE)