auth

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2026 License: AGPL-3.0 Imports: 23 Imported by: 0

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

View Source
const (
	OpenAICodexBrowserLoginMethod    = "browser"
	OpenAICodexDeviceCodeLoginMethod = "device_code"
)

Login method ids for the OpenAI Codex provider.

Variables

View Source
var ErrKeychainUnavailable = errors.New("OS keychain unavailable: vix stores credentials only in the OS keychain. " +
	"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.

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

func CodexAccountID(accessToken string) string

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

func SetLogger(l *slog.Logger)

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 AuthInfo

type AuthInfo struct {
	URL          string
	Instructions string
}

AuthInfo is passed to the OnAuth callback when a browser-based flow starts.

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

func (b *MemoryBackend) Get(key string) (string, bool, error)

func (*MemoryBackend) Set

func (b *MemoryBackend) Set(key, value string) error

type Prompt

type Prompt struct {
	Message     string
	Placeholder string
	AllowEmpty  bool
}

Prompt asks the user for a single line of free-form input.

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

func GetProvider(id string) (Provider, bool)

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

type SelectOption struct {
	ID    string
	Label string
}

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

func NewStorage(b Backend) *Storage

NewStorage constructs a Storage over the given backend.

func (*Storage) AccessToken

func (s *Storage) AccessToken(provider string) (token string, expired bool, ok bool)

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

func (s *Storage) AccessTokenRefreshing(ctx context.Context, provider string) (string, error)

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

func (s *Storage) HasLogin(provider string) bool

HasLogin reports whether an OAuth login is stored for a provider.

func (*Storage) List

func (s *Storage) List() []string

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

func (s *Storage) Login(ctx context.Context, providerID string, cb LoginCallbacks) error

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.

func (*Storage) Logout

func (s *Storage) Logout(provider string) error

Logout removes a provider's stored credentials.

func (*Storage) Remove

func (s *Storage) Remove(provider string) error

Remove deletes any stored credentials for a provider.

func (*Storage) Set

func (s *Storage) Set(provider string, creds Credentials) error

Set persists credentials for a provider. It fails with ErrKeychainUnavailable when the keychain cannot be reached rather than writing secrets elsewhere.

Jump to

Keyboard shortcuts

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