keychain

package
v1.0.56 Latest Latest
Warning

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

Go to latest
Published: May 28, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package keychain is gro's credential adapter. Despite the historical package name, it no longer shells out to macOS `security` or Linux `secret-tool`, and no longer writes a plaintext token.json: it is a thin wrapper over cli-common's credstore, which owns OS-keyring storage, §1.4 backend selection (incl. Linux fail-closed and the encrypted-file fallback), Windows Credential Manager, and the §1.5.2 allowed-key allowlist. The name is retained to avoid churning every importer during the Phase B migration (Open CLI Collective Secret-Handling Standard §2.3).

The access secret is the per-user OAuth token: the whole oauth2.Token (AccessToken AND RefreshToken are secret) serialized as one credstore string value under the single bundle key "oauth_token". The OAuth client JSON is deployment material (§1.2) and is NOT stored here.

Index

Constants

View Source
const KeyOAuthToken = "oauth_token" //nolint:gosec // G101: a bundle key name, not a credential

KeyOAuthToken is gro's single bundle key (§1.3). The migration renames the historical keychain account "oauth_token" into this same key under the resolved credential_ref.

Variables

View Source
var ErrTokenNotFound = errors.New("no token found in secure storage")

ErrTokenNotFound indicates no token exists in the keyring (errors.Is-able wrapper of credstore.ErrNotFound). Name retained for existing callers.

Functions

func EnsureMigrated added in v1.0.48

func EnsureMigrated() error

EnsureMigrated runs (and resolves) the one-time §1.8 legacy migration up front via the full Open() path, then closes. A legacy-vs-keyring conflict surfaces as a hard error. Shared by `gro init` and `gro set-credential` so the migration guarantee lives in exactly one place.

func GetBackendFlagOverride added in v1.0.53

func GetBackendFlagOverride() (value string, flagSet bool)

GetBackendFlagOverride returns the current override. Two return values preserve the flag-set vs unset distinction credstore.BindBackendFlag depends on.

func NewPersistentTokenSource

func NewPersistentTokenSource(ctx context.Context, cfg *oauth2.Config, initial *oauth2.Token, persist TokenPersister) oauth2.TokenSource

NewPersistentTokenSource creates a TokenSource that persists refreshed tokens through persist. When the underlying oauth2 package refreshes an expired token, this wrapper detects the change and writes it back via the caller-captured persister (no long-lived credstore Store handle).

func PassphraseSource added in v1.0.48

func PassphraseSource(service string) string

PassphraseSource describes, for `config show`, where the file-backend passphrase would come from (§1.4: the user must understand their posture). Only meaningful when the file backend is in use.

func SetBackendFlagOverride added in v1.0.53

func SetBackendFlagOverride(value string, flagSet bool)

SetBackendFlagOverride records the user-supplied --backend flag for the next openWith call. Called by root.WireBackendSelection at PersistentPreRunE time. flagSet matches cobra's pflag.Flag.Changed — true when the user passed --backend on the command line, regardless of whether the value is empty.

Types

type PersistentTokenSource

type PersistentTokenSource struct {
	// contains filtered or unexported fields
}

PersistentTokenSource wraps a TokenSource and persists refreshed tokens via the injected persister. This solves the problem where oauth2's automatic token refresh does not persist the rotated token back to storage.

func (*PersistentTokenSource) Token

func (p *PersistentTokenSource) Token() (*oauth2.Token, error)

Token returns a valid token, refreshing and persisting if necessary. This method is safe for concurrent use.

type Store added in v1.0.48

type Store struct {
	// contains filtered or unexported fields
}

Store is an open handle to gro's credential bundle. Construct with one of the Open* functions, always Close. It carries the resolved ref so callers can report it in `config show` / errors without re-deriving it (the ref is not secret — §1.12).

func Open added in v1.0.48

func Open() (*Store, error)

