CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
oauth-mcp-proxy is an OAuth 2.1 authentication library for Go MCP servers. It provides server-side OAuth integration with minimal code (3-line integration via WithOAuth()), supporting multiple providers (HMAC, Okta, Google, Azure AD).
Version: v1.0.0 (Supports both mark3labs/mcp-go and official modelcontextprotocol/go-sdk)
Build Commands
# Run tests
make test
# Run tests with verbose output
make test-verbose
# Run tests with coverage report (generates coverage.html)
make test-coverage
# Run linters (same as CI - checks go.mod tidy + golangci-lint)
make lint
# Format code
make fmt
# Clean build artifacts and caches
make clean
# Install/download dependencies
make install
# Check for security vulnerabilities
make vulnArchitecture
Package Structure (v1.0.0)
Core Components
Core Package (SDK-agnostic):
oauth.go -
Servertype,NewServer(),ValidateTokenCached()(used by adapters)config.go - Configuration validation and provider setup
cache.go - Token caching logic (
TokenCache,CachedToken)context.go - Context utilities (
WithOAuthToken,GetOAuthToken,WithUser,GetUserFromContext)handlers.go - OAuth HTTP endpoints
provider/provider.go - Token validators (HMACValidator, OIDCValidator)
Adapters (SDK-specific):
mark3labs/ - Middleware adapter for
mark3labs/mcp-gomcp/ - HTTP handler wrapper for official SDK
Key Design Patterns
OpenTelemetry Pattern: Core logic is SDK-agnostic; adapters provide SDK-specific integration
Instance-scoped: Each
Serverinstance has its own token cache and validator (no globals)Provider abstraction:
TokenValidatorinterface supports multiple OAuth providersCaching strategy: Tokens cached for 5 minutes using SHA-256 hash as key
Context propagation: OAuth token extracted from HTTP header → stored in context → validated → user added to context
Integration Flow
mark3labs SDK:
Official SDK:
Provider System
HMAC: Validates JWT tokens with shared secret (testing/dev)
OIDC: Validates tokens via JWKS/OIDC discovery (Okta/Google/Azure)
All validation happens in
provider/provider.goValidators implement
TokenValidatorinterface
Testing
The codebase has extensive test coverage across multiple scenarios:
api_test.go - Core API functionality tests
integration_test.go - End-to-end integration tests
security_test.go - Security validation tests
attack_scenarios_test.go - Security attack scenario tests
middleware_compatibility_test.go - Middleware compatibility tests
provider/provider_test.go - Token validator tests
Run single test:
Test Patterns
Tests use table-driven subtests with t.Run():
Mock validators implement TokenValidator interface. Use httptest.NewRecorder() for HTTP handler tests.
Configuration
ConfigBuilder Pattern (Recommended)
Use ConfigBuilder for production code instead of direct Config structs:
Build() validates config and auto-constructs ServerURL if not set.
Context Timeouts
OIDC validation: 10 seconds
Provider initialization: 30 seconds
Security Requirements
Redirect URI validation: All URIs must be in explicit allowlist
State parameter HMAC: OAuth states are HMAC-signed to prevent CSRF
Audience validation: Both HMAC and OIDC validators explicitly check
audclaimNo raw token logging: Only log
fmt.Sprintf("%x", sha256.Sum256([]byte(token)))[:16]TLS in production: Always warn if
useTLS=falseinLogStartup()
Important Notes
User Context: Always use
GetUserFromContext(ctx)in tool handlers to access authenticated userToken Caching: Tokens cached for 5 minutes - design for this TTL in testing. Cache uses
sync.RWMutexwith background cleanup viadeleteExpiredToken()goroutineLogging: Config.Logger is optional. If nil, uses default logger (log.Printf with level prefixes)
Modes: Library supports "native" (token validation only) and "proxy" (OAuth flow proxy) modes. Auto-detected based on
ClientIDpresenceAdapter Pattern:
WithOAuth()is in adapter packages (mark3labs.WithOAuth()ormcp.WithOAuth()) for SDK-specific integration
Common Gotchas
SDK Imports: Adapter code (
mark3labs/,mcp/) can import SDKs. Core package cannot - keep it SDK-agnosticContext Propagation: Always extract user via
GetUserFromContext(ctx)in tool handlersCache Expiry: Background cleanup runs in goroutine to avoid lock contention
Mode Detection: Config auto-detects "native" vs "proxy" based on
ClientIDpresenceLogger Fallback: If
cfg.Logger == nil, usesdefaultLogger{}withlog.Printf
File Naming Conventions
Core logic:
oauth.go,config.go,cache.go,context.go,handlers.go,middleware.goTests:
*_test.go(e.g.,security_test.go,integration_test.go)Adapters:
mark3labs/oauth.go,mcp/oauth.go(not*_adapter.go)Provider:
provider/provider.go(single file, multiple validators)
Using the Library
With mark3labs/mcp-go
Note: WrapMCPEndpoint() provides automatic 401 handling with proper WWW-Authenticate headers when Bearer token is missing. It also passes through OPTIONS requests (CORS) and non-Bearer auth schemes.
With Official SDK
Note: Official SDK adapter includes automatic 401 handling in the returned handler.
Extending the Library
Adding a New OAuth Provider
Add validator to
provider/provider.goimplementingTokenValidatorinterfaceUpdate
createValidator()switch inconfig.goAdd provider documentation in
docs/providers/
Adding a New SDK Adapter
Create
<sdk>/oauth.gowithWithOAuth()functionFollow pattern: create
oauth.Server, register handlers, return SDK-specific middleware/optionNever import MCP SDKs in core package
Adding New Endpoints
Add handler method to
OAuth2Handlerinhandlers.goRegister in
RegisterHandlers()inoauth.go
Documentation References
examples/README.md- Complete setup guide with Okta configurationexamples/mark3labs/andexamples/official/- Working examples (simple + advanced)docs/providers/*.md- Provider-specific setup (OKTA.md, GOOGLE.md, AZURE.md, HMAC.md)docs/CONFIGURATION.md- All configuration optionsdocs/SECURITY.md- Production best practicesdocs/TROUBLESHOOTING.md- Common issues and solutions
Last updated
Was this helpful?