Documentation
¶
Overview ¶
Package oauth provides OAuth 2.1 authentication support for MCP servers. This file implements OAuth flow coordination to prevent race conditions.
Package oauth provides OAuth 2.1 authentication support for MCP servers. This file implements correlation ID tracking for OAuth flow traceability.
Package oauth provides OAuth authentication functionality for MCP servers.
Package oauth provides OAuth 2.1 authentication support for MCP servers. This file implements enhanced logging utilities with sensitive data redaction.
Package oauth provides OAuth 2.1 authentication support for MCP servers. This file implements proactive token refresh management.
Package oauth provides OAuth authentication functionality for MCP servers.
Index ¶
- Constants
- Variables
- func BuildRFC8414MetadataURLs(authServerURL string) []string
- func CorrelationLogger(ctx context.Context, logger *zap.Logger) *zap.Logger
- func CorrelationLoggerWithFlow(ctx context.Context, logger *zap.Logger) *zap.Logger
- func CreateOAuthConfig(serverConfig *config.ServerConfig, storage *storage.BoltDB) *client.OAuthConfig
- func CreateOAuthConfigWithExtraParams(ctx context.Context, serverConfig *config.ServerConfig, ...) (*client.OAuthConfig, map[string]string)
- func DetectOAuthAvailability(baseURL string, timeout time.Duration) bool
- func DiscoverAuthServerURL(serverURL string, timeout time.Duration) string
- func DiscoverScopesFromAuthorizationServer(baseURL string, timeout time.Duration) ([]string, error)
- func DiscoverScopesFromProtectedResource(metadataURL string, timeout time.Duration) ([]string, error)
- func ExtractResourceMetadataURL(wwwAuthHeader string) string
- func FindWorkingMetadataURL(serverURL string, timeout time.Duration) (string, error)
- func GenerateServerKey(serverName, serverURL string) string
- func GetCorrelationID(ctx context.Context) string
- func GetPersistedRefreshToken(serverName, serverURL string, boltStorage *storage.BoltDB) string
- func HasPersistedToken(serverName, serverURL string, boltStorage *storage.BoltDB) (hasToken bool, hasRefreshToken bool, isExpired bool)
- func IsOAuthCapable(serverConfig *config.ServerConfig) bool
- func IsOAuthConfigured(serverConfig *config.ServerConfig) bool
- func IsOAuthError(err string) bool
- func LogActualTokenRefreshAttempt(logger *zap.Logger, serverName string, tokenAge time.Duration)
- func LogActualTokenRefreshResult(logger *zap.Logger, serverName string, success bool, duration time.Duration, ...)
- func LogClientConnectionAttempt(logger *zap.Logger, attempt int, maxAttempts int)
- func LogClientConnectionFailure(logger *zap.Logger, attempt int, err error)
- func LogClientConnectionSuccess(logger *zap.Logger, duration time.Duration)
- func LogOAuthFlowEnd(logger *zap.Logger, serverName string, correlationID string, success bool, ...)
- func LogOAuthFlowStart(logger *zap.Logger, serverName string, correlationID string)
- func LogOAuthRequest(logger *zap.Logger, method, url string, headers http.Header)
- func LogOAuthResponse(logger *zap.Logger, statusCode int, headers http.Header, ...)
- func LogOAuthResponseError(logger *zap.Logger, statusCode int, errorMsg string, duration time.Duration)
- func LogTokenMetadata(logger *zap.Logger, metadata TokenMetadata)
- func LogTokenRefreshAttempt(logger *zap.Logger, attempt int, maxAttempts int)deprecated
- func LogTokenRefreshFailure(logger *zap.Logger, attempt int, err error)deprecated
- func LogTokenRefreshSuccess(logger *zap.Logger, duration time.Duration)deprecated
- func NewCorrelationID() string
- func NewPersistentTokenStore(serverName, serverURL string, storage *storage.BoltDB) client.TokenStore
- func RedactHeaders(headers http.Header) map[string]string
- func RedactSensitiveData(data string) string
- func RedactURL(urlStr string) string
- func ShouldUseOAuth(serverConfig *config.ServerConfig) bool
- func WithCorrelationID(ctx context.Context, id string) context.Context
- func WithFlowContext(ctx context.Context, flowCtx *OAuthFlowContext) context.Context
- type CallbackServer
- type CallbackServerManager
- type CompletionEvent
- type DatabaseOAuthNotifier
- type OAuthFlowContext
- type OAuthFlowCoordinator
- func (c *OAuthFlowCoordinator) CleanupStaleFlows() int
- func (c *OAuthFlowCoordinator) EndFlow(serverName string, success bool, err error)
- func (c *OAuthFlowCoordinator) GetActiveFlow(serverName string) *OAuthFlowContext
- func (c *OAuthFlowCoordinator) IsFlowActive(serverName string) bool
- func (c *OAuthFlowCoordinator) StartFlow(serverName string) (*OAuthFlowContext, error)
- func (c *OAuthFlowCoordinator) UpdateFlowState(serverName string, state OAuthFlowState)
- func (c *OAuthFlowCoordinator) WaitForFlow(ctx context.Context, serverName string, timeout time.Duration) error
- type OAuthFlowState
- type OAuthMetadataError
- type OAuthMetadataValidationResult
- type OAuthServerMetadata
- type OAuthStatus
- type OAuthTransportWrapper
- type PersistentTokenStore
- type ProtectedResourceMetadata
- type RefreshEventEmitter
- type RefreshManager
- func (m *RefreshManager) GetRefreshState(serverName string) *RefreshStateInfo
- func (m *RefreshManager) GetSchedule(serverName string) *RefreshSchedule
- func (m *RefreshManager) GetScheduleCount() int
- func (m *RefreshManager) OnTokenCleared(serverName string)
- func (m *RefreshManager) OnTokenSaved(serverName string, expiresAt time.Time)
- func (m *RefreshManager) SetEventEmitter(emitter RefreshEventEmitter)
- func (m *RefreshManager) SetMetricsRecorder(recorder RefreshMetricsRecorder)
- func (m *RefreshManager) SetRuntime(runtime RefreshRuntimeOperations)
- func (m *RefreshManager) Start(ctx context.Context) error
- func (m *RefreshManager) Stop()
- type RefreshManagerConfig
- type RefreshMetricsRecorder
- type RefreshRuntimeOperations
- type RefreshSchedule
- type RefreshState
- type RefreshStateInfo
- type RefreshTokenConfig
- type RefreshTokenStore
- type TokenMetadata
- type TokenRefreshResult
- type TokenStoreManager
- func (m *TokenStoreManager) GetOrCreateTokenStore(serverName string) client.TokenStore
- func (m *TokenStoreManager) HasRecentOAuthCompletion(serverName string) bool
- func (m *TokenStoreManager) HasTokenStore(serverName string) bool
- func (m *TokenStoreManager) HasValidToken(ctx context.Context, serverName string, storage *storage.BoltDB) bool
- func (m *TokenStoreManager) MarkOAuthCompleted(serverName string)
- func (m *TokenStoreManager) MarkOAuthCompletedWithDB(serverName string, storage DatabaseOAuthNotifier) error
- func (m *TokenStoreManager) NotifyTokenSaved(serverName string, expiresAt time.Time)
- func (m *TokenStoreManager) SetOAuthCompletionCallback(callback func(serverName string))
- func (m *TokenStoreManager) SetTokenSavedCallback(callback func(serverName string, expiresAt time.Time))
Constants ¶
const ( // Default OAuth redirect URI base - port will be dynamically assigned DefaultRedirectURIBase = "http://127.0.0.1" DefaultRedirectPath = "/oauth/callback" )
const ( // DefaultFlowTimeout is the maximum time to wait for an OAuth flow to complete. DefaultFlowTimeout = 5 * time.Minute // StaleFlowTimeout is the time after which a flow is considered stale and can be cleared. StaleFlowTimeout = 10 * time.Minute )
Default timeouts for OAuth flow coordination.
const ( // DefaultRefreshThreshold is the percentage of token lifetime at which proactive refresh triggers. // Used in hybrid calculation: refresh at the EARLIER of (threshold * lifetime) or (expiry - MinRefreshBuffer). // 0.75 means refresh at 75% of lifetime for long-lived tokens. DefaultRefreshThreshold = 0.75 // DefaultMaxRetries is the maximum number of refresh attempts before giving up. // Set to 0 for unlimited retries until token expiration (FR-009). DefaultMaxRetries = 0 // MinRefreshInterval prevents too-frequent refresh attempts. MinRefreshInterval = 5 * time.Second // MinRefreshBuffer is the minimum time before expiration to schedule a refresh. // This ensures adequate time for retries even with short-lived tokens. // Industry best practice: Google and Microsoft recommend 5 minutes. MinRefreshBuffer = 5 * time.Minute // RetryBackoffBase is the base duration for exponential backoff on retry. // Per FR-008: minimum 10 seconds between refresh attempts per server. RetryBackoffBase = 10 * time.Second // MaxRetryBackoff is the maximum backoff duration (5 minutes per FR-009). MaxRetryBackoff = 5 * time.Minute // MaxExpiredTokenAge is how long after token expiration we continue retrying // before giving up completely. After this duration, we assume the refresh token // is no longer valid even if it wasn't explicitly rejected. MaxExpiredTokenAge = 24 * time.Hour )
Default refresh configuration
const ( // TokenRefreshGracePeriod defines how long before expiration we should trigger a refresh. // This prevents race conditions where a token expires during an API call. // Setting this to 5 minutes allows proactive token refresh before expiration. TokenRefreshGracePeriod = 5 * time.Minute )
Variables ¶
var ( // ErrServerNotOAuth indicates server doesn't use OAuth authentication. // This is returned when attempting OAuth operations on a non-OAuth server. ErrServerNotOAuth = errors.New("server does not use OAuth") // ErrTokenExpired indicates OAuth token has expired. // This triggers token refresh or re-authentication flow. ErrTokenExpired = errors.New("OAuth token has expired") // ErrRefreshFailed indicates token refresh failed after all retry attempts. // This typically requires manual re-authentication via browser. ErrRefreshFailed = errors.New("OAuth token refresh failed") // ErrNoRefreshToken indicates refresh token is not available. // Some OAuth providers don't issue refresh tokens. ErrNoRefreshToken = errors.New("no refresh token available") )
OAuth-specific sentinel errors for consistent error handling across the codebase. Note: ErrFlowInProgress and ErrFlowTimeout are defined in coordinator.go
var ErrFlowInProgress = errors.New("OAuth flow already in progress")
ErrFlowInProgress indicates an OAuth flow is already in progress for the server.
var ErrFlowTimeout = errors.New("timeout waiting for OAuth flow to complete")
ErrFlowTimeout indicates that waiting for an OAuth flow timed out.
Functions ¶
func BuildRFC8414MetadataURLs ¶ added in v0.15.0
BuildRFC8414MetadataURLs constructs OAuth Authorization Server Metadata URLs per RFC 8414.
RFC 8414 Section 3.1 specifies that when the issuer URL contains a path component, the well-known path should be inserted between the host and the path:
https://example.com/path → https://example.com/.well-known/oauth-authorization-server/path
However, some servers (like the current codebase's test servers) expect the path appended:
https://example.com/path → https://example.com/path/.well-known/oauth-authorization-server
This function returns both variants to try, with RFC 8414 compliant path first.
For URLs without a path:
https://example.com → https://example.com/.well-known/oauth-authorization-server
func CorrelationLogger ¶
CorrelationLogger returns a logger with the correlation_id field added if present in context. If no correlation ID is found, returns the original logger unchanged.
func CorrelationLoggerWithFlow ¶
CorrelationLoggerWithFlow returns a logger with both correlation_id and flow state fields.
func CreateOAuthConfig ¶
func CreateOAuthConfig(serverConfig *config.ServerConfig, storage *storage.BoltDB) *client.OAuthConfig
CreateOAuthConfig creates an OAuth configuration for dynamic client registration This implements proper callback server coordination required for Cloudflare OAuth
Note: For zero-config OAuth with auto-detected resource parameter, use CreateOAuthConfigWithExtraParams() instead, which returns both config and extraParams.
func CreateOAuthConfigWithExtraParams ¶
func CreateOAuthConfigWithExtraParams(ctx context.Context, serverConfig *config.ServerConfig, storage *storage.BoltDB) (*client.OAuthConfig, map[string]string)
CreateOAuthConfigWithExtraParams creates an OAuth configuration and returns auto-detected extra parameters. This function implements RFC 8707 resource auto-detection for zero-config OAuth.
The function returns:
- *client.OAuthConfig: The OAuth configuration for mcp-go client
- map[string]string: Extra parameters (including auto-detected resource) to inject into authorization URL
Resource auto-detection logic (in priority order):
- Manual extra_params.resource from config (highest priority - preserves backward compatibility)
- Auto-detected resource from RFC 9728 Protected Resource Metadata
- Fallback to server URL if metadata is unavailable or lacks resource field
func DetectOAuthAvailability ¶
DetectOAuthAvailability checks if a server supports OAuth by probing the well-known endpoint Returns true if OAuth metadata is discoverable, false otherwise
func DiscoverAuthServerURL ¶ added in v0.15.0
DiscoverAuthServerURL attempts to discover the OAuth authorization server URL by fetching the Protected Resource Metadata (RFC 9728) from the MCP server. Returns the first authorization server URL, or empty string if discovery fails.
This is important for servers like Smithery that use separate domains:
- MCP Server: server.smithery.ai/googledrive
- Auth Server: auth.smithery.ai/googledrive
func DiscoverScopesFromAuthorizationServer ¶
DiscoverScopesFromAuthorizationServer attempts to discover scopes from OAuth Server Metadata (RFC 8414)
func DiscoverScopesFromProtectedResource ¶
func DiscoverScopesFromProtectedResource(metadataURL string, timeout time.Duration) ([]string, error)
DiscoverScopesFromProtectedResource attempts to discover scopes from Protected Resource Metadata (RFC 9728) This is a convenience wrapper around DiscoverProtectedResourceMetadata for backward compatibility.
func ExtractResourceMetadataURL ¶
ExtractResourceMetadataURL parses WWW-Authenticate header to extract resource_metadata URL Format: Bearer error="invalid_request", resource_metadata="https://..."
func FindWorkingMetadataURL ¶ added in v0.15.0
FindWorkingMetadataURL tries each URL from BuildRFC8414MetadataURLs and returns the first one that successfully returns valid OAuth metadata. This is used to pre-validate which URL format works for a given server before passing it to the OAuth handler.
Returns the working URL and nil error on success, or empty string and error if none work.
func GenerateServerKey ¶
GenerateServerKey creates a unique key for a server by combining name and URL Exported for use by connection.go when persisting DCR credentials
func GetCorrelationID ¶
GetCorrelationID retrieves the correlation ID from the context. Returns an empty string if no correlation ID is present.
func GetPersistedRefreshToken ¶
GetPersistedRefreshToken retrieves the refresh token from persistent storage if available. Returns empty string if no token exists or token has no refresh_token.
func HasPersistedToken ¶
func HasPersistedToken(serverName, serverURL string, boltStorage *storage.BoltDB) (hasToken bool, hasRefreshToken bool, isExpired bool)
HasPersistedToken checks if a token exists in persistent storage (BBolt) for the server. This is the preferred method to check for existing tokens as it reflects actual token availability.
func IsOAuthCapable ¶
func IsOAuthCapable(serverConfig *config.ServerConfig) bool
IsOAuthCapable determines if a server can use OAuth authentication Returns true if:
- OAuth is explicitly configured in config, OR
- Server uses HTTP-based protocol (OAuth auto-detection available)
func IsOAuthConfigured ¶
func IsOAuthConfigured(serverConfig *config.ServerConfig) bool
IsOAuthConfigured checks if server has OAuth configuration in config file This is mainly for informational purposes - we don't require pre-configuration
func IsOAuthError ¶
IsOAuthError checks if an error message indicates an OAuth-related problem. This is the exported version for use by other packages.
func LogActualTokenRefreshAttempt ¶ added in v0.16.0
LogActualTokenRefreshAttempt logs an actual proactive token refresh attempt. This is called by RefreshManager when it initiates a token refresh operation.
func LogActualTokenRefreshResult ¶ added in v0.16.0
func LogActualTokenRefreshResult(logger *zap.Logger, serverName string, success bool, duration time.Duration, err error)
LogActualTokenRefreshResult logs the result of an actual token refresh operation. This is called by RefreshManager after a refresh attempt completes.
func LogClientConnectionAttempt ¶ added in v0.16.0
LogClientConnectionAttempt logs a client connection attempt (not an actual token refresh). Note: This is called when retrying client.Start(), which may trigger automatic token refresh internally by mcp-go, but we cannot observe whether refresh actually occurred.
func LogClientConnectionFailure ¶ added in v0.16.0
LogClientConnectionFailure logs a failed client connection attempt.
func LogClientConnectionSuccess ¶ added in v0.16.0
LogClientConnectionSuccess logs a successful client connection. Note: This does NOT mean a token refresh occurred - it means the client connected. The mcp-go library may have used a cached token or performed automatic refresh internally.
func LogOAuthFlowEnd ¶
func LogOAuthFlowEnd(logger *zap.Logger, serverName string, correlationID string, success bool, duration time.Duration)
LogOAuthFlowEnd logs the end of an OAuth flow.
func LogOAuthFlowStart ¶
LogOAuthFlowStart logs the start of an OAuth flow.
func LogOAuthRequest ¶
LogOAuthRequest logs an OAuth HTTP request with redacted sensitive data. Use at debug level for comprehensive request tracing.
func LogOAuthResponse ¶
func LogOAuthResponse(logger *zap.Logger, statusCode int, headers http.Header, duration time.Duration)
LogOAuthResponse logs an OAuth HTTP response with redacted sensitive data. Use at debug level for comprehensive response tracing.
func LogOAuthResponseError ¶
func LogOAuthResponseError(logger *zap.Logger, statusCode int, errorMsg string, duration time.Duration)
LogOAuthResponseError logs an OAuth HTTP response error.
func LogTokenMetadata ¶
func LogTokenMetadata(logger *zap.Logger, metadata TokenMetadata)
LogTokenMetadata logs token metadata without exposing actual token values. Safe to use at info level as no sensitive data is included.
func LogTokenRefreshAttempt
deprecated
func LogTokenRefreshFailure
deprecated
func LogTokenRefreshSuccess
deprecated
func NewCorrelationID ¶
func NewCorrelationID() string
NewCorrelationID generates a new unique correlation ID using UUID v4.
func NewPersistentTokenStore ¶
func NewPersistentTokenStore(serverName, serverURL string, storage *storage.BoltDB) client.TokenStore
NewPersistentTokenStore creates a new persistent token store for a server
func RedactHeaders ¶
RedactHeaders creates a copy of headers with sensitive values redacted. Returns a map suitable for logging.
func RedactSensitiveData ¶
RedactSensitiveData redacts sensitive information from a string. It replaces tokens, secrets, and other sensitive data with redacted placeholders.
func ShouldUseOAuth ¶
func ShouldUseOAuth(serverConfig *config.ServerConfig) bool
ShouldUseOAuth determines if OAuth should be attempted for a given server Headers are tried first if configured, then OAuth as fallback on auth errors
func WithCorrelationID ¶
WithCorrelationID returns a new context with the given correlation ID attached.
func WithFlowContext ¶
func WithFlowContext(ctx context.Context, flowCtx *OAuthFlowContext) context.Context
WithFlowContext returns a new context with the OAuth flow context attached.
Types ¶
type CallbackServer ¶
type CallbackServer struct {
Port int
RedirectURI string
Server *http.Server
CallbackChan chan map[string]string
// contains filtered or unexported fields
}
CallbackServer represents an active OAuth callback server
func GetCallbackServer ¶
func GetCallbackServer(serverName string) (*CallbackServer, bool)
GetCallbackServer is a global helper to access callback servers
type CallbackServerManager ¶
type CallbackServerManager struct {
// contains filtered or unexported fields
}
CallbackServerManager manages OAuth callback servers for dynamic port allocation
func GetGlobalCallbackManager ¶
func GetGlobalCallbackManager() *CallbackServerManager
GetGlobalCallbackManager returns the global callback manager instance
func (*CallbackServerManager) GetCallbackServer ¶
func (m *CallbackServerManager) GetCallbackServer(serverName string) (*CallbackServer, bool)
GetCallbackServer retrieves the callback server for a given server name
func (*CallbackServerManager) StartCallbackServer ¶
func (m *CallbackServerManager) StartCallbackServer(serverName string, preferredPort int) (*CallbackServer, error)
StartCallbackServer starts a new OAuth callback server for the given server name. If preferredPort > 0, it attempts to bind to that port first for redirect URI persistence (Spec 022). Falls back to dynamic allocation if the preferred port is unavailable.
func (*CallbackServerManager) StopCallbackServer ¶
func (m *CallbackServerManager) StopCallbackServer(serverName string) error
StopCallbackServer stops and removes the callback server for a given server name
type CompletionEvent ¶
type CompletionEvent = storage.OAuthCompletionEvent
OAuthCompletionEvent represents an OAuth completion event (re-exported from storage)
type DatabaseOAuthNotifier ¶
type DatabaseOAuthNotifier interface {
SaveOAuthCompletionEvent(event *CompletionEvent) error
}
DatabaseOAuthNotifier interface for database-based OAuth completion notifications
type OAuthFlowContext ¶
type OAuthFlowContext struct {
// CorrelationID is a UUID that uniquely identifies this OAuth flow.
CorrelationID string
// ServerName is the name of the MCP server being authenticated.
ServerName string
// StartTime is when the OAuth flow was initiated.
StartTime time.Time
// State is the current state of the OAuth flow.
State OAuthFlowState
}
OAuthFlowContext represents the context for a single OAuth authentication flow. It contains a unique correlation ID that links all log entries for this flow.
func GetFlowContext ¶
func GetFlowContext(ctx context.Context) *OAuthFlowContext
GetFlowContext retrieves the OAuth flow context from the context. Returns nil if no flow context is present.
func NewOAuthFlowContext ¶
func NewOAuthFlowContext(serverName string) *OAuthFlowContext
NewOAuthFlowContext creates a new OAuth flow context with a unique correlation ID.
func (*OAuthFlowContext) Duration ¶
func (c *OAuthFlowContext) Duration() time.Duration
Duration returns the time elapsed since the flow started.
func (*OAuthFlowContext) SetState ¶
func (c *OAuthFlowContext) SetState(state OAuthFlowState)
SetState updates the state of the OAuth flow.
type OAuthFlowCoordinator ¶
type OAuthFlowCoordinator struct {
// contains filtered or unexported fields
}
OAuthFlowCoordinator coordinates OAuth flows to ensure only one flow runs per server. This prevents race conditions where multiple reconnection attempts trigger concurrent OAuth flows.
func GetGlobalCoordinator ¶
func GetGlobalCoordinator() *OAuthFlowCoordinator
GetGlobalCoordinator returns the global OAuth flow coordinator instance.
func NewOAuthFlowCoordinator ¶
func NewOAuthFlowCoordinator() *OAuthFlowCoordinator
NewOAuthFlowCoordinator creates a new OAuth flow coordinator.
func (*OAuthFlowCoordinator) CleanupStaleFlows ¶
func (c *OAuthFlowCoordinator) CleanupStaleFlows() int
CleanupStaleFlows removes any OAuth flows that have been running longer than StaleFlowTimeout. This should be called periodically to prevent memory leaks from abandoned flows.
func (*OAuthFlowCoordinator) EndFlow ¶
func (c *OAuthFlowCoordinator) EndFlow(serverName string, success bool, err error)
EndFlow marks an OAuth flow as completed (success or failure). This notifies any waiting goroutines and cleans up the flow state.
func (*OAuthFlowCoordinator) GetActiveFlow ¶
func (c *OAuthFlowCoordinator) GetActiveFlow(serverName string) *OAuthFlowContext
GetActiveFlow returns the active OAuth flow context for the given server, if any.
func (*OAuthFlowCoordinator) IsFlowActive ¶
func (c *OAuthFlowCoordinator) IsFlowActive(serverName string) bool
IsFlowActive checks if an OAuth flow is currently active for the given server.
func (*OAuthFlowCoordinator) StartFlow ¶
func (c *OAuthFlowCoordinator) StartFlow(serverName string) (*OAuthFlowContext, error)
StartFlow starts a new OAuth flow for the given server. If a flow is already in progress, returns the existing flow context and ErrFlowInProgress. The caller should use WaitForFlow() instead if they want to wait for the existing flow.
func (*OAuthFlowCoordinator) UpdateFlowState ¶
func (c *OAuthFlowCoordinator) UpdateFlowState(serverName string, state OAuthFlowState)
UpdateFlowState updates the state of an active OAuth flow.
func (*OAuthFlowCoordinator) WaitForFlow ¶
func (c *OAuthFlowCoordinator) WaitForFlow(ctx context.Context, serverName string, timeout time.Duration) error
WaitForFlow waits for an active OAuth flow to complete. Returns nil if no flow is active (caller should start one). Returns ErrFlowTimeout if the wait times out. Returns the flow's error if it failed.
type OAuthFlowState ¶
type OAuthFlowState int
OAuthFlowState represents the current state of an OAuth authentication flow.
const ( // FlowInitiated indicates the OAuth flow has started. FlowInitiated OAuthFlowState = iota // FlowAuthenticating indicates the browser is open and waiting for user authentication. FlowAuthenticating // FlowTokenExchange indicates the authorization code is being exchanged for tokens. FlowTokenExchange // FlowCompleted indicates the OAuth flow completed successfully. FlowCompleted // FlowFailed indicates the OAuth flow failed with an error. FlowFailed )
func (OAuthFlowState) String ¶
func (s OAuthFlowState) String() string
String returns a human-readable representation of the OAuth flow state.
type OAuthMetadataError ¶
type OAuthMetadataError struct {
ErrorType string
ErrorCode string
ServerName string
Message string
Details *metadataErrorDetails
Suggestion string
}
OAuthMetadataError represents a metadata validation error (internal type) This is converted to contracts.OAuthFlowError by the caller
func (*OAuthMetadataError) Error ¶
func (e *OAuthMetadataError) Error() string
type OAuthMetadataValidationResult ¶
type OAuthMetadataValidationResult struct {
Valid bool
ProtectedResourceMetadata *ProtectedResourceMetadata
AuthorizationServerMetadata *OAuthServerMetadata
ProtectedResourceMetadataURL string
AuthorizationServerMetadataURL string
AuthorizationServerMetadataURLsTried []string // All URLs tried (RFC 8414 fallback)
ProtectedResourceError error
AuthorizationServerError error
}
OAuthMetadataValidationResult contains the result of OAuth metadata validation
func ValidateOAuthMetadata ¶
func ValidateOAuthMetadata(serverURL, serverName string, timeout time.Duration) (*OAuthMetadataValidationResult, error)
ValidateOAuthMetadata performs pre-flight validation of OAuth metadata. It fetches and validates both protected resource metadata (RFC 9728) and authorization server metadata (RFC 8414) before starting the OAuth flow. This enables early failure with clear, actionable error messages.
Parameters:
- serverURL: The MCP server URL to validate OAuth for
- serverName: The server name for error messages
- timeout: Timeout for HTTP requests
Returns:
- *OAuthMetadataValidationResult: Validation result with metadata if successful
- error: Structured OAuthFlowError if validation fails
type OAuthServerMetadata ¶
type OAuthServerMetadata struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
}
OAuthServerMetadata represents RFC 8414 OAuth Authorization Server Metadata
type OAuthStatus ¶
type OAuthStatus string
OAuthStatus represents the current authentication state of an OAuth server.
const ( // OAuthStatusNone indicates the server does not use OAuth. OAuthStatusNone OAuthStatus = "none" // OAuthStatusAuthenticated indicates valid OAuth token is available. OAuthStatusAuthenticated OAuthStatus = "authenticated" // OAuthStatusExpired indicates OAuth token has expired. OAuthStatusExpired OAuthStatus = "expired" // OAuthStatusError indicates OAuth authentication error. OAuthStatusError OAuthStatus = "error" )
func CalculateOAuthStatus ¶
func CalculateOAuthStatus(token *storage.OAuthTokenRecord, lastError string) OAuthStatus
CalculateOAuthStatus determines the OAuth status for a server based on token state. Returns OAuthStatusNone if token is nil (server doesn't use OAuth). Returns OAuthStatusError if lastError contains OAuth-related errors. Returns OAuthStatusExpired if token has expired. Returns OAuthStatusAuthenticated if token is valid.
func (OAuthStatus) IsValid ¶
func (s OAuthStatus) IsValid() bool
IsValid returns true if the status represents a valid OAuth state.
func (OAuthStatus) String ¶
func (s OAuthStatus) String() string
String returns the string representation of the OAuthStatus.
type OAuthTransportWrapper ¶
type OAuthTransportWrapper struct {
// contains filtered or unexported fields
}
OAuthTransportWrapper wraps an HTTP RoundTripper to inject extra OAuth parameters into authorization and token requests without modifying the mcp-go library.
This wrapper intercepts HTTP requests and adds custom parameters to: - Authorization requests (query parameters) - Token exchange requests (form body parameters) - Token refresh requests (form body parameters)
The wrapper is stateless and thread-safe for concurrent use.
func NewOAuthTransportWrapper ¶
func NewOAuthTransportWrapper(transport http.RoundTripper, extraParams map[string]string, logger *zap.Logger) *OAuthTransportWrapper
NewOAuthTransportWrapper creates a new transport wrapper that injects extra params.
Parameters:
- transport: The base HTTP RoundTripper to wrap (use http.DefaultTransport if nil)
- extraParams: Map of extra parameters to inject into OAuth requests
- logger: Logger for debug output (uses zap.L() if nil)
Returns a wrapper that can be used as http.Client.Transport.
func (*OAuthTransportWrapper) RoundTrip ¶
RoundTrip implements http.RoundTripper by intercepting requests and injecting extra params.
This method: 1. Detects OAuth authorization and token requests by URL path 2. Clones the request to avoid modifying the original 3. Injects extra parameters into query string (authorization) or body (token) 4. Delegates to the wrapped transport for actual HTTP execution 5. Logs parameter injection at DEBUG level for observability
type PersistentTokenStore ¶
type PersistentTokenStore struct {
// contains filtered or unexported fields
}
PersistentTokenStore implements client.TokenStore using BBolt storage
func (*PersistentTokenStore) ClearToken ¶
func (p *PersistentTokenStore) ClearToken() error
ClearToken removes the OAuth token from persistent storage
type ProtectedResourceMetadata ¶
type ProtectedResourceMetadata struct {
Resource string `json:"resource"`
ResourceName string `json:"resource_name,omitempty"`
AuthorizationServers []string `json:"authorization_servers"`
BearerMethodsSupported []string `json:"bearer_methods_supported,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
}
ProtectedResourceMetadata represents RFC 9728 Protected Resource Metadata
func DiscoverProtectedResourceMetadata ¶
func DiscoverProtectedResourceMetadata(metadataURL string, timeout time.Duration) (*ProtectedResourceMetadata, error)
DiscoverProtectedResourceMetadata fetches RFC 9728 Protected Resource Metadata and returns the full metadata structure including the resource parameter. This is the primary function for RFC 8707 resource auto-detection.
type RefreshEventEmitter ¶
type RefreshEventEmitter interface {
EmitOAuthTokenRefreshed(serverName string, expiresAt time.Time)
EmitOAuthRefreshFailed(serverName string, errorMsg string)
}
RefreshEventEmitter defines event emission methods for OAuth refresh events.
type RefreshManager ¶
type RefreshManager struct {
// contains filtered or unexported fields
}
RefreshManager coordinates proactive OAuth token refresh across all servers.
func NewRefreshManager ¶
func NewRefreshManager( tokenStore RefreshTokenStore, coordinator *OAuthFlowCoordinator, config *RefreshManagerConfig, logger *zap.Logger, ) *RefreshManager
NewRefreshManager creates a new RefreshManager instance.
func (*RefreshManager) GetRefreshState ¶ added in v0.16.0
func (m *RefreshManager) GetRefreshState(serverName string) *RefreshStateInfo
GetRefreshState returns the current refresh state for a server. This is used by the health calculator to determine health status. Returns nil if no schedule exists for the server.
func (*RefreshManager) GetSchedule ¶
func (m *RefreshManager) GetSchedule(serverName string) *RefreshSchedule
GetSchedule returns the refresh schedule for a server (for testing/debugging).
func (*RefreshManager) GetScheduleCount ¶
func (m *RefreshManager) GetScheduleCount() int
GetScheduleCount returns the number of active schedules (for testing/debugging).
func (*RefreshManager) OnTokenCleared ¶
func (m *RefreshManager) OnTokenCleared(serverName string)
OnTokenCleared is called when a token is cleared (e.g., logout). It cancels any scheduled refresh for that server.
func (*RefreshManager) OnTokenSaved ¶
func (m *RefreshManager) OnTokenSaved(serverName string, expiresAt time.Time)
OnTokenSaved is called when a token is saved to storage. It reschedules the proactive refresh for the new token expiration.
func (*RefreshManager) SetEventEmitter ¶
func (m *RefreshManager) SetEventEmitter(emitter RefreshEventEmitter)
SetEventEmitter sets the event emitter for SSE notifications.
func (*RefreshManager) SetMetricsRecorder ¶ added in v0.16.0
func (m *RefreshManager) SetMetricsRecorder(recorder RefreshMetricsRecorder)
SetMetricsRecorder sets the metrics recorder for Prometheus metrics. This enables FR-011: OAuth refresh metrics emission.
func (*RefreshManager) SetRuntime ¶
func (m *RefreshManager) SetRuntime(runtime RefreshRuntimeOperations)
SetRuntime sets the runtime operations interface. This must be called before Start() to enable token refresh.
func (*RefreshManager) Start ¶
func (m *RefreshManager) Start(ctx context.Context) error
Start initializes the refresh manager and loads existing tokens. For non-expired tokens, it schedules proactive refresh using hybrid strategy. For expired tokens with valid refresh tokens, it attempts immediate refresh.
func (*RefreshManager) Stop ¶
func (m *RefreshManager) Stop()
Stop cancels all scheduled refreshes and cleans up resources.
type RefreshManagerConfig ¶
type RefreshManagerConfig struct {
Threshold float64 // Percentage of lifetime at which to refresh (default: 0.8)
MaxRetries int // Maximum retry attempts (default: 3)
}
RefreshManagerConfig holds configuration for the RefreshManager.
type RefreshMetricsRecorder ¶ added in v0.16.0
type RefreshMetricsRecorder interface {
// RecordOAuthRefresh records an OAuth token refresh attempt.
// Result should be one of: "success", "failed_network", "failed_invalid_grant", "failed_other".
RecordOAuthRefresh(server, result string)
// RecordOAuthRefreshDuration records the duration of an OAuth token refresh attempt.
RecordOAuthRefreshDuration(server, result string, duration time.Duration)
}
RefreshMetricsRecorder defines metrics recording methods for OAuth refresh operations. This interface decouples RefreshManager from the concrete MetricsManager.
type RefreshRuntimeOperations ¶
RefreshRuntimeOperations defines runtime methods needed by RefreshManager.
type RefreshSchedule ¶
type RefreshSchedule struct {
ServerName string // Unique server identifier
ExpiresAt time.Time // When the current token expires
ScheduledRefresh time.Time // When proactive refresh is scheduled (hybrid strategy)
RetryCount int // Number of refresh retry attempts
LastError string // Last refresh error message
Timer *time.Timer // Background timer for scheduled refresh
RetryBackoff time.Duration // Current backoff duration for retries
MaxBackoff time.Duration // Maximum backoff duration (5 minutes)
LastAttempt time.Time // Time of last refresh attempt
RefreshState RefreshState // Current state for health reporting
}
RefreshSchedule tracks the proactive refresh state for a single server.
type RefreshState ¶ added in v0.16.0
type RefreshState int
RefreshState represents the current state of token refresh for health reporting.
const ( // RefreshStateIdle means no refresh is pending or in progress. RefreshStateIdle RefreshState = iota // RefreshStateScheduled means a proactive refresh is scheduled (hybrid: 75% lifetime or 5min buffer). RefreshStateScheduled // RefreshStateRetrying means refresh failed and is retrying with exponential backoff. RefreshStateRetrying // RefreshStateFailed means refresh permanently failed (e.g., invalid_grant). RefreshStateFailed )
func (RefreshState) String ¶ added in v0.16.0
func (s RefreshState) String() string
String returns the string representation of RefreshState.
type RefreshStateInfo ¶ added in v0.16.0
type RefreshStateInfo struct {
State RefreshState // Current refresh state
RetryCount int // Number of retry attempts
LastError string // Last error message
NextAttempt *time.Time // When next refresh attempt is scheduled
ExpiresAt time.Time // When the token expires
}
RefreshStateInfo contains refresh state information for health status reporting.
type RefreshTokenConfig ¶
type RefreshTokenConfig struct {
MaxAttempts int
InitialBackoff time.Duration
MaxBackoff time.Duration
}
RefreshTokenConfig contains configuration for token refresh operations.
func DefaultRefreshConfig ¶
func DefaultRefreshConfig() RefreshTokenConfig
DefaultRefreshConfig returns the default token refresh configuration.
type RefreshTokenStore ¶
type RefreshTokenStore interface {
ListOAuthTokens() ([]*storage.OAuthTokenRecord, error)
GetOAuthToken(serverName string) (*storage.OAuthTokenRecord, error)
}
RefreshTokenStore defines storage operations needed by RefreshManager.
type TokenMetadata ¶
type TokenMetadata struct {
TokenType string
ExpiresAt time.Time
ExpiresIn time.Duration
Scope string
HasRefreshToken bool
}
TokenMetadata contains non-sensitive token information for logging.
type TokenRefreshResult ¶
type TokenRefreshResult struct {
Success bool
NewToken *storage.OAuthTokenRecord
Error error
Attempt int
MaxAttempts int
}
TokenRefreshResult contains the result of a token refresh attempt.
type TokenStoreManager ¶
type TokenStoreManager struct {
// contains filtered or unexported fields
}
Global token store manager to persist tokens across client instances
func GetTokenStoreManager ¶
func GetTokenStoreManager() *TokenStoreManager
GetTokenStoreManager returns the global token store manager for debugging
func (*TokenStoreManager) GetOrCreateTokenStore ¶
func (m *TokenStoreManager) GetOrCreateTokenStore(serverName string) client.TokenStore
GetOrCreateTokenStore returns a shared token store for the given server
func (*TokenStoreManager) HasRecentOAuthCompletion ¶
func (m *TokenStoreManager) HasRecentOAuthCompletion(serverName string) bool
HasRecentOAuthCompletion checks if OAuth was recently completed for a server
func (*TokenStoreManager) HasTokenStore ¶
func (m *TokenStoreManager) HasTokenStore(serverName string) bool
HasTokenStore checks if a token store exists for the server in memory (for debugging) Note: This only checks the in-memory store, not persisted tokens in BBolt. Use HasPersistedToken() to check for tokens in persistent storage.
func (*TokenStoreManager) HasValidToken ¶
func (m *TokenStoreManager) HasValidToken(ctx context.Context, serverName string, storage *storage.BoltDB) bool
HasValidToken checks if a server has a valid, non-expired OAuth token Returns true if token exists and hasn't expired (with grace period)
func (*TokenStoreManager) MarkOAuthCompleted ¶
func (m *TokenStoreManager) MarkOAuthCompleted(serverName string)
MarkOAuthCompleted records that OAuth was successfully completed for a server This method is used by CLI processes to notify server processes about OAuth completion
func (*TokenStoreManager) MarkOAuthCompletedWithDB ¶
func (m *TokenStoreManager) MarkOAuthCompletedWithDB(serverName string, storage DatabaseOAuthNotifier) error
MarkOAuthCompletedWithDB records OAuth completion in the database for cross-process notification This is the new preferred method that works across different processes
func (*TokenStoreManager) NotifyTokenSaved ¶
func (m *TokenStoreManager) NotifyTokenSaved(serverName string, expiresAt time.Time)
NotifyTokenSaved triggers the token saved callback if set. Called by PersistentTokenStore.SaveToken() to notify the RefreshManager.
func (*TokenStoreManager) SetOAuthCompletionCallback ¶
func (m *TokenStoreManager) SetOAuthCompletionCallback(callback func(serverName string))
SetOAuthCompletionCallback sets a callback function to be called when OAuth completes
func (*TokenStoreManager) SetTokenSavedCallback ¶
func (m *TokenStoreManager) SetTokenSavedCallback(callback func(serverName string, expiresAt time.Time))
SetTokenSavedCallback sets a callback function to be called when a token is saved. Used by RefreshManager to reschedule proactive refresh when tokens are updated.