Documentation
¶
Overview ¶
Package auth implements the OAuth login + credential system for vix's subscription-based providers — Anthropic (Claude Pro/Max) and OpenAI Codex (ChatGPT) — plus OpenRouter's key-minting PKCE flow, and the shared machinery they rely on: PKCE generation, RFC 8628 device-code polling, a local callback HTTP server, a provider registry, and credential storage with automatic token refresh.
Credentials are stored exclusively in the OS keychain (see storage.go). When no keychain is reachable, login and refresh fail loudly rather than writing secrets to disk.
Index ¶
- Constants
- Variables
- func AuthLogPath() string
- func CodexAccountID(accessToken string) string
- func KeychainAvailable() bool
- func RegisterProvider(p Provider)
- func ResetProviders()
- func SetLogger(l *slog.Logger)
- func UnregisterProvider(id string)
- type AuthInfo
- type Backend
- type Credentials
- type DeviceCodeInfo
- type LoginCallbacks
- type MemoryBackend
- type Prompt
- type Provider
- type SelectOption
- type SelectPrompt
- type Storage
- func (s *Storage) AccessToken(provider string) (token string, expired bool, ok bool)
- func (s *Storage) AccessTokenRefreshing(ctx context.Context, provider string) (string, error)
- func (s *Storage) Get(provider string) (Credentials, bool, error)
- func (s *Storage) HasLogin(provider string) bool
- func (s *Storage) List() []string
- func (s *Storage) Login(ctx context.Context, providerID string, cb LoginCallbacks) error
- func (s *Storage) Logout(provider string) error
- func (s *Storage) Remove(provider string) error
- func (s *Storage) Set(provider string, creds Credentials) error
Constants ¶
const ( OpenAICodexBrowserLoginMethod = "browser" OpenAICodexDeviceCodeLoginMethod = "device_code" )
Login method ids for the OpenAI Codex provider.
Variables ¶
"On headless Linux/WSL/containers, run vix with a working keychain (e.g. gnome-keyring + D-Bus) " +
"or authenticate via an API key env var instead of an interactive login")
ErrKeychainUnavailable is returned when an operation that must persist a secret cannot reach the OS keychain. vix deliberately has no plaintext file fallback: secrets live in the keychain or not at all.
var ErrNoCredentials = errors.New("no OAuth credentials stored")
ErrNoCredentials is returned when no OAuth login is stored for a provider.
Functions ¶
func AuthLogPath ¶
func AuthLogPath() string
AuthLogPath returns the path of the default auth log file, honouring VIX_AUTH_LOG, or "" if the home directory cannot be determined.
func CodexAccountID ¶
CodexAccountID extracts the chatgpt_account_id claim from a Codex OAuth access token (a JWT), or "" if absent. Inference adapters send it as the chatgpt-account-id header.
func KeychainAvailable ¶
func KeychainAvailable() bool
KeychainAvailable is the exported form of keyringAvailable, for callers (e.g. the UI) that want to warn before offering an interactive login.
func RegisterProvider ¶
func RegisterProvider(p Provider)
RegisterProvider registers (or replaces) a custom OAuth provider.
func ResetProviders ¶
func ResetProviders()
ResetProviders restores the registry to the built-in providers only.
func SetLogger ¶
SetLogger overrides the logger used by the auth package. Pass nil to restore the default file logger. Used by the daemon to merge auth logs into its own stream, and by tests to capture or discard output.
func UnregisterProvider ¶
func UnregisterProvider(id string)
UnregisterProvider removes a custom provider, or restores the built-in implementation when id names a built-in.
Types ¶
type Backend ¶
type Backend interface {
Get(key string) (value string, ok bool, err error)
Set(key, value string) error
Delete(key string) error
}
Backend abstracts where OAuth credentials are persisted. The production backend is the OS keychain; tests use an in-memory backend.
type Credentials ¶
type Credentials struct {
// Access is the token used to authenticate API requests.
Access string
// Refresh is the long-lived token used to obtain a new Access token.
Refresh string
// Expires is the absolute access-token expiry in Unix milliseconds. A
// refresh is due once nowMillis() >= Expires.
Expires int64
// Extra carries additional provider-specific fields that must survive a
// storage round-trip. Keys are stored at the top level of the JSON object.
Extra map[string]any
}
Credentials holds the stored credential material for a provider.
It is a flat JSON object with the well-known access/refresh/expires fields plus arbitrary provider-specific extras (e.g. accountId for OpenAI Codex). The extras round-trip through storage via custom JSON (un)marshalling so the stored object stays flat.
func (Credentials) Expired ¶
func (c Credentials) Expired() bool
Expired reports whether the access token is at or past its expiry. Tokens with no expiry (Expires == 0) are treated as non-expiring.
func (Credentials) MarshalJSON ¶
func (c Credentials) MarshalJSON() ([]byte, error)
MarshalJSON flattens Extra into the same object as access/refresh/expires so the keychain representation stays a single flat object.
func (Credentials) StringExtra ¶
func (c Credentials) StringExtra(key string) string
StringExtra returns the string value of an extra field, or "" if absent or not a string.
func (*Credentials) UnmarshalJSON ¶
func (c *Credentials) UnmarshalJSON(data []byte) error
UnmarshalJSON reads the flat credential object, pulling the well-known fields out and keeping everything else in Extra.
type DeviceCodeInfo ¶
type DeviceCodeInfo struct {
UserCode string
VerificationURI string
IntervalSeconds int
ExpiresInSeconds int
}
DeviceCodeInfo is passed to the OnDeviceCode callback for device-code flows.
type LoginCallbacks ¶
type LoginCallbacks struct {
OnAuth func(AuthInfo)
OnDeviceCode func(DeviceCodeInfo)
OnPrompt func(Prompt) (string, error)
OnProgress func(string)
OnManualCodeInput func() (string, error)
OnSelect func(SelectPrompt) (string, error)
}
LoginCallbacks is the set of UI hooks a login flow drives. Optional hooks may be nil; the flows degrade gracefully (e.g. falling back to OnPrompt when OnManualCodeInput is nil). Cancellation is carried by the context passed to Login.
type MemoryBackend ¶
type MemoryBackend struct {
// contains filtered or unexported fields
}
MemoryBackend is an in-memory Backend for tests.
func NewMemoryBackend ¶
func NewMemoryBackend() *MemoryBackend
NewMemoryBackend constructs an empty in-memory backend.
func (*MemoryBackend) Delete ¶
func (b *MemoryBackend) Delete(key string) error
func (*MemoryBackend) Set ¶
func (b *MemoryBackend) Set(key, value string) error
type Provider ¶
type Provider interface {
// ID is the stable identifier used as the storage key and registry key.
ID() string
// Name is a human-readable label.
Name() string
// UsesCallbackServer reports whether Login spins up a local HTTP server to
// receive the OAuth redirect (and therefore supports manual code paste).
UsesCallbackServer() bool
// Login runs the interactive flow and returns credentials to persist.
Login(ctx context.Context, cb LoginCallbacks) (Credentials, error)
// RefreshToken exchanges a refresh token for fresh credentials.
RefreshToken(ctx context.Context, creds Credentials) (Credentials, error)
// APIKey returns the request credential (access token) for the provider.
APIKey(creds Credentials) string
}
Provider is the interface every OAuth provider implements.
func GetProvider ¶
GetProvider returns the registered provider for id.
func GetProviders ¶
func GetProviders() []Provider
GetProviders returns all registered providers in a stable order (built-ins first, then any custom providers in registration order).
type SelectOption ¶
SelectOption is a single choice in a SelectPrompt.
type SelectPrompt ¶
type SelectPrompt struct {
Message string
Options []SelectOption
}
SelectPrompt asks the user to pick one of several options.
type Storage ¶
type Storage struct {
// contains filtered or unexported fields
}
Storage manages OAuth credentials with automatic refresh-on-expiry, backed exclusively by the OS keychain.
func DefaultStorage ¶
func DefaultStorage() *Storage
DefaultStorage returns the process-wide Storage, always backed by the OS keychain. There is no file fallback: operations that must persist a secret fail with ErrKeychainUnavailable when the keychain is unreachable.
func NewStorage ¶
NewStorage constructs a Storage over the given backend.
func (*Storage) AccessToken ¶
AccessToken returns the stored access token for a provider without refreshing. expired reports whether it is past expiry; ok is false when no login is stored.
func (*Storage) AccessTokenRefreshing ¶
AccessTokenRefreshing returns a valid access token for a provider, refreshing and persisting the credentials first if they have expired. Refreshes are serialized within the process and re-checked after acquiring the lock so a concurrent refresh is not duplicated.
func (*Storage) Get ¶
func (s *Storage) Get(provider string) (Credentials, bool, error)
Get returns the stored credentials for a provider.
func (*Storage) List ¶
List returns the registered provider ids that currently have stored credentials. (The OS keychain cannot be enumerated, so this checks each known provider.)
func (*Storage) Login ¶
Login runs the provider's interactive login flow and persists the resulting credentials. It checks keychain reachability up front so the user is not asked to authenticate in the browser only to have storage fail afterwards.