Skip to content

🎯 Fail with descriptive error instead of silently sending unauthenticated API requests when keyring access fails #1

Description

@condoraltidoi32

📝 Description

When executing gh api commands, cli attempts to resolve authentication tokens for the target host via the configured credential store (such as the system keyring, environment variables, or config files). If credential retrieval from the system keyring fails due to an operational or system error (e.g., locked keychain, missing D-Bus session, OS permission denied, corrupted secret store), the error is swallowed and treated as if no credentials exist.

Consequently, gh api silently proceeds with an unauthenticated HTTP request. This results in misleading downstream API errors—such as HTTP 404 Not Found for private resources or HTTP 403 API rate limit exceeded—masking the underlying keyring issue and complicating debugging.

🎯 Acceptance Criteria

  • Credential resolution distinguishes between a missing token (ErrNotFound) and an operational keyring failure (e.g., D-Bus disconnected, keychain locked, access denied).
  • If keyring access returns an operational failure during token resolution for gh api, the command must abort immediately and return a non-zero exit code.
  • The CLI outputs a clear, actionable error message to stderr explaining that keyring access failed, providing the underlying OS/keyring error message and troubleshooting advice (e.g., verifying keyring status or setting GH_TOKEN).
  • Fallback to unauthenticated requests only occurs when no authentication is configured or when unauthenticated access is explicitly requested (if applicable).
  • Existing fallback behavior across multiple credential providers (e.g., GH_TOKEN / GITHUB_TOKEN environment variables -> keyring -> hosts config file) remains intact when a provider is simply not set/found.

🛠️ Technical Specifications & Context

  • Repository / Tech Stack: Go (condoraltidoi32/cli).
  • Relevant Packages:
    • pkg/cmd/api/api.go: API command execution logic and HTTP client instantiation.
    • pkg/cmd/factory/: Factory methods constructing the authenticated http.Client and HttpClientFunc.
    • internal/config/: Configuration reader and auth token retrieval mechanisms (AuthToken, Authentication).
    • pkg/authtoken/ / Keyring implementation (e.g., wrapper around zalando/go-keyring or platform secret services).
  • Implementation Strategy:
    1. Inspect the keyring token retrieval function (e.g., TokenFromKeyring or Keyring.Get). Ensure it returns wrapped errors conforming to errors.Is(err, keyring.ErrNotFound) when a secret is absent vs when the keyring daemon fails.
    2. In the token resolution pipeline (e.g., AuthTokenForHost / AuthToken), do not discard operational errors from the keyring. If an error other than "not found" is encountered, halt execution and propagate the error.
    3. Ensure pkg/cmd/api handles this propagated error by surfacing it before constructing or executing the HTTP request.

🧪 Verification & Testing

  • Unit Tests:
    • Add unit tests in the auth/config package verifying that ErrNotFound proceeds through the resolution chain, while generic/operational errors (errors.New("dbus: connection closed"), errSecAuthFailed) halt execution and return the error.
    • Add tests for gh api verifying that an operational keyring error terminates the command with the proper exit code and error output.
  • Manual Verification:
    1. Configure gh to store credentials in the system keyring.
    2. Simulate a keyring failure (e.g., in Linux, run unset DBUS_SESSION_BUS_ADDRESS; gh api user or lock the login keychain on macOS).
    3. Verify that gh api repos/<private-repo> outputs a keyring error and halts instead of performing an unauthenticated request that returns HTTP 404.
    4. Verify that normal authentication still functions properly when the keyring is accessible or when GH_TOKEN is supplied.

BountyHub Bounty

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions