From 0e2750dfd9a22079a8d4f88d0f33b3c7b63e6337 Mon Sep 17 00:00:00 2001 From: Ahmed Abushagur Date: Thu, 19 Feb 2026 16:30:44 -0800 Subject: [PATCH] fix: persist gh auth credentials for interactive sessions (#1491) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: persist gh auth credentials to disk for interactive sessions When GITHUB_TOKEN is in the environment, gh auth status returns success (gh checks env vars first), so ensure_gh_auth() short-circuits before gh auth login --with-token writes credentials to ~/.config/gh/hosts.yml. The interactive session starts without GITHUB_TOKEN in env, so gh reports "not logged into any GitHub hosts". Fix: always run gh auth login --with-token when GITHUB_TOKEN is set, persisting credentials to disk regardless of gh auth status. Co-Authored-By: Claude Opus 4.6 * fix: unset GITHUB_TOKEN env var before gh auth login --with-token gh refuses to store credentials when GITHUB_TOKEN is already set in the environment: "The value of the GITHUB_TOKEN environment variable is being used for authentication." Save the value, unset the env var, pipe it to gh auth login, then re-export. Co-Authored-By: Claude Opus 4.6 * fix: address security review — validate token format, skip if already persisted - Add GITHUB_TOKEN format validation (ghp_, gho_, ghu_, ghs_, ghr_, github_pat_) - Add fast path: check gh auth status with env var unset before persisting - Document plaintext credential store behavior (standard gh CLI behavior) Agent: pr-maintainer Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: B <6723574+louisgv@users.noreply.github.com> --- shared/github-auth.sh | 45 +++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/shared/github-auth.sh b/shared/github-auth.sh index 8f9e3c78..ad3c5637 100755 --- a/shared/github-auth.sh +++ b/shared/github-auth.sh @@ -213,20 +213,45 @@ _install_gh_binary() { # ============================================================ ensure_gh_auth() { - if gh auth status &>/dev/null; then - log_info "Authenticated with GitHub CLI" - return 0 - fi - - log_step "Not authenticated with GitHub CLI" - - # Non-interactive: use GITHUB_TOKEN if set + # When GITHUB_TOKEN is set, persist it to gh's credential store so it + # survives into the interactive session (where the env var is absent). + # NOTE: This writes the token to ~/.config/gh/hosts.yml in plaintext, + # which is standard gh CLI behavior (same as `gh auth login`). if [[ -n "${GITHUB_TOKEN:-}" ]]; then - log_step "Authenticating with GITHUB_TOKEN..." - printf '%s\n' "${GITHUB_TOKEN}" | gh auth login --with-token || { + # Validate token format: must start with a known GitHub prefix + case "${GITHUB_TOKEN}" in + ghp_*|gho_*|ghu_*|ghs_*|ghr_*|github_pat_*) + ;; + *) + log_error "GITHUB_TOKEN has unexpected format (expected ghp_, gho_, ghu_, ghs_, ghr_, or github_pat_ prefix)" + return 1 + ;; + esac + + # Fast path: skip persistence if gh is already authenticated with + # stored credentials (not just the env var). Temporarily unset + # GITHUB_TOKEN so gh auth status checks disk credentials only. + local _gh_token="${GITHUB_TOKEN}" + unset GITHUB_TOKEN + if gh auth status &>/dev/null; then + export GITHUB_TOKEN="${_gh_token}" + log_info "Authenticated with GitHub CLI (credentials already persisted)" + return 0 + fi + + log_step "Persisting GITHUB_TOKEN to gh credential store..." + # GITHUB_TOKEN is already unset above so gh auth login won't refuse + # with "The value of the GITHUB_TOKEN environment variable is being + # used for authentication." + printf '%s\n' "${_gh_token}" | gh auth login --with-token || { log_error "Failed to authenticate with GITHUB_TOKEN" + export GITHUB_TOKEN="${_gh_token}" return 1 } + export GITHUB_TOKEN="${_gh_token}" + elif gh auth status &>/dev/null; then + log_info "Authenticated with GitHub CLI" + return 0 else # Device code flow — works on headless/remote servers # Shows a URL + code; user opens URL in local browser and enters the code