Documentation
¶
Overview ¶
Package oauth implements OAuth 2.1 proxy functionality for remote MCP server authentication.
This package provides the server-side OAuth client and proxy implementation that allows the Muster Server to authenticate with remote MCP servers on behalf of users without exposing sensitive tokens to the Muster Agent.
Architecture ¶
The OAuth proxy follows a three-legged OAuth 2.1 Authorization Code flow:
- User requests a tool that requires authentication
- Muster Server detects 401 Unauthorized from the remote MCP server
- Muster Server generates an authorization URL and returns "Auth Required" challenge
- User authenticates via their browser with the Identity Provider
- Browser redirects to Muster Server's callback endpoint with authorization code
- Muster Server exchanges code for tokens and stores them securely
- User retries the original request, which now succeeds with the stored token
Components ¶
- TokenStore: In-memory storage for OAuth tokens, indexed by session and issuer
- StateStore: Manages OAuth state parameters for CSRF protection
- Client: Handles OAuth flows, code exchange, and token refresh
- Handler: HTTP handler for the /oauth/callback endpoint
- Manager: Coordinates OAuth flows and integrates with the aggregator
Security ¶
## Token Storage
Tokens are stored in-memory only and are never persisted to disk. This means:
- Tokens are lost when the Muster Server restarts
- Users must re-authenticate after a restart
- No encryption-at-rest is needed since tokens exist only in process memory
For persistent token storage in future versions, encryption would be required.
## Session Isolation
Each MCP connection (SSE or Streamable HTTP) receives a unique UUID-based session ID from the mcp-go library. Tokens are stored with a composite key of (SessionID, Issuer, Scope), ensuring complete isolation between users. User A cannot access User B's tokens.
For stdio transport (single-user CLI), a default session ID is used. This is acceptable since stdio is inherently single-user (one process = one user).
## TLS/HTTPS Requirements (CRITICAL)
Production deployments MUST use HTTPS for all OAuth-related endpoints:
Muster Server's public URL (oauth.publicUrl configuration): The OAuth callback endpoint receives authorization codes. Without HTTPS, attackers could intercept these codes and exchange them for tokens.
OAuth Issuer URLs: All communication with Identity Providers (metadata discovery, token exchange, token refresh) must be over HTTPS. The issuer's TLS certificate provides integrity and authenticity guarantees for OAuth metadata.
Remote MCP Server URLs: When MCP servers require OAuth authentication, their endpoints should use HTTPS to protect the bearer tokens in Authorization headers.
Without TLS, the following attacks become possible:
- Authorization code interception during OAuth callback
- Token theft via man-in-the-middle attacks
- Metadata manipulation to redirect token exchanges to malicious endpoints
- Bearer token theft from Authorization headers
## Rate Limiting Recommendations
The OAuth callback endpoint (/oauth/callback by default) should be protected by rate limiting at the infrastructure level (ingress controller, load balancer, or API gateway). Recommended limits:
- Per-IP rate limit: 10-20 requests per minute
- Global rate limit: 100-500 requests per minute (depending on expected user base)
Rate limiting protects against:
- Denial of service attacks on the OAuth callback endpoint
- Brute-force attempts to guess authorization codes or state parameters
- Resource exhaustion from excessive token exchange requests
Example Kubernetes Ingress annotation (nginx):
nginx.ingress.kubernetes.io/limit-rps: "10" nginx.ingress.kubernetes.io/limit-connections: "5"
## Logging Security
Session IDs are truncated in log output to prevent full session identifiers from appearing in logs. Only the first 8 characters are logged (e.g., "abc12345..."). Access tokens and refresh tokens are never logged.
Token refresh operations are logged at INFO level for operational monitoring, including duration metrics for performance tracking.
SSO Support ¶
The package supports Single Sign-On (SSO) through Token Forwarding and Token Exchange. When a user authenticates with muster, the token can be forwarded to downstream servers (Token Forwarding) or exchanged for a token valid on a remote IdP (Token Exchange).
Index ¶
- Constants
- func GetExpectedIssuer(config *api.TokenExchangeConfig) string
- type Adapter
- func (a *Adapter) ClearTokenByIssuer(sessionID, issuer string)
- func (a *Adapter) CreateAuthChallenge(ctx context.Context, sessionID, serverName, issuer, scope string) (*api.AuthChallenge, error)
- func (a *Adapter) ExchangeTokenForRemoteCluster(ctx context.Context, localToken, userID string, ...) (string, error)
- func (a *Adapter) ExchangeTokenForRemoteClusterWithClient(ctx context.Context, localToken, userID string, ...) (string, error)
- func (a *Adapter) FindTokenWithIDToken(sessionID string) *api.OAuthToken
- func (a *Adapter) GetCIMDHandler() http.HandlerFunc
- func (a *Adapter) GetCIMDPath() string
- func (a *Adapter) GetCallbackPath() string
- func (a *Adapter) GetFullTokenByIssuer(sessionID, issuer string) *api.OAuthToken
- func (a *Adapter) GetHTTPHandler() http.Handler
- func (a *Adapter) GetToken(sessionID, serverName string) *api.OAuthToken
- func (a *Adapter) GetTokenByIssuer(sessionID, issuer string) *api.OAuthToken
- func (a *Adapter) IsEnabled() bool
- func (a *Adapter) Register()
- func (a *Adapter) RegisterServer(serverName, issuer, scope string)
- func (a *Adapter) SetAuthCompletionCallback(callback api.AuthCompletionCallback)
- func (a *Adapter) ShouldServeCIMD() bool
- func (a *Adapter) Stop()
- func (a *Adapter) StoreToken(sessionID, issuer string, token *api.OAuthToken)
- type AuthCompletionCallback
- type AuthRequiredResponse
- type AuthServerConfig
- type Client
- func (c *Client) DiscoverMetadata(ctx context.Context, issuer string) (*pkgoauth.Metadata, error)
- func (c *Client) ExchangeCode(ctx context.Context, code, codeVerifier, issuer string) (*pkgoauth.Token, error)
- func (c *Client) GenerateAuthURL(ctx context.Context, sessionID, serverName, issuer, scope string) (string, error)
- func (c *Client) GetCIMDURL() string
- func (c *Client) GetClientMetadata() *pkgoauth.ClientMetadata
- func (c *Client) GetRedirectURI() string
- func (c *Client) GetStateStore() *StateStore
- func (c *Client) GetToken(sessionID, issuer, scope string) *pkgoauth.Token
- func (c *Client) GetTokenStore() *TokenStore
- func (c *Client) SetHTTPClient(httpClient *http.Client)
- func (c *Client) Stop()
- func (c *Client) StoreToken(sessionID string, token *pkgoauth.Token)
- type ExchangeRequest
- type ExchangeResult
- type Handler
- type Manager
- func (m *Manager) ClearTokenByIssuer(sessionID, issuer string)
- func (m *Manager) CreateAuthChallenge(ctx context.Context, sessionID, serverName, issuer, scope string) (*AuthRequiredResponse, error)
- func (m *Manager) ExchangeTokenForRemoteCluster(ctx context.Context, localToken, userID string, ...) (string, error)
- func (m *Manager) ExchangeTokenForRemoteClusterWithClient(ctx context.Context, localToken, userID string, ...) (string, error)
- func (m *Manager) GetCIMDHandler() http.HandlerFunc
- func (m *Manager) GetCIMDPath() string
- func (m *Manager) GetCallbackPath() string
- func (m *Manager) GetHTTPHandler() http.Handler
- func (m *Manager) GetServerConfig(serverName string) *AuthServerConfig
- func (m *Manager) GetToken(sessionID, serverName string) *pkgoauth.Token
- func (m *Manager) GetTokenByIssuer(sessionID, issuer string) *pkgoauth.Token
- func (m *Manager) GetTokenExchanger() *TokenExchanger
- func (m *Manager) HandleCallback(ctx context.Context, code, state string) error
- func (m *Manager) IsEnabled() bool
- func (m *Manager) RegisterServer(serverName, issuer, scope string)
- func (m *Manager) SetAuthCompletionCallback(callback AuthCompletionCallback)
- func (m *Manager) ShouldServeCIMD() bool
- func (m *Manager) Stop()
- func (m *Manager) StoreToken(sessionID, issuer string, token *pkgoauth.Token)
- type OAuthState
- type RedactedToken
- type StateStore
- type TokenExchanger
- func (e *TokenExchanger) Cleanup() int
- func (e *TokenExchanger) ClearAllCache()
- func (e *TokenExchanger) ClearCache(tokenEndpoint, connectorID, userID string)
- func (e *TokenExchanger) Exchange(ctx context.Context, req *ExchangeRequest) (*ExchangeResult, error)
- func (e *TokenExchanger) ExchangeWithClient(ctx context.Context, req *ExchangeRequest, httpClient *http.Client) (*ExchangeResult, error)
- func (e *TokenExchanger) GetCacheStats() oidc.TokenExchangeCacheStats
- type TokenExchangerOptions
- type TokenKey
- type TokenStore
- func (ts *TokenStore) Count() int
- func (ts *TokenStore) Delete(key TokenKey)
- func (ts *TokenStore) DeleteByIssuer(sessionID, issuer string)
- func (ts *TokenStore) DeleteBySession(sessionID string)
- func (ts *TokenStore) Get(key TokenKey) *pkgoauth.Token
- func (ts *TokenStore) GetAllForSession(sessionID string) map[TokenKey]*pkgoauth.Token
- func (ts *TokenStore) GetByIssuer(sessionID, issuer string) *pkgoauth.Token
- func (ts *TokenStore) GetTokenKeyByIssuer(sessionID, issuer string) *TokenKey
- func (ts *TokenStore) Stop()
- func (ts *TokenStore) Store(key TokenKey, token *pkgoauth.Token)
Constants ¶
const DefaultOIDCScopes = "openid profile email groups"
DefaultOIDCScopes is the default set of scopes requested for OIDC token exchange. These scopes provide identity (openid), user profile info (profile, email), and group membership (groups) for RBAC decisions.
Variables ¶
This section is empty.
Functions ¶
func GetExpectedIssuer ¶
func GetExpectedIssuer(config *api.TokenExchangeConfig) string
GetExpectedIssuer returns the expected issuer URL for token validation. If ExpectedIssuer is explicitly set in the config, it is used directly. Otherwise, the issuer is derived from DexTokenEndpoint (backward compatible).
This separation is important for proxied access scenarios:
- DexTokenEndpoint may go through a proxy (e.g., https://dex-cluster.proxy.example.com/token)
- ExpectedIssuer is the actual Dex issuer (e.g., https://dex.cluster-b.example.com)
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter implements api.OAuthHandler by wrapping the OAuth Manager. This follows the service locator pattern where packages communicate through interfaces defined in the api package.
func NewAdapter ¶
NewAdapter creates a new OAuth API adapter wrapping the given manager.
func (*Adapter) ClearTokenByIssuer ¶
ClearTokenByIssuer removes all tokens for a given session and issuer.
func (*Adapter) CreateAuthChallenge ¶
func (a *Adapter) CreateAuthChallenge(ctx context.Context, sessionID, serverName, issuer, scope string) (*api.AuthChallenge, error)
CreateAuthChallenge creates an authentication challenge for a 401 response.
func (*Adapter) ExchangeTokenForRemoteCluster ¶
func (a *Adapter) ExchangeTokenForRemoteCluster(ctx context.Context, localToken, userID string, config *api.TokenExchangeConfig) (string, error)
ExchangeTokenForRemoteCluster exchanges a local token for one valid on a remote cluster. This implements RFC 8693 Token Exchange for cross-cluster SSO scenarios.
func (*Adapter) ExchangeTokenForRemoteClusterWithClient ¶
func (a *Adapter) ExchangeTokenForRemoteClusterWithClient(ctx context.Context, localToken, userID string, config *api.TokenExchangeConfig, httpClient *http.Client) (string, error)
ExchangeTokenForRemoteClusterWithClient exchanges a local token for one valid on a remote cluster using a custom HTTP client. This is used when the token exchange endpoint is accessed via Teleport Application Access, which requires mutual TLS authentication.
func (*Adapter) FindTokenWithIDToken ¶
func (a *Adapter) FindTokenWithIDToken(sessionID string) *api.OAuthToken
FindTokenWithIDToken searches for any token in the session that has an ID token. This is used as a fallback when the muster issuer is not explicitly configured. Returns the first token found with an ID token, or nil if none exists.
func (*Adapter) GetCIMDHandler ¶
func (a *Adapter) GetCIMDHandler() http.HandlerFunc
GetCIMDHandler returns the HTTP handler for serving the CIMD.
func (*Adapter) GetCIMDPath ¶
GetCIMDPath returns the path for serving the CIMD.
func (*Adapter) GetCallbackPath ¶
GetCallbackPath returns the configured callback path.
func (*Adapter) GetFullTokenByIssuer ¶
func (a *Adapter) GetFullTokenByIssuer(sessionID, issuer string) *api.OAuthToken
GetFullTokenByIssuer retrieves the full token (including ID token if available) for the given session and issuer. Returns nil if no valid token exists. The IDToken field may be empty if the token was obtained without an ID token.
func (*Adapter) GetHTTPHandler ¶
GetHTTPHandler returns the HTTP handler for OAuth callback endpoints.
func (*Adapter) GetToken ¶
func (a *Adapter) GetToken(sessionID, serverName string) *api.OAuthToken
GetToken retrieves a valid token for the given session and server.
func (*Adapter) GetTokenByIssuer ¶
func (a *Adapter) GetTokenByIssuer(sessionID, issuer string) *api.OAuthToken
GetTokenByIssuer retrieves a valid token for the given session and issuer.
func (*Adapter) Register ¶
func (a *Adapter) Register()
Register registers this adapter with the API layer.
func (*Adapter) RegisterServer ¶
RegisterServer registers OAuth configuration for a remote MCP server.
func (*Adapter) SetAuthCompletionCallback ¶
func (a *Adapter) SetAuthCompletionCallback(callback api.AuthCompletionCallback)
SetAuthCompletionCallback sets the callback to be called after successful authentication.
func (*Adapter) ShouldServeCIMD ¶
ShouldServeCIMD returns true if muster should serve its own CIMD.
func (*Adapter) Stop ¶
func (a *Adapter) Stop()
Stop stops the OAuth handler and cleans up resources.
func (*Adapter) StoreToken ¶ added in v0.0.231
func (a *Adapter) StoreToken(sessionID, issuer string, token *api.OAuthToken)
StoreToken persists a token for the given session and issuer. This converts the API token to a pkg/oauth token and delegates to the manager's single backing store.
type AuthCompletionCallback ¶
type AuthCompletionCallback func(ctx context.Context, sessionID, serverName, accessToken string) error
AuthCompletionCallback is called after successful OAuth authentication.
type AuthRequiredResponse ¶
type AuthRequiredResponse struct {
// Status indicates this is an auth required response.
Status string `json:"status"` // "auth_required"
// AuthURL is the OAuth authorization URL the user should visit.
AuthURL string `json:"auth_url"`
// ServerName is the name of the MCP server requiring authentication.
ServerName string `json:"server_name,omitempty"`
// Message is a human-readable description of why auth is needed.
Message string `json:"message,omitempty"`
}
AuthRequiredResponse represents an authentication challenge returned when a remote MCP server requires OAuth authentication. Note: This is different from pkgoauth.AuthChallenge which represents parsed WWW-Authenticate header data. This is a user-facing response.
type AuthServerConfig ¶
AuthServerConfig holds OAuth configuration for a specific remote MCP server.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client handles OAuth 2.1 flows for remote MCP server authentication.
func (*Client) DiscoverMetadata ¶
DiscoverMetadata fetches OAuth metadata for an issuer. This is exposed for external access to metadata discovery.
func (*Client) ExchangeCode ¶
func (c *Client) ExchangeCode(ctx context.Context, code, codeVerifier, issuer string) (*pkgoauth.Token, error)
ExchangeCode exchanges an authorization code for tokens.
func (*Client) GenerateAuthURL ¶
func (c *Client) GenerateAuthURL(ctx context.Context, sessionID, serverName, issuer, scope string) (string, error)
GenerateAuthURL creates an OAuth authorization URL for user authentication. Returns the URL. The code verifier is stored with the state for later retrieval.
func (*Client) GetCIMDURL ¶
GetCIMDURL returns the URL where the Client ID Metadata Document is served. This is derived from the clientID which is expected to be the CIMD URL.
func (*Client) GetClientMetadata ¶
func (c *Client) GetClientMetadata() *pkgoauth.ClientMetadata
GetClientMetadata returns the Client ID Metadata Document for this client.
func (*Client) GetRedirectURI ¶
GetRedirectURI returns the full redirect URI for OAuth callbacks.
func (*Client) GetStateStore ¶
func (c *Client) GetStateStore() *StateStore
GetStateStore returns the state store for external access.
func (*Client) GetToken ¶
GetToken retrieves a valid token for the given session and issuer. Returns nil if no valid token exists.
func (*Client) GetTokenStore ¶
func (c *Client) GetTokenStore() *TokenStore
GetTokenStore returns the token store for external access.
func (*Client) SetHTTPClient ¶
SetHTTPClient sets a custom HTTP client for the OAuth client. This is useful for testing.
type ExchangeRequest ¶
type ExchangeRequest struct {
// Config is the token exchange configuration for the target cluster.
// Uses the API type directly to avoid duplication (DRY principle).
Config *api.TokenExchangeConfig
// SubjectToken is the local token to exchange (ID token or access token).
SubjectToken string
// SubjectTokenType specifies whether SubjectToken is an ID token or access token.
// Use oidc.TokenTypeIDToken or oidc.TokenTypeAccessToken.
// Defaults to TokenTypeIDToken if not specified.
SubjectTokenType string
// UserID is extracted from the validated subject token's "sub" claim.
// CRITICAL: This must come from validated JWT claims, not user input.
// Used for cache key generation.
UserID string
}
ExchangeRequest contains the parameters for a token exchange operation.
type ExchangeResult ¶
type ExchangeResult struct {
// AccessToken is the exchanged token valid on the remote cluster.
AccessToken string
// IssuedTokenType is the type of the issued token.
IssuedTokenType string
// FromCache indicates whether the token was served from cache.
FromCache bool
}
ExchangeResult contains the result of a successful token exchange.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler provides HTTP handlers for OAuth callback endpoints.
func NewHandler ¶
NewHandler creates a new OAuth HTTP handler.
func (*Handler) HandleCallback ¶
func (h *Handler) HandleCallback(w http.ResponseWriter, r *http.Request)
HandleCallback handles the OAuth callback endpoint. This is called by the browser after the user authenticates with the IdP.
func (*Handler) ServeCIMD ¶
func (h *Handler) ServeCIMD(w http.ResponseWriter, r *http.Request)
ServeCIMD handles GET requests to serve the Client ID Metadata Document (CIMD). This allows muster to self-host its own CIMD without requiring external static hosting. The CIMD is dynamically generated from the OAuth configuration.
func (*Handler) ServeHTTP ¶
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler for the OAuth handler.
func (*Handler) SetManager ¶
SetManager sets the manager reference for callback handling. This is called by the Manager after creating the Handler.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager coordinates OAuth flows for remote MCP server authentication. It manages the OAuth MCP client, HTTP handlers, and integrates with the aggregator.
func NewManager ¶
func NewManager(cfg config.OAuthMCPClientConfig) *Manager
NewManager creates a new OAuth manager with the given configuration. The cfg parameter contains the OAuth MCP client/proxy configuration for authenticating TO remote MCP servers.
func (*Manager) ClearTokenByIssuer ¶
ClearTokenByIssuer removes all tokens for a given session and issuer. This is used to clear invalid/expired tokens before requesting fresh authentication.
func (*Manager) CreateAuthChallenge ¶
func (m *Manager) CreateAuthChallenge(ctx context.Context, sessionID, serverName, issuer, scope string) (*AuthRequiredResponse, error)
CreateAuthChallenge creates an authentication challenge for a 401 response. Returns the auth URL the user should visit and the challenge response.
func (*Manager) ExchangeTokenForRemoteCluster ¶
func (m *Manager) ExchangeTokenForRemoteCluster(ctx context.Context, localToken, userID string, config *api.TokenExchangeConfig) (string, error)
ExchangeTokenForRemoteCluster exchanges a local token for one valid on a remote cluster. This implements RFC 8693 Token Exchange for cross-cluster SSO scenarios.
Args:
- ctx: Context for the operation
- localToken: The local ID token to exchange
- userID: The user's unique identifier (from validated JWT 'sub' claim)
- config: Token exchange configuration for the remote cluster
Returns the exchanged access token, or an error if exchange fails.
func (*Manager) ExchangeTokenForRemoteClusterWithClient ¶
func (m *Manager) ExchangeTokenForRemoteClusterWithClient(ctx context.Context, localToken, userID string, config *api.TokenExchangeConfig, httpClient *http.Client) (string, error)
ExchangeTokenForRemoteClusterWithClient exchanges a local token for one valid on a remote cluster using a custom HTTP client. This is used when the token exchange endpoint is accessed via Teleport Application Access, which requires mutual TLS authentication.
The httpClient parameter should be configured with the appropriate TLS certificates (e.g., Teleport Machine ID certificates). If nil, uses the default HTTP client.
Args:
- ctx: Context for the operation
- localToken: The local ID token to exchange
- userID: The user's unique identifier (from validated JWT 'sub' claim)
- config: Token exchange configuration for the remote cluster
- httpClient: Custom HTTP client with Teleport TLS certificates (or nil for default)
Returns the exchanged access token, or an error if exchange fails.
func (*Manager) GetCIMDHandler ¶
func (m *Manager) GetCIMDHandler() http.HandlerFunc
GetCIMDHandler returns the HTTP handler for serving the CIMD.
func (*Manager) GetCIMDPath ¶
GetCIMDPath returns the path for serving the CIMD.
func (*Manager) GetCallbackPath ¶
GetCallbackPath returns the configured callback path.
func (*Manager) GetHTTPHandler ¶
GetHTTPHandler returns the HTTP handler for OAuth endpoints.
func (*Manager) GetServerConfig ¶
func (m *Manager) GetServerConfig(serverName string) *AuthServerConfig
GetServerConfig returns the OAuth configuration for a server.
func (*Manager) GetToken ¶
GetToken retrieves a valid token for the given session and server. mcp-go handles token refresh via its transport layer, so this method simply returns the stored token without proactive refresh.
func (*Manager) GetTokenByIssuer ¶
GetTokenByIssuer retrieves a valid token for the given session and issuer. This is used for SSO when we have the issuer from a 401 response. mcp-go handles token refresh via its transport layer, so this method simply returns the stored token without proactive refresh.
func (*Manager) GetTokenExchanger ¶
func (m *Manager) GetTokenExchanger() *TokenExchanger
GetTokenExchanger returns the token exchanger for direct access. This is useful for cache management and monitoring.
func (*Manager) HandleCallback ¶
HandleCallback processes an OAuth callback and stores the token. Note: This is a programmatic API for testing. The production flow uses Handler.HandleCallback which is the actual HTTP endpoint and handles the auth completion callback invocation.
func (*Manager) RegisterServer ¶
RegisterServer registers OAuth configuration for a remote MCP server.
func (*Manager) SetAuthCompletionCallback ¶
func (m *Manager) SetAuthCompletionCallback(callback AuthCompletionCallback)
SetAuthCompletionCallback sets the callback to be called after successful authentication. The aggregator uses this to establish session connections after browser OAuth completes.
func (*Manager) ShouldServeCIMD ¶
ShouldServeCIMD returns true if muster should serve its own CIMD.
type OAuthState ¶
type OAuthState struct {
// SessionID links the OAuth flow to the user's session.
SessionID string `json:"session_id"`
// ServerName is the MCP server that requires authentication.
ServerName string `json:"server_name"`
// Nonce is a random value for CSRF protection.
Nonce string `json:"nonce"`
// CreatedAt is when the state was created (for expiration).
CreatedAt time.Time `json:"created_at"`
// RedirectURI is where to redirect after callback processing.
RedirectURI string `json:"redirect_uri,omitempty"`
// Issuer is the OAuth issuer URL for token exchange.
Issuer string `json:"issuer,omitempty"`
// CodeVerifier is the PKCE code verifier for this flow.
// Stored server-side only, not transmitted in the state parameter.
CodeVerifier string `json:"-"`
}
OAuthState represents the state parameter data for OAuth flows. This is serialized and passed through the OAuth flow to link the callback to the original request. This is server-specific as it handles CSRF protection for server-side OAuth.
type RedactedToken ¶
type RedactedToken struct {
// contains filtered or unexported fields
}
RedactedToken wraps a sensitive token string to prevent accidental logging.
This type implements fmt.Stringer to return "[REDACTED]" instead of the actual token value, preventing accidental credential leakage in log messages, error strings, or debug output.
Usage:
token := oauth.NewRedactedToken("secret-token-value")
fmt.Println(token) // prints: [REDACTED]
actualValue := token.Value() // returns: "secret-token-value"
func (RedactedToken) GoString ¶
func (t RedactedToken) GoString() string
GoString implements fmt.GoStringer for %#v formatting, also returning "[REDACTED]" to prevent accidental logging.
func (RedactedToken) IsEmpty ¶
func (t RedactedToken) IsEmpty() bool
IsEmpty returns true if the token value is empty.
func (RedactedToken) MarshalJSON ¶
func (t RedactedToken) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler, returning "[REDACTED]" to prevent accidental JSON serialization of the token value.
func (RedactedToken) MarshalText ¶
func (t RedactedToken) MarshalText() ([]byte, error)
MarshalText implements encoding.TextMarshaler, returning "[REDACTED]" to prevent accidental serialization of the token value.
func (RedactedToken) String ¶
func (t RedactedToken) String() string
String implements fmt.Stringer, returning "[REDACTED]" to prevent accidental logging of the token value.
func (RedactedToken) Value ¶
func (t RedactedToken) Value() string
Value returns the actual token value. Use this method only when the token needs to be sent in an HTTP header or similar authenticated request. Never log the result of this method.
type StateStore ¶
type StateStore struct {
// contains filtered or unexported fields
}
StateStore provides thread-safe storage for OAuth state parameters. State parameters are used to link OAuth callbacks to original requests and provide CSRF protection.
IMPORTANT: StateStore starts a background goroutine for cleanup. Callers MUST call Stop() when done to prevent goroutine leaks. Typically this is done via defer after creating the store, or in a shutdown hook for long-lived stores.
func NewStateStore ¶
func NewStateStore() *StateStore
NewStateStore creates a new state store with default expiration.
func (*StateStore) Delete ¶
func (ss *StateStore) Delete(nonce string)
Delete removes a state from the store.
func (*StateStore) GenerateState ¶
func (ss *StateStore) GenerateState(sessionID, serverName, issuer, codeVerifier string) (encodedState string, err error)
GenerateState creates a new OAuth state parameter and stores it. Returns the encoded state string to include in the authorization URL. The nonce is embedded within the encoded state and used for server-side lookup.
Args:
- sessionID: The user's session ID
- serverName: The MCP server name requiring authentication
- issuer: The OAuth issuer URL
- codeVerifier: The PKCE code verifier for this flow
func (*StateStore) ValidateState ¶
func (ss *StateStore) ValidateState(encodedState string) *OAuthState
ValidateState validates an OAuth state parameter from a callback. Returns the original state data if valid, nil if invalid or expired.
type TokenExchanger ¶
type TokenExchanger struct {
// contains filtered or unexported fields
}
TokenExchanger performs RFC 8693 OAuth 2.0 Token Exchange for cross-cluster SSO. It enables users authenticated to muster (Cluster A) to access MCP servers on remote clusters (Cluster B) by exchanging their local token for a token valid on the remote cluster's Identity Provider.
This is different from token forwarding (ForwardToken), which forwards muster's ID token directly. Token exchange is useful when:
- Remote clusters have separate Dex instances
- The remote Dex is configured with an OIDC connector pointing to muster's Dex
- You need a token issued by the remote cluster's IdP
Thread-safe: Yes, the underlying TokenExchangeClient is thread-safe.
func NewTokenExchangerWithOptions ¶
func NewTokenExchangerWithOptions(opts TokenExchangerOptions) *TokenExchanger
NewTokenExchangerWithOptions creates a new TokenExchanger with custom options.
func (*TokenExchanger) Cleanup ¶
func (e *TokenExchanger) Cleanup() int
Cleanup removes expired tokens from the cache. This should be called periodically for long-running services.
func (*TokenExchanger) ClearAllCache ¶
func (e *TokenExchanger) ClearAllCache()
ClearAllCache removes all cached tokens.
func (*TokenExchanger) ClearCache ¶
func (e *TokenExchanger) ClearCache(tokenEndpoint, connectorID, userID string)
ClearCache removes a cached token for the given parameters. This is useful when a cached token is rejected by the remote server.
func (*TokenExchanger) Exchange ¶
func (e *TokenExchanger) Exchange(ctx context.Context, req *ExchangeRequest) (*ExchangeResult, error)
Exchange exchanges a local token for a token valid on a remote cluster. The token is cached to reduce the number of exchange requests.
Args:
- ctx: Context for cancellation and timeouts
- req: Exchange request parameters
Returns the exchanged token or an error if exchange fails.
func (*TokenExchanger) ExchangeWithClient ¶
func (e *TokenExchanger) ExchangeWithClient(ctx context.Context, req *ExchangeRequest, httpClient *http.Client) (*ExchangeResult, error)
ExchangeWithClient exchanges a local token for a token valid on a remote cluster using a custom HTTP client. This is used when the token exchange endpoint is accessed via Teleport Application Access, which requires mutual TLS authentication.
The httpClient parameter should be configured with the appropriate TLS certificates (e.g., Teleport Machine ID certificates). If nil, uses the default exchanger client.
Args:
- ctx: Context for cancellation and timeouts
- req: Exchange request parameters
- httpClient: Custom HTTP client with Teleport TLS certificates (or nil for default)
Returns the exchanged token or an error if exchange fails.
func (*TokenExchanger) GetCacheStats ¶
func (e *TokenExchanger) GetCacheStats() oidc.TokenExchangeCacheStats
GetCacheStats returns statistics about the token exchange cache.
type TokenExchangerOptions ¶
type TokenExchangerOptions struct {
// Logger for debug/info messages (nil uses default logger).
Logger *slog.Logger
// AllowPrivateIP allows token endpoints to resolve to private IP addresses.
// WARNING: Reduces SSRF protection. Only enable for internal/VPN deployments.
AllowPrivateIP bool
// CacheMaxEntries is the maximum number of cached tokens (0 = default: 10000).
CacheMaxEntries int
// HTTPClient is the HTTP client to use for token exchange requests.
// If nil, an appropriate client is created based on AllowPrivateIP setting.
// Use this to configure custom TLS settings (e.g., for self-signed certs).
HTTPClient *http.Client
}
TokenExchangerOptions configures the TokenExchanger.
type TokenKey ¶
TokenKey uniquely identifies a token in the store. Tokens are indexed by session ID, issuer, and scope to enable SSO. This is server-specific as it handles session-scoped token storage.
type TokenStore ¶
type TokenStore struct {
// contains filtered or unexported fields
}
TokenStore provides thread-safe in-memory storage for OAuth tokens. Tokens are indexed by session ID, issuer, and scope to support SSO.
IMPORTANT: TokenStore starts a background goroutine for cleanup. Callers MUST call Stop() when done to prevent goroutine leaks. Typically this is done via defer after creating the store, or in a shutdown hook for long-lived stores.
func NewTokenStore ¶
func NewTokenStore() *TokenStore
NewTokenStore creates a new in-memory token store. It starts a background goroutine for periodic cleanup of expired tokens.
func (*TokenStore) Count ¶
func (ts *TokenStore) Count() int
Count returns the number of tokens in the store.
func (*TokenStore) Delete ¶
func (ts *TokenStore) Delete(key TokenKey)
Delete removes a token from the store.
func (*TokenStore) DeleteByIssuer ¶
func (ts *TokenStore) DeleteByIssuer(sessionID, issuer string)
DeleteByIssuer removes all tokens for a given session and issuer. This is used to clear invalid/expired tokens before requesting fresh authentication.
func (*TokenStore) DeleteBySession ¶
func (ts *TokenStore) DeleteBySession(sessionID string)
DeleteBySession removes all tokens for a given session.
func (*TokenStore) Get ¶
func (ts *TokenStore) Get(key TokenKey) *pkgoauth.Token
Get retrieves a token from the store by key. Returns nil if the token doesn't exist or has expired.
func (*TokenStore) GetAllForSession ¶
func (ts *TokenStore) GetAllForSession(sessionID string) map[TokenKey]*pkgoauth.Token
GetAllForSession returns all valid tokens for a session. This is useful for listing all servers a session is authenticated with.
func (*TokenStore) GetByIssuer ¶
func (ts *TokenStore) GetByIssuer(sessionID, issuer string) *pkgoauth.Token
GetByIssuer finds a token for the given session and issuer, regardless of scope. This enables SSO when the exact scope doesn't match but the issuer does.
func (*TokenStore) GetTokenKeyByIssuer ¶
func (ts *TokenStore) GetTokenKeyByIssuer(sessionID, issuer string) *TokenKey
GetTokenKeyByIssuer finds the token key for a given session and issuer. This is useful for linking session connections to their tokens.