go-sdk/examples/server/auth-middleware/README.md
Eliya Sadan d6d6edd6d4
auth: add OAuth 2.0 Protected Resource Metadata handler
This change adds support for RFC 9728 (OAuth 2.0 Protected Resource Metadata)
by introducing a `ProtectedResourceMetadataHandler`.

The handler includes built-in CORS support with `Access-Control-Allow-Origin: *`
by default, as OAuth metadata is public information meant for client discovery.

Documentation includes examples for using custom CORS policies with
popular middleware libraries (github.com/rs/cors and github.com/jub0bs/cors).

The implementation follows RFC 9728 §3.1 for OAuth 2.0 Authorization Server
Metadata discovery, enabling clients to discover protected resource capabilities
and authentication requirements.
2025-12-08 14:54:52 -05:00

283 lines
8.3 KiB
Markdown

# MCP Server with Auth Middleware
This example demonstrates how to integrate the Go MCP SDK's `auth.RequireBearerToken` middleware with an MCP server to provide authenticated access to MCP tools and resources.
## Features
The server provides authentication and authorization capabilities for MCP tools:
### 1. Authentication Methods
- **JWT Token Authentication**: JSON Web Token-based authentication
- **API Key Authentication**: API key-based authentication
- **Scope-based Access Control**: Permission-based access to MCP tools
### 2. MCP Integration
- **Authenticated MCP Tools**: Tools that require authentication and check permissions
- **Token Generation**: Utility endpoints for generating test tokens
- **Middleware Integration**: Seamless integration with MCP server handlers
## Setup
```bash
cd examples/server/auth-middleware
go mod tidy
go run main.go
```
## Testing
```bash
# Run all tests
go test -v
# Run benchmark tests
go test -bench=.
# Generate coverage report
go test -cover
```
## Endpoints
### Public Endpoints (No Authentication Required)
- `GET /health` - Health check
- `GET /.well-known/oauth-protected-resource` - OAuth 2.0 Protected Resource Metadata (RFC 9728)
### MCP Endpoints (Authentication Required)
- `POST /mcp/jwt` - MCP server with JWT authentication
- `POST /mcp/apikey` - MCP server with API key authentication
### Utility Endpoints
- `GET /generate-token` - Generate JWT token
- `POST /generate-api-key` - Generate API key
## Available MCP Tools
The server provides three authenticated MCP tools:
### 1. Say Hi (`say_hi`)
A simple greeting tool that requires authentication.
**Parameters:**
- None required
**Required Scopes:**
- Any authenticated user
### 2. Get User Info (`get_user_info`)
Retrieves user information based on the provided user ID.
**Parameters:**
- `user_id` (string): The user ID to get information for
**Required Scopes:**
- `read` permission
### 3. Create Resource (`create_resource`)
Creates a new resource with the provided details.
**Parameters:**
- `name` (string): The name of the resource
- `description` (string): The description of the resource
- `content` (string): The content of the resource
**Required Scopes:**
- `write` permission
## Example Usage
### 1. Generating JWT Token and Using MCP Tools
```bash
# Generate a token
curl 'http://localhost:8080/generate-token?user_id=alice&scopes=read,write'
# Use MCP tool with JWT authentication
curl -H 'Authorization: Bearer <generated_token>' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"say_hi","arguments":{}}}' \
http://localhost:8080/mcp/jwt
```
### 2. Generating API Key and Using MCP Tools
```bash
# Generate an API key
curl -X POST 'http://localhost:8080/generate-api-key?user_id=bob&scopes=read'
# Use MCP tool with API key authentication
curl -H 'Authorization: Bearer <generated_api_key>' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_user_info","arguments":{"user_id":"test"}}}' \
http://localhost:8080/mcp/apikey
```
### 3. Testing Scope Restrictions
```bash
# Access MCP tool requiring write scope
curl -H 'Authorization: Bearer <token_with_write_scope>' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_resource","arguments":{"name":"test","description":"test resource","content":"test content"}}}' \
http://localhost:8080/mcp/jwt
```
## Core Concepts
### Authentication Integration
This example demonstrates how to integrate `auth.RequireBearerToken` middleware with an MCP server to provide authenticated access. The MCP server operates as an HTTP handler protected by authentication middleware.
### Key Features
1. **MCP Server Integration**: Create MCP server using `mcp.NewServer`
2. **Authentication Middleware**: Protect MCP handlers with `auth.RequireBearerToken`
3. **Token Verification**: Validate tokens using provided `TokenVerifier` functions
4. **Scope Checking**: Verify required permissions (scopes) are present
5. **Expiration Validation**: Check that tokens haven't expired
6. **Context Injection**: Add verified token information to request context
7. **Authenticated MCP Tools**: Tools that operate based on authentication information
8. **Error Handling**: Return appropriate HTTP status codes and error messages on authentication failure
### Implementation
```go
// Create MCP server
server := mcp.NewServer(&mcp.Implementation{Name: "authenticated-mcp-server"}, nil)
// Create authentication middleware
authMiddleware := auth.RequireBearerToken(verifier, &auth.RequireBearerTokenOptions{
Scopes: []string{"read", "write"},
})
// Create MCP handler
handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
return server
}, nil)
// Apply authentication middleware to MCP handler
authenticatedHandler := authMiddleware(customMiddleware(handler))
```
### Parameters
- **verifier**: Function to verify tokens (`TokenVerifier` type)
- **opts**: Authentication options
- `Scopes`: List of required permissions
- `ResourceMetadataURL`: OAuth 2.0 resource metadata URL
### Error Responses
- **401 Unauthorized**: Token is invalid, expired, or missing
- **403 Forbidden**: Required scopes are insufficient
- **WWW-Authenticate Header**: Included when resource metadata URL is configured
## Implementation Details
### 1. TokenVerifier Implementation
```go
func jwtVerifier(ctx context.Context, tokenString string) (*auth.TokenInfo, error) {
// JWT token verification logic
// On success: Return TokenInfo
// On failure: Return auth.ErrInvalidToken
}
```
### 2. Using Authentication Information in MCP Tools
```go
// Get authentication information in MCP tool
func MyTool(ctx context.Context, req *mcp.CallToolRequest, args MyArgs) (*mcp.CallToolResult, any, error) {
// Extract authentication info from request
userInfo := req.Extra.TokenInfo
// Check scopes
if !slices.Contains(userInfo.Scopes, "read") {
return nil, nil, fmt.Errorf("insufficient permissions: read scope required")
}
// Execute tool logic
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: "Tool executed successfully"},
},
}, nil, nil
}
```
### 3. Middleware Composition
```go
// Combine authentication middleware with custom middleware
authenticatedHandler := authMiddleware(customMiddleware(mcpHandler))
```
## Security Best Practices
1. **Environment Variables**: Use environment variables for JWT secrets in production
2. **Database Storage**: Store API keys in a database
3. **HTTPS Usage**: Always use HTTPS in production environments
4. **Token Expiration**: Set appropriate token expiration times
5. **Principle of Least Privilege**: Grant only the minimum required scopes
## CORS Support
The OAuth metadata endpoint (`/.well-known/oauth-protected-resource`) supports CORS to enable
cross-origin client discovery. It sets `Access-Control-Allow-Origin: *` by default because
OAuth metadata is public information meant for client discovery (RFC 9728 §3.1).
### Custom CORS Policies
For more sophisticated CORS requirements (origin validation, credentials, etc.), wrap the handler
with a CORS middleware library:
**Using github.com/rs/cors:**
```go
import "github.com/rs/cors"
c := cors.New(cors.Options{
AllowedOrigins: []string{"https://example.com"},
AllowedMethods: []string{"GET", "OPTIONS"},
})
http.Handle("/.well-known/oauth-protected-resource",
c.Handler(auth.ProtectedResourceMetadataHandler(metadata)))
```
**Using github.com/jub0bs/cors:**
```go
import "github.com/jub0bs/cors"
corsMiddleware, err := cors.NewMiddleware(cors.Config{
Origins: []string{"https://example.com"},
Methods: []string{"GET", "OPTIONS"},
})
http.Handle("/.well-known/oauth-protected-resource",
corsMiddleware.Wrap(auth.ProtectedResourceMetadataHandler(metadata)))
```
## Use Cases
**Ideal for:**
- MCP servers requiring authentication and authorization
- Applications needing scope-based access control
- Systems requiring both JWT and API key authentication
- Projects needing secure MCP tool access
- Scenarios requiring audit trails and permission management
**Examples:**
- Enterprise MCP servers with user management
- Multi-tenant MCP applications
- Secure API gateways with MCP integration
- Development environments with authentication requirements
- Production systems requiring fine-grained access control