Open resolves the authoritative credential_ref from config.yml (§1.3 — the service/profile are parsed, never assumed), opens the backing credstore, and runs the one-time legacy migration (§1.8) before returning. Used by all real API commands AND `config test` (the smoke check must surface migration/conflicts exactly as a real command would). A legacy-vs-keyring conflict surfaces here as a §1.8 error.

func OpenForMigrationOverwrite added in v1.0.48

func OpenForMigrationOverwrite() (*Store, error)

OpenForMigrationOverwrite is Open with the §1.8 `--overwrite` resolution: a legacy value is forced over an existing keyring entry. It still cannot resolve a legacy-vs-legacy disagreement (the user must pick).

func OpenNoMigrate added in v1.0.48

func OpenNoMigrate() (*Store, error)

OpenNoMigrate opens WITHOUT the one-time migration. Reserved for the diagnostic/remediation paths (`config show`, `config clear`): if migration ran first it would return a §1.8 conflict before the user could inspect or clear the conflicting entry, leaving no way out.

func OpenRef added in v1.0.48

func OpenRef(ref string) (*Store, error)

OpenRef opens a store against an explicit ref instead of config.yml's credential_ref — used by `gro set-credential --ref` and the refresh persister. An empty ref falls back to the configured/default ref. Migration does NOT run here: the one-time §1.8 migration only ever targets the canonical configured ref (running it against an arbitrary --ref would discover the default ref's legacy data and could write it under the wrong service/profile).

func (*Store) Backend added in v1.0.48

func (s *Store) Backend() (credstore.Backend, credstore.Source)

Backend reports the credstore backend and how it was selected, for `config show` (§1.6). Neither value is secret.

func (*Store) Close added in v1.0.48

func (s *Store) Close() error

Close releases the backing store. Safe on a nil receiver.

func (*Store) DeleteToken added in v1.0.48

func (s *Store) DeleteToken() error

DeleteToken removes the token (idempotent: an absent key is not an error — §1.7). The Exists pre-check is backend-agnostic: credstore's file backend surfaces a raw os "not found" rather than ErrNotFound on Delete. A genuine Exists failure (e.g. keyring temporarily inaccessible) is surfaced, not swallowed — otherwise a non-deletion would be reported as success.

func (*Store) HasToken added in v1.0.48

func (s *Store) HasToken() (bool, error)

HasToken reports presence without returning the value (`config show`, `init` overwrite check — §1.6). A genuine Exists failure (keyring temporarily inaccessible) is surfaced, not folded into "absent": callers that gate re-auth/overwrite on this must not mistake an error for "no token" and clobber a token that is actually present.

func (*Store) Ref added in v1.0.48

func (s *Store) Ref() string

Ref returns the resolved credential ref (non-secret; safe to display).

func (*Store) Service added in v1.0.48

func (s *Store) Service() string

Service returns the resolved service segment (non-secret; used for the §1.4 passphrase-source label).

func (*Store) SetToken added in v1.0.48

func (s *Store) SetToken(tok *oauth2.Token) error

SetToken stores the OAuth token. Ingress-only at init/set-credential, plus the single sanctioned non-ingress write: runtime token refresh persisting the rotated token back under the active ref (standard §ix / line 174).

func (*Store) Token added in v1.0.48

func (s *Store) Token() (*oauth2.Token, error)

Token returns the OAuth token from the keyring. ErrTokenNotFound (an errors.Is-matchable wrapper of credstore.ErrNotFound) when unset.

type TokenPersister added in v1.0.48

type TokenPersister func(*oauth2.Token) error

TokenPersister persists a refreshed OAuth token. It is supplied by the caller (auth.GetHTTPClient) bound to the ref captured at construction time, so the only sanctioned non-ingress keyring write (runtime token refresh, standard §ix / line 174) updates the existing oauth_token key under the active ref — never a new key, never a different ref.

Jump to

Keyboard shortcuts

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