keychain

package
v1.1.67 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 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 CredentialRefEnvVar added in v1.1.67

func CredentialRefEnvVar() string

CredentialRefEnvVar is the per-invocation credential-ref override env var (e.g. "GOOGLE_READONLY_CREDENTIAL_REF"). It is derived from gro's service so it always tracks the same <SERVICE>_ prefix credstore uses for the backend env var, and is never hard-coded (§1.3). Exported so the cobra layer can name it in the --ref flag's help text.

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 GetCredentialRefOverride added in v1.1.67

func GetCredentialRefOverride() (value string, flagSet bool)

GetCredentialRefOverride returns the current --ref override and whether it was set. The flag-set vs unset distinction lets an explicit empty value be told apart from "no flag".

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.

func SetCredentialRefOverride added in v1.1.67

func SetCredentialRefOverride(value string, flagSet bool)

SetCredentialRefOverride records the user-supplied --ref flag for the next keychain.Open* call. Called by root.WireCredentialRefSelection at PersistentPreRunE time. Mirrors SetBackendFlagOverride: a persistent flag can't be threaded through the parameterless keychain.Open() the read commands call, so it is recorded here and read back at the single resolution site (open). flagSet matches cobra's pflag.Flag.Changed — true when the user passed --ref on the command line, regardless of value.

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