oauth

package
v0.17.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 3, 2026 License: MIT Imports: 24 Imported by: 0

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

View Source
const (
	// Default OAuth redirect URI base - port will be dynamically assigned
	DefaultRedirectURIBase = "http://127.0.0.1"
	DefaultRedirectPath    = "/oauth/callback"
)
View Source
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.

View Source
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

View Source
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

View Source
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

View Source
var ErrFlowInProgress = errors.New("OAuth flow already in progress")

ErrFlowInProgress indicates an OAuth flow is already in progress for the server.

View Source
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

func BuildRFC8414MetadataURLs(authServerURL string) []string

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

func CorrelationLogger(ctx context.Context, logger *zap.Logger) *zap.Logger

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

func CorrelationLoggerWithFlow(ctx context.Context, logger *zap.Logger) *zap.Logger

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):

  1. Manual extra_params.resource from config (highest priority - preserves backward compatibility)
  2. Auto-detected resource from RFC 9728 Protected Resource Metadata
  3. Fallback to server URL if metadata is unavailable or lacks resource field

func DetectOAuthAvailability

func DetectOAuthAvailability(baseURL string, timeout time.Duration) bool

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

func DiscoverAuthServerURL(serverURL string, timeout time.Duration) string

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

func DiscoverScopesFromAuthorizationServer(baseURL string, timeout time.Duration) ([]string, error)

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

func ExtractResourceMetadataURL(wwwAuthHeader string) string

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

func FindWorkingMetadataURL(serverURL string, timeout time.Duration) (string, error)

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

func GenerateServerKey(serverName, serverURL string) string

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

func GetCorrelationID(ctx context.Context) string

GetCorrelationID retrieves the correlation ID from the context. Returns an empty string if no correlation ID is present.

func GetPersistedRefreshToken

func GetPersistedRefreshToken(serverName, serverURL string, boltStorage *storage.BoltDB) string

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:

  1. OAuth is explicitly configured in config, OR
  2. 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

func IsOAuthError(err string) bool

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

func LogActualTokenRefreshAttempt(logger *zap.Logger, serverName string, tokenAge time.Duration)

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

func LogClientConnectionAttempt(logger *zap.Logger, attempt int, maxAttempts int)

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

func LogClientConnectionFailure(logger *zap.Logger, attempt int, err error)

LogClientConnectionFailure logs a failed client connection attempt.

func LogClientConnectionSuccess added in v0.16.0

func LogClientConnectionSuccess(logger *zap.Logger, duration time.Duration)

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

func LogOAuthFlowStart(logger *zap.Logger, serverName string, correlationID string)

LogOAuthFlowStart logs the start of an OAuth flow.

func LogOAuthRequest

func LogOAuthRequest(logger *zap.Logger, method, url string, headers http.Header)

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 LogTokenRefreshAttempt(logger *zap.Logger, attempt int, maxAttempts int)

Deprecated: Use LogClientConnectionAttempt instead. LogTokenRefreshAttempt is kept for backward compatibility but is misleading.

func LogTokenRefreshFailure deprecated

func LogTokenRefreshFailure(logger *zap.Logger, attempt int, err error)

Deprecated: Use LogClientConnectionFailure instead. LogTokenRefreshFailure is kept for backward compatibility but is misleading.

func LogTokenRefreshSuccess deprecated

func LogTokenRefreshSuccess(logger *zap.Logger, duration time.Duration)

Deprecated: Use LogClientConnectionSuccess instead. LogTokenRefreshSuccess is kept for backward compatibility but is misleading. This is called when client.Start() succeeds, not when a token refresh occurs.

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

func RedactHeaders(headers http.Header) map[string]string

RedactHeaders creates a copy of headers with sensitive values redacted. Returns a map suitable for logging.

func RedactSensitiveData

func RedactSensitiveData(data string) string

RedactSensitiveData redacts sensitive information from a string. It replaces tokens, secrets, and other sensitive data with redacted placeholders.

func RedactURL

func RedactURL(urlStr string) string

RedactURL redacts sensitive query parameters from a URL string.

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

func WithCorrelationID(ctx context.Context, id string) context.Context

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

func (w *OAuthTransportWrapper) RoundTrip(req *http.Request) (*http.Response, error)

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

func (*PersistentTokenStore) GetToken

func (p *PersistentTokenStore) GetToken(ctx context.Context) (*client.Token, error)

GetToken retrieves the OAuth token from persistent storage

func (*PersistentTokenStore) SaveToken

func (p *PersistentTokenStore) SaveToken(ctx context.Context, token *client.Token) error

SaveToken stores the OAuth token to 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

type RefreshRuntimeOperations interface {
	RefreshOAuthToken(serverName string) error
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL