fix: persist gh auth credentials for interactive sessions (#1491)

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
This commit is contained in:
Ahmed Abushagur 2026-02-19 16:30:44 -08:00 committed by GitHub
parent 9e2f84adf0
commit 0e2750dfd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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