Skip to content

Refreshable tokens (5/7): gh auth status short-lived token details - #14454

Open
babakks wants to merge 1 commit into
babakks/refresh-token-c2-auth-tokenfrom
babakks/refresh-token-c3-auth-status
Open

babakks wants to merge 1 commit into
babakks/refresh-token-c2-auth-tokenfrom
babakks/refresh-token-c3-auth-status

Conversation

@babakks

@babakks babakks commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

Part of #14449. Based on #14453 (auth token).

Description

Teaches gh auth status to show short-lived token details and to refresh the token as part of reporting status, so what it displays reflects a currently valid credential rather than a stale or expired one.

How did you test this change?

This feature is verified end to end as a whole rather than per PR. End-to-end tests should pass.

Key points

  • Status now surfaces the expiry information that a short-lived token carries and a non-expiring token does not. This makes the difference between a permanent and a refreshable credential visible to the user at a glance.
  • Reporting status resolves the active token, so it goes through the same refresh path as the other consumers (HTTP transport, gh auth token, the git credential helper). That means a short-lived token whose access token has expired is refreshed and reported as valid, rather than being surfaced as broken. Status reflects the credential gh would actually use, not a stale snapshot.
  • Because the refresh goes through the shared serialized path from PR 2, running gh auth status concurrently with another gh process cannot double-spend the refresh token.

Notes for reviewers

Focus on the status output formatting for the refreshable case and the point where resolving status triggers a refresh. Confirm the non-refreshable path is unchanged.

Commit:

  • fix(auth status): show and refresh short-lived token details

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

Who answers review comments:

  • @babakks will read and reply directly.
  • An agent will draft replies and @username will read them before they are posted.
  • Nobody has explicitly committed to replying.

Refresh an expired or near-expiry refreshable token while reporting status,
unless --no-refresh is given, and show whether the token is short-lived, when it
expires, and whether it was just refreshed. Expose the refreshable and expiry
fields through the JSON output.

Co-authored-by: Copilot <[email protected]>
Copilot-Session: 2dc3f23c-61a6-43a7-85cd-90aa872a7fc6

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Rejected refresh tokens produce misleading status output, and refreshable credentials can be incorrectly labeled short-lived.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Updates gh auth status to refresh and report refreshable credentials.

Changes:

  • Adds automatic refresh with --no-refresh opt-out.
  • Exposes token expiry metadata in human and JSON output.
  • Adds refresh and output tests.
File Description
pkg/​cmd/​auth/​status/​status.go Resolves refreshable credentials and displays expiry details.
pkg/​cmd/​auth/​status/​status_test.go Tests refresh behavior, flag parsing, and JSON output.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

sb.WriteString(fmt.Sprintf(" - Token: %s\n", cs.Bold(e.Token)))

if e.Refreshable {
sb.WriteString(" - Short-lived token that gh refreshes automatically\n")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't seem right. If something does allow these tokens to be non-expiring that should probably be cleaned up elsewhere.

Refreshable: opts.credential.IsRefreshable(),
TokenExpiresAt: opts.credential.ExpiresAt,
RefreshTokenExpiresAt: opts.credential.RefreshTokenExpiresAt,
justRefreshed: opts.refreshStatus == gh.RefreshStatusDone,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user's refresh token has been rejected (expired, already used, revoked) and they run gh auth status to see what's wrong, the refresh path from PR 2 deletes that account's stored credential before anything is printed. resolveCredential then throws away both the error and the RefreshStatusExpired result, and the only thing that reaches here is whether the token was refreshed. So buildEntry carries on with an empty credential and sends the scope check with Authorization: token (nothing after it).

gh auth token and the git credential helper in this stack both handle RefreshStatusExpired explicitly:

// A rejected refresh token can only be recovered by re-authenticating, so surface that as an error rather than
// handing back the stale token, which would fail on the next API call anyway.
if refreshStatus == gh.RefreshStatusExpired {
errMsg := fmt.Sprintf("the token for %s has expired", hostname)
if opts.Username != "" {
errMsg += fmt.Sprintf(" for account %s", opts.Username)
}
errMsg += "; please run 'gh auth login' to re-authenticate"
return errors.New(errMsg)
}

This should probably do the same: carry the refresh status into the entry, skip the scope request, and print a clear reason. Something like:

// buildEntry
if opts.refreshStatus == gh.RefreshStatusExpired {
    entry.State = authEntryStateError
    entry.Error = "refresh token was rejected; the stored credential was removed"
    return entry // don't send an empty-token request
}

The text output would need a matching line for this case, and the token source shouldn't be left blank.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants