Documentation
¶
Overview ¶
Package auth holds the credential store and resolution ladder for remote LLM providers. It is the runtime side of package provider: provider knows *which* providers exist and how to reach them; auth knows *how to authenticate* to them — API keys stored by /login, OAuth tokens with refresh, and the fallback chain that picks a credential at call time.
The store is a single JSON file (~/.config/nib/credentials.json), written atomically with 0o600 perms under a 0o700 directory — the same discipline as chat/sessionstore.go. No SQLite, no external deps.
Index ¶
- func RegisterPostExchange(providerID string, hook PostExchangeHook)
- type Credential
- func LoginAPIKey(store *Store, def provider.Definition, apiKey string) (Credential, error)
- func LoginAPIKeyAt(store *Store, def provider.Definition, apiKey, baseURL string) (Credential, error)
- func LoginDeviceCode(ctx context.Context, store *Store, def provider.Definition, ...) (Credential, error)
- func LoginOAuth(ctx context.Context, store *Store, def provider.Definition, onURL func(string)) (Credential, error)
- type CredentialKind
- type LoginFlow
- type OAuthFlow
- type PostExchangeHook
- type Resolved
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterPostExchange ¶
func RegisterPostExchange(providerID string, hook PostExchangeHook)
RegisterPostExchange registers a post-exchange hook for a provider ID. Intended to be called from init() in adapter packages.
Types ¶
type Credential ¶
type Credential struct {
ProviderID string `json:"provider_id"`
Kind CredentialKind `json:"kind"`
// CredentialAPIKey fields.
APIKey string `json:"api_key,omitempty"`
// BaseURL overrides the provider's default endpoint, for providers that
// have none (Azure: one resource URL per account).
BaseURL string `json:"base_url,omitempty"`
// CredentialOAuth fields.
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
Email string `json:"email,omitempty"`
AccountID string `json:"account_id,omitempty"`
OrgID string `json:"org_id,omitempty"`
OrgName string `json:"org_name,omitempty"`
// ProjectID is used by Cloud Code Assist (google-gemini-cli). Discovered
// via loadCodeAssist/onboardUser after the OAuth exchange.
ProjectID string `json:"project_id,omitempty"`
AuthorizedAt time.Time `json:"authorized_at,omitempty"`
}
Credential is one stored login for one provider. The store keeps at most one credential per provider (single-account); multi-account is a Phase 3 goal. Fields not relevant to the Kind are left zero-valued and omitted from JSON.
func LoginAPIKey ¶
func LoginAPIKey(store *Store, def provider.Definition, apiKey string) (Credential, error)
LoginAPIKey creates and stores an API-key credential for a provider. Returns the credential (with AuthorizedAt set).
func LoginAPIKeyAt ¶ added in v0.9.1
func LoginAPIKeyAt(store *Store, def provider.Definition, apiKey, baseURL string) (Credential, error)
LoginAPIKeyAt is LoginAPIKey with an endpoint override stored alongside the key, for providers without a default base URL (see provider.Definition.NeedsBaseURL). An empty baseURL keeps the default.
func LoginDeviceCode ¶
func LoginDeviceCode(ctx context.Context, store *Store, def provider.Definition, onInstructions func(string, string)) (Credential, error)
LoginDeviceCode runs the RFC 8628 device-code flow and saves the credential. onInstructions is called with a human-readable instruction string (containing the user code and verification URL) before polling begins, so the caller can display it and optionally open the verification URL in a browser.
func LoginOAuth ¶
func LoginOAuth(ctx context.Context, store *Store, def provider.Definition, onURL func(string)) (Credential, error)
LoginOAuth runs the full OAuth flow (start + complete) and saves the credential. onURL is called with the authorize URL before waiting for the callback, so the caller can display it or open a browser.
func (Credential) DisplayLabel ¶
func (c Credential) DisplayLabel() string
DisplayLabel returns a human-readable label for the account picker ("user@example.com" for OAuth, "API key" for api-key creds).
func (Credential) IsExpired ¶
func (c Credential) IsExpired() bool
IsExpired reports whether the access token has fully expired (no skew). Used to decide whether to attempt a refresh vs. demand re-login.
func (Credential) NeedsRefresh ¶
func (c Credential) NeedsRefresh() bool
NeedsRefresh reports whether an OAuth credential's access token is expired or about to expire. Always false for non-OAuth credentials.
func (Credential) StatusLine ¶
func (c Credential) StatusLine() string
StatusLine returns a one-line summary of a stored credential for display in `nib login --list`. Example: "user@example.com (OAuth, expires in 3d)".
type CredentialKind ¶
type CredentialKind string
CredentialKind classifies how a credential authenticates.
const ( // CredentialAPIKey is a plain API key entered via /login or stored from // config. The key is sent as-is in the Authorization header (OpenAI) or // x-api-key header (Anthropic). CredentialAPIKey CredentialKind = "api-key" // CredentialOAuth is an OAuth access/refresh token pair obtained via the // authorization-code flow with PKCE. The access token is sent as a Bearer // token; when it expires, the refresh token is used to get a new one. CredentialOAuth CredentialKind = "oauth" )
type LoginFlow ¶
type LoginFlow struct {
ProviderID string
Prompt string // authorize URL or device-code instructions
URL string // URL to open in a browser (may differ from Prompt)
// contains filtered or unexported fields
}
LoginFlow represents an in-progress login flow. The caller displays Prompt to the user (and optionally opens URL in a browser), then calls Complete to finish the flow asynchronously.
func NewLoginFlow ¶
func NewLoginFlow(providerID, prompt, url string, complete func(context.Context) (Credential, error)) *LoginFlow
NewLoginFlow constructs a LoginFlow with the given display fields and completion function. It is intended for providers that finish the flow synchronously (e.g. Copilot token import) and need a no-op Complete.
func StartLogin ¶
StartLogin begins an OAuth-code or device-code login flow for def. The returned LoginFlow's Prompt should be displayed to the user and URL opened in a browser; then Complete should be called to finish the flow. For LoginOAuthCode and LoginDeviceCode only. For other login kinds, use LoginAPIKey or the provider-specific importer.
type OAuthFlow ¶
type OAuthFlow struct {
// contains filtered or unexported fields
}
OAuthFlow holds the state of an in-progress OAuth authorization-code flow: the PKCE verifier, the CSRF state, and the running callback server. Start with StartOAuthFlow, display AuthorizeURL to the user, then call Complete to wait for the callback and exchange the code.
func StartOAuthFlow ¶
func StartOAuthFlow(def provider.Definition) (*OAuthFlow, error)
StartOAuthFlow generates PKCE parameters, starts the loopback callback server, and returns a flow ready to display the authorize URL.
func (*OAuthFlow) AuthorizeURL ¶
AuthorizeURL returns the URL the user must open to authorize the app.
type PostExchangeHook ¶
type PostExchangeHook func(ctx context.Context, cred Credential, def provider.Definition) (Credential, error)
PostExchangeHook is called after a successful OAuth token exchange but before the credential is saved. It can enrich the credential (e.g., discover a Cloud Code Assist project ID) or validate the exchange (e.g., require a refresh token). Returning an error aborts the login.
Hooks are registered per provider ID via RegisterPostExchange, typically from an adapter package's init().
func GetPostExchange ¶
func GetPostExchange(providerID string) PostExchangeHook
GetPostExchange returns the post-exchange hook for a provider, or nil.
type Resolved ¶
type Resolved struct {
// APIKey is the credential: an API key for CredentialAPIKey creds, or an
// OAuth access token for CredentialOAuth creds. Empty if nothing resolved.
APIKey string
// IsOAuth is true when APIKey is an OAuth access token. The adapter uses
// this to pick Authorization: Bearer + anthropic-beta vs x-api-key.
IsOAuth bool
}
Resolved is the output of the credential resolution ladder: the key or token to present on the wire, and whether it is an OAuth bearer token (which changes the headers the adapter sends).
func Resolve ¶
Resolve runs the credential resolution ladder for a provider:
- Stored credential from /login (OAuth with refresh, or API key)
- configAPIKey (from types.Config.APIKey — the existing YAML/env path)
- Environment variable (def.EnvVar — e.g. ANTHROPIC_API_KEY)
- Fallback (empty Resolved)
A stored OAuth credential whose access token is about to expire is refreshed in-place and persisted back to the store before returning. If the refresh fails, the resolver falls through to configAPIKey / env rather than returning an error — a stale OAuth token is worse than a working API key.
func ResolveOrError ¶
ResolveOrError is like Resolve but returns an error when no credential is found, for call sites that require auth (no anonymous access).
type Store ¶
type Store struct {
Path string
}
Store persists credentials as a single JSON file at Path. The file is written atomically (temp + rename, same directory), with 0o600 file mode under a 0o700 directory — the same discipline as chat/sessionstore.go. Concurrent writers are not serialized by the store; nib is single-process.
func NewStore ¶
NewStore returns a store backed by path. The file is not created until the first Save; Load and Delete tolerate a missing file.
func (*Store) All ¶
func (s *Store) All() ([]Credential, error)
All returns every stored credential, sorted by providerID for stable output. Used by `nib login --list` and the /logout picker.
func (*Store) Delete ¶
Delete removes the stored credential for providerID. A missing entry is not an error — the caller (logout) wants it gone, and it already is.
func (*Store) Get ¶
func (s *Store) Get(providerID string) (Credential, bool, error)
Get returns the stored credential for providerID, or ok=false if none.
func (*Store) Save ¶
func (s *Store) Save(cred Credential) error
Save stores cred for cred.ProviderID, replacing any existing credential for that provider (single-account model). Sets AuthorizedAt to now for OAuth credentials that do not already have it.