Documentation
¶
Overview ¶
Package oauth provides OAuth 2.0 authentication for LLM providers. It supports PKCE flow (Google Gemini, ChatGPT) and Device Code flow (Qwen, MiniMax).
Index ¶
- func FindAvailablePort(startPort int) (int, error)
- func VerifyChallenge(verifier, expectedChallenge string) bool
- type CallbackResult
- type CallbackServer
- func (s *CallbackServer) CallbackURL() string
- func (s *CallbackServer) Close() error
- func (s *CallbackServer) Port() int
- func (s *CallbackServer) Start() error
- func (s *CallbackServer) WaitForCallback() (*CallbackResult, error)
- func (s *CallbackServer) WaitForCallbackWithContext(ctx context.Context) (*CallbackResult, error)
- type Config
- type DeviceCodeError
- type DeviceCodeFlow
- func (d *DeviceCodeFlow) ExchangePKCE(ctx context.Context, code, verifier, redirectURI string) (*OAuthCredential, error)
- func (d *DeviceCodeFlow) Poll(ctx context.Context, deviceCode string, interval time.Duration) (*OAuthCredential, error)
- func (d *DeviceCodeFlow) RefreshToken(ctx context.Context, refreshToken string) (*OAuthCredential, error)
- func (d *DeviceCodeFlow) Start(ctx context.Context) (*DeviceCodeResponse, error)
- type DeviceCodeProvider
- type DeviceCodeResponse
- type HubAdapter
- type HubAdapterConfig
- type LoginResult
- type OAuthCredential
- type OAuthFlowType
- type OAuthProvider
- type PKCEPair
- type ProviderConfig
- type TokenManager
- func (tm *TokenManager) DeleteCredential(provider string) error
- func (tm *TokenManager) GetCredential(provider string) (*OAuthCredential, error)
- func (tm *TokenManager) GetStatus() map[string]TokenStatus
- func (tm *TokenManager) GetValidToken(provider string) (*OAuthCredential, error)
- func (tm *TokenManager) GetValidTokenInterface(provider string) (interface{}, error)
- func (tm *TokenManager) ListProviders() []string
- func (tm *TokenManager) NeedsLogin(provider string) bool
- func (tm *TokenManager) Refresh(provider string) (*OAuthCredential, error)
- func (tm *TokenManager) RegisterProvider(provider OAuthProvider)
- func (tm *TokenManager) SaveCredential(cred *OAuthCredential) error
- func (tm *TokenManager) StartAutoRefresh()
- func (tm *TokenManager) Stop()
- type TokenResponse
- type TokenStatus
- type TokenStore
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FindAvailablePort ¶
FindAvailablePort finds an available port on localhost.
func VerifyChallenge ¶
VerifyChallenge verifies that a verifier produces the expected challenge. This is useful for testing and validation.
Types ¶
type CallbackResult ¶
type CallbackResult struct {
// Code is the authorization code (on success)
Code string
// State is the state parameter from the callback
State string
// Error contains the error message (on failure)
Error string
// ErrorDescription provides more details about the error
ErrorDescription string
}
CallbackResult contains the result of an OAuth callback.
type CallbackServer ¶
type CallbackServer struct {
// contains filtered or unexported fields
}
CallbackServer handles OAuth callbacks on a local HTTP server.
func NewCallbackServer ¶
func NewCallbackServer(port int, state string, timeout time.Duration, logger *slog.Logger) *CallbackServer
NewCallbackServer creates a new callback server.
func (*CallbackServer) CallbackURL ¶
func (s *CallbackServer) CallbackURL() string
CallbackURL returns the callback URL for this server.
func (*CallbackServer) Close ¶
func (s *CallbackServer) Close() error
Close stops the callback server.
func (*CallbackServer) Port ¶
func (s *CallbackServer) Port() int
Port returns the port the server is listening on.
func (*CallbackServer) Start ¶
func (s *CallbackServer) Start() error
Start starts the callback server.
func (*CallbackServer) WaitForCallback ¶
func (s *CallbackServer) WaitForCallback() (*CallbackResult, error)
WaitForCallback waits for the OAuth callback or timeout.
func (*CallbackServer) WaitForCallbackWithContext ¶
func (s *CallbackServer) WaitForCallbackWithContext(ctx context.Context) (*CallbackResult, error)
WaitForCallbackWithContext waits for the OAuth callback with context support.
type Config ¶
type Config struct {
// Providers maps provider names to their configurations
Providers map[string]ProviderConfig `yaml:"providers" json:"providers"`
}
Config contains OAuth configuration for all providers.
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns the default OAuth configuration.
type DeviceCodeError ¶
type DeviceCodeError struct {
Code string // "authorization_pending", "slow_down", etc.
ErrorDescription string
}
DeviceCodeError represents an error from the device code flow.
func (*DeviceCodeError) Error ¶
func (e *DeviceCodeError) Error() string
func (*DeviceCodeError) IsPending ¶
func (e *DeviceCodeError) IsPending() bool
IsPending returns true if the error indicates authorization is still pending.
type DeviceCodeFlow ¶
type DeviceCodeFlow struct {
// DeviceCodeURL is the endpoint to request a device code
DeviceCodeURL string
// TokenURL is the endpoint to exchange device code for tokens
TokenURL string
// ClientID is the OAuth client ID
ClientID string
// Scopes are the OAuth scopes to request
Scopes []string
// contains filtered or unexported fields
}
DeviceCodeFlow handles OAuth device code flow for providers like Qwen and MiniMax.
func NewDeviceCodeFlow ¶
func NewDeviceCodeFlow(deviceCodeURL, tokenURL, clientID string, scopes []string, logger *slog.Logger) *DeviceCodeFlow
NewDeviceCodeFlow creates a new device code flow handler.
func (*DeviceCodeFlow) ExchangePKCE ¶
func (d *DeviceCodeFlow) ExchangePKCE(ctx context.Context, code, verifier, redirectURI string) (*OAuthCredential, error)
ExchangePKCE exchanges an authorization code for tokens using PKCE. This is used by providers that support PKCE flow (like Qwen Portal).
func (*DeviceCodeFlow) Poll ¶
func (d *DeviceCodeFlow) Poll(ctx context.Context, deviceCode string, interval time.Duration) (*OAuthCredential, error)
Poll polls the token endpoint until the user completes verification. It respects the polling interval and handles the "authorization_pending" response.
func (*DeviceCodeFlow) RefreshToken ¶
func (d *DeviceCodeFlow) RefreshToken(ctx context.Context, refreshToken string) (*OAuthCredential, error)
RefreshToken refreshes an access token using a refresh token.
func (*DeviceCodeFlow) Start ¶
func (d *DeviceCodeFlow) Start(ctx context.Context) (*DeviceCodeResponse, error)
Start initiates the device code flow and returns the device code response.
type DeviceCodeProvider ¶
type DeviceCodeProvider interface {
OAuthProvider
// StartDeviceFlow initiates a device code flow
StartDeviceFlow(ctx context.Context) (*DeviceCodeResponse, error)
// PollForToken polls the token endpoint until the user completes verification
PollForToken(ctx context.Context, deviceCode string, interval time.Duration) (*OAuthCredential, error)
}
DeviceCodeProvider extends OAuthProvider with device code flow methods.
type DeviceCodeResponse ¶
type DeviceCodeResponse struct {
// DeviceCode is used to poll for the token
DeviceCode string `json:"device_code"`
// UserCode is displayed to the user for verification
UserCode string `json:"user_code"`
// VerificationURI is the URL the user visits to enter the code
VerificationURI string `json:"verification_uri"`
// VerificationURIComplete includes the user code in the URL (optional)
VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
// ExpiresIn is the time in seconds until the device code expires
ExpiresIn int `json:"expires_in"`
// Interval is the polling interval in seconds
Interval int `json:"interval"`
}
DeviceCodeResponse represents the response from a device code authorization request.
type HubAdapter ¶ added in v1.13.0
type HubAdapter struct {
// contains filtered or unexported fields
}
HubAdapter implements OAuthManager by delegating to an OAuth Hub instance. It maps DevClaw provider names to Hub connection IDs.
func NewHubAdapter ¶ added in v1.13.0
func NewHubAdapter(cfg HubAdapterConfig) (*HubAdapter, error)
NewHubAdapter creates a new OAuth Hub adapter.
func (*HubAdapter) GetValidToken ¶ added in v1.13.0
func (h *HubAdapter) GetValidToken(provider string) (*OAuthCredential, error)
GetValidToken returns a valid access token for the given provider. It calls the Hub's /api/v1/token/get endpoint.
func (*HubAdapter) SaveCredential ¶ added in v1.13.0
func (h *HubAdapter) SaveCredential(_ *OAuthCredential) error
SaveCredential is a no-op for Hub mode — the Hub manages token storage.
type HubAdapterConfig ¶ added in v1.13.0
type HubAdapterConfig struct {
HubURL string
APIKey string
APIKeyEnvVar string
Logger *slog.Logger
}
HubAdapterConfig configures the Hub adapter.
type LoginResult ¶
type LoginResult struct {
// Credential is the OAuth credential (on success)
Credential *OAuthCredential
// Provider is the provider name
Provider string
// Error contains any error that occurred
Error error
// Warning contains non-fatal warnings (e.g., experimental feature)
Warning string
}
LoginResult contains the result of an OAuth login attempt.
type OAuthCredential ¶
type OAuthCredential struct {
// Provider identifies which OAuth provider this credential belongs to
// (e.g., "gemini", "chatgpt", "qwen", "minimax")
Provider string `json:"provider"`
// AccessToken is the OAuth access token used for API requests
AccessToken string `json:"access_token"`
// RefreshToken is used to obtain new access tokens
RefreshToken string `json:"refresh_token"`
// ExpiresAt is when the access token expires
ExpiresAt time.Time `json:"expires_at"`
// Email is the user's email associated with the OAuth account
Email string `json:"email,omitempty"`
// ClientID is the OAuth client ID used for this credential
ClientID string `json:"client_id,omitempty"`
// Metadata contains provider-specific data (e.g., project IDs, regions)
Metadata map[string]string `json:"metadata,omitempty"`
}
OAuthCredential represents stored OAuth tokens for a provider.
func (*OAuthCredential) GetAccessToken ¶
func (c *OAuthCredential) GetAccessToken() string
GetAccessToken returns the access token (implements interface for LLMClient).
func (*OAuthCredential) IsExpired ¶
func (c *OAuthCredential) IsExpired(buffer time.Duration) bool
IsExpired returns true if the credential has expired or will expire within the buffer.
func (*OAuthCredential) IsValid ¶
func (c *OAuthCredential) IsValid() bool
IsValid returns true if the credential has a valid access token that hasn't expired.
type OAuthFlowType ¶
type OAuthFlowType string
OAuthFlowType indicates the type of OAuth flow to use.
const ( // FlowPKCE indicates a PKCE-based OAuth flow with local callback FlowPKCE OAuthFlowType = "pkce" // FlowDeviceCode indicates a device code flow for CLI/headless environments FlowDeviceCode OAuthFlowType = "device_code" )
type OAuthProvider ¶
type OAuthProvider interface {
// Name returns the provider identifier (e.g., "gemini", "chatgpt")
Name() string
// Label returns a human-readable provider name
Label() string
// AuthURL returns the authorization URL for the OAuth flow
// state is a random string for CSRF protection
// challenge is the PKCE code challenge (for PKCE flows)
AuthURL(state, challenge string) string
// TokenURL returns the token exchange endpoint
TokenURL() string
// ExchangeCode exchanges an authorization code for tokens
// code is the authorization code from the callback
// verifier is the PKCE code verifier
ExchangeCode(ctx context.Context, code, verifier string) (*OAuthCredential, error)
// RefreshToken obtains a new access token using a refresh token
RefreshToken(ctx context.Context, refreshToken string) (*OAuthCredential, error)
// Scopes returns the OAuth scopes required for this provider
Scopes() []string
// SupportsPKCE returns true if the provider supports PKCE flow
SupportsPKCE() bool
// SupportsDeviceCode returns true if the provider supports device code flow
SupportsDeviceCode() bool
}
OAuthProvider defines the interface for OAuth providers.
type PKCEPair ¶
type PKCEPair struct {
// Verifier is the random string used to verify the OAuth flow
Verifier string `json:"verifier"`
// Challenge is the SHA256 hash of the verifier, base64url encoded
Challenge string `json:"challenge"`
}
PKCEPair contains the PKCE verifier and challenge for OAuth flows.
func GeneratePKCE ¶
GeneratePKCE creates a new PKCE verifier and challenge pair. The verifier is a cryptographically random string. The challenge is the SHA256 hash of the verifier, base64url encoded.
type ProviderConfig ¶
type ProviderConfig struct {
// Enabled indicates if this provider is available
Enabled bool `yaml:"enabled" json:"enabled"`
// ClientID is the OAuth client ID (optional, some providers have defaults)
ClientID string `yaml:"client_id,omitempty" json:"client_id,omitempty"`
// ClientSecret is the OAuth client secret (optional, rarely needed)
ClientSecret string `yaml:"client_secret,omitempty" json:"client_secret,omitempty"`
// RedirectPort is the local port for OAuth callbacks (PKCE flow)
RedirectPort int `yaml:"redirect_port,omitempty" json:"redirect_port,omitempty"`
// Region is the provider region (e.g., "global" or "cn" for MiniMax)
Region string `yaml:"region,omitempty" json:"region,omitempty"`
}
ProviderConfig contains configuration for an OAuth provider.
type TokenManager ¶
type TokenManager struct {
// contains filtered or unexported fields
}
TokenManager manages OAuth tokens with automatic refresh.
func NewTokenManager ¶
func NewTokenManager(dataDir string, logger *slog.Logger) (*TokenManager, error)
NewTokenManager creates a new token manager.
func (*TokenManager) DeleteCredential ¶
func (tm *TokenManager) DeleteCredential(provider string) error
DeleteCredential removes a credential for a provider.
func (*TokenManager) GetCredential ¶
func (tm *TokenManager) GetCredential(provider string) (*OAuthCredential, error)
GetCredential returns the credential for a provider.
func (*TokenManager) GetStatus ¶
func (tm *TokenManager) GetStatus() map[string]TokenStatus
GetStatus returns the status of all provider tokens.
func (*TokenManager) GetValidToken ¶
func (tm *TokenManager) GetValidToken(provider string) (*OAuthCredential, error)
GetValidToken returns a valid access token, refreshing if necessary.
func (*TokenManager) GetValidTokenInterface ¶
func (tm *TokenManager) GetValidTokenInterface(provider string) (interface{}, error)
GetValidTokenInterface returns a valid token as interface{} (for LLMClient compatibility).
func (*TokenManager) ListProviders ¶
func (tm *TokenManager) ListProviders() []string
ListProviders returns the list of providers with credentials.
func (*TokenManager) NeedsLogin ¶
func (tm *TokenManager) NeedsLogin(provider string) bool
NeedsLogin returns true if a provider needs to log in (no valid credential).
func (*TokenManager) Refresh ¶
func (tm *TokenManager) Refresh(provider string) (*OAuthCredential, error)
Refresh forces a refresh of a provider's token.
func (*TokenManager) RegisterProvider ¶
func (tm *TokenManager) RegisterProvider(provider OAuthProvider)
RegisterProvider registers an OAuth provider for token refresh.
func (*TokenManager) SaveCredential ¶
func (tm *TokenManager) SaveCredential(cred *OAuthCredential) error
SaveCredential saves a credential for a provider.
func (*TokenManager) StartAutoRefresh ¶
func (tm *TokenManager) StartAutoRefresh()
StartAutoRefresh starts the background token refresh goroutine.
func (*TokenManager) Stop ¶
func (tm *TokenManager) Stop()
Stop stops the background refresh goroutine.
type TokenResponse ¶
type TokenResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
Scope string `json:"scope,omitempty"`
IDToken string `json:"id_token,omitempty"`
}
TokenResponse represents the response from a token exchange request.
type TokenStatus ¶
type TokenStatus struct {
Provider string `json:"provider"`
Status string `json:"status"` // valid, expiring_soon, expired, unknown
Email string `json:"email,omitempty"`
ExpiresIn time.Duration `json:"expires_in,omitempty"`
HasToken bool `json:"has_token"`
}
TokenStatus represents the status of a provider's token.
type TokenStore ¶
type TokenStore struct {
Version int `json:"version"`
Providers map[string]*OAuthCredential `json:"providers"`
}
TokenStore is the on-disk format for OAuth tokens.