Skip to content

Fix git-credential helper to respect username parameter - #11937

Closed
cavanaug wants to merge 3 commits into
cli:trunkfrom
cavanaug:fix-git-credential-username
Closed

cavanaug wants to merge 3 commits into
cli:trunkfrom
cavanaug:fix-git-credential-username

Conversation

@cavanaug

@cavanaug cavanaug commented Oct 15, 2025 •

Copy link
Copy Markdown

Summary

Changes

Previously, the git-credential helper ignored usernames passed by git (e.g., username=dilbertwork_acme) and always returned the active user's token. This behavior was inconsistent with gh auth token --user USERNAME, which correctly looks up tokens for specific users.

This PR restructures the token lookup logic to:

  1. Check environment variables first (highest priority, overrides username)
  2. If username provided, look up that specific user's token via TokenForUser()
  3. Fall back to active user's token if user-specific token not found
  4. Remove restrictive validation that rejected non-active usernames

Testing

  • All existing tests pass (14 git-credential tests)
  • Added 3 new test cases covering user-specific token lookups
  • Verified with real-world multi-user scenarios

Fixes authentication for multi-account setups where git passes user-specific credentials to the helper.

Fixes #11938

@cavanaug
cavanaug requested a review from a team as a code owner October 15, 2025 20:19
@cavanaug
cavanaug requested review from babakks and Copilot October 15, 2025 20:19
@cliAutomation cliAutomation added the external pull request originating outside of the CLI core team label Oct 15, 2025
@cliAutomation

Copy link
Copy Markdown
Contributor

Hi! Thanks for the pull request. Please ensure that this change is linked to an issue by mentioning an issue number in the description of the pull request. If this pull request would close the issue, please put the word 'Fixes' before the issue number somewhere in the pull request body. If this is a tiny change like fixing a typo, feel free to ignore this message.

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.

Pull Request Overview

This PR fixes the gh auth git-credential helper to properly respect username parameters passed by git, enabling multi-account authentication workflows. Previously, the helper ignored usernames and always returned the active user's token, creating issues for users managing multiple GitHub accounts.

Key Changes:

  • Restructured token lookup to check environment variables first, then look up user-specific tokens via TokenForUser(), and finally fall back to the active user's token
  • Removed validation that rejected non-active usernames
  • Added TokenForUser() method to the config interface

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
pkg/cmd/auth/gitcredential/helper.go Implements new token lookup logic with environment variable priority, user-specific token lookups, and fallback handling
pkg/cmd/auth/gitcredential/helper_test.go Adds test implementation for TokenForUser() and 3 new test cases for user-specific token scenarios

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +146 to +153
// If user-specific token lookup failed, fall back to active token/user
if gotToken == "" {
gotToken, source = cfg.ActiveToken(lookupHost)
if gotToken == "" && strings.HasPrefix(lookupHost, "gist.") {
lookupHost = strings.TrimPrefix(lookupHost, "gist.")
gotToken, source = cfg.ActiveToken(lookupHost)
}
}

Copilot AI Oct 15, 2025

Copy link

Choose a reason for hiding this comment

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

The fallback logic for user-specific token lookup uses the original lookupHost which may have already been modified in line 139. If the gist prefix was stripped during user-specific lookup, the fallback will use the modified lookupHost instead of the original wants[\"host\"], potentially causing incorrect token lookups.

Copilot uses AI. Check for mistakes.
Comment thread pkg/cmd/auth/gitcredential/helper.go Outdated
Comment on lines +137 to +139
} else if strings.HasPrefix(wants["host"], "gist.") {
// Try without gist. prefix for user lookup
lookupHost = strings.TrimPrefix(wants["host"], "gist.")

Copilot AI Oct 15, 2025

Copy link

Choose a reason for hiding this comment

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

The condition checks wants[\"host\"] but the prefix was already stripped from lookupHost on line 122. This should check strings.HasPrefix(lookupHost, \"gist.\") instead to correctly handle the gist fallback case for user-specific token lookups.

Suggested change
} else if strings.HasPrefix(wants["host"], "gist.") {
// Try without gist. prefix for user lookup
lookupHost = strings.TrimPrefix(wants["host"], "gist.")
} else if strings.HasPrefix(lookupHost, "gist.") {
// Try without gist. prefix for user lookup
lookupHost = strings.TrimPrefix(lookupHost, "gist.")

Copilot uses AI. Check for mistakes.
Replace buggy implementation that mutates lookupHost with cleaner helper
function approach. This fixes two issues identified in code review:

1. Lines 137-139 checked wants["host"] but should check lookupHost after
   gist prefix was stripped on line 122
2. Lines 146-153 used mutated lookupHost in fallback logic, potentially
   causing incorrect lookups

The new tryBothHosts helper function encapsulates gist hostname fallback
without state mutation, making the code clearer and less error-prone.
@cavanaug
cavanaug requested a review from Copilot October 15, 2025 21:08

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.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread pkg/cmd/auth/gitcredential/helper.go Outdated
// If the host starts with "gist.", try without the prefix
if strings.HasPrefix(host, "gist.") {
strippedHost := strings.TrimPrefix(host, "gist.")
return lookup(strippedHost)

Copilot AI Oct 15, 2025

Copy link

Choose a reason for hiding this comment

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

When the first lookup succeeds with err == nil but returns an empty token, the function returns (token, src, nil) at line 123. However, when the gist host fallback at line 128 fails, the function returns the original empty token from line 121, not the error from the fallback attempt. This could mask legitimate errors from the fallback lookup. Consider returning the fallback error when both lookups fail.

Suggested change
return lookup(strippedHost)
fallbackToken, fallbackSrc, fallbackErr := lookup(strippedHost)
if fallbackErr == nil && fallbackToken != "" {
return fallbackToken, fallbackSrc, nil
}
return fallbackToken, fallbackSrc, fallbackErr

Copilot uses AI. Check for mistakes.
Comment on lines +158 to +166
if gotToken == "" {
gotToken, source, _ = tryBothHosts(lookupHost, func(host string) (string, string, error) {
token, src := cfg.ActiveToken(host)
if token == "" {
return "", "", fmt.Errorf("no token")
}
return token, src, nil
})
}

Copilot AI Oct 15, 2025

Copy link

Choose a reason for hiding this comment

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

The error from tryBothHosts is silently discarded at line 159. This fallback logic should either handle the error or document why it's safe to ignore. If the active token lookup fails, the subsequent check at line 189 will catch the empty token, but explicit error handling would make the code's intent clearer.

Copilot uses AI. Check for mistakes.
Comment thread pkg/cmd/auth/gitcredential/helper.go Outdated
Comment on lines +134 to +140
envToken, envSource, _ := tryBothHosts(lookupHost, func(host string) (string, string, error) {
token, src := cfg.ActiveToken(host)
if token == "" {
return "", "", fmt.Errorf("no token")
}
return token, src, nil
})

Copilot AI Oct 15, 2025

Copy link

Choose a reason for hiding this comment

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

The lambda function wrapping cfg.ActiveToken() is duplicated at lines 134-140, 159-165. Consider extracting this into a reusable helper function to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
- Return fallback error when both host lookups fail (instead of original error)
- Extract activeTokenLookup helper to eliminate duplicate lambda functions
- Add comment documenting intentional error discard in fallback logic
@cavanaug
cavanaug requested a review from Copilot October 15, 2025 23:19

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.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

if fallbackErr == nil && fallbackToken != "" {
return fallbackToken, fallbackSrc, nil
}
return fallbackToken, fallbackSrc, fallbackErr

Copilot AI Oct 15, 2025

Copy link

Choose a reason for hiding this comment

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

When the gist host fallback fails, this returns the fallback error instead of the original error. This could mask the real issue. Consider returning the original error when both attempts fail, as it provides more accurate debugging information.

Suggested change
return fallbackToken, fallbackSrc, fallbackErr
return token, src, err

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I dunno, I think this is debateable...

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your pull request! 🎉

This PR appears to fix the following issues that are not labeled with help wanted Contributions welcome :

As outlined in our Contributing Guidelines, we expect that PRs are only created for issues that have been labeled help wanted.

While we appreciate your initiative, please note that:

  • PRs for non-help wanted issues may not be reviewed immediately as they might not align with our current priorities
  • The issue might already be assigned to a team member or planned for a specific release
  • We may need to close this PR. For example, if it conflicts with ongoing work or architectural decisions

What happens next:

  • Our team will review this PR and the associated issues
  • We may add the help wanted label to the issues, if appropriate, and review this pull request
  • In some cases, we may need to close the PR. For example, if it doesn't fit our current roadmap

Thank you for your understanding and contribution to the project! 🙏

This comment was automatically generated by cliAutomation.

@babakks

babakks commented Oct 22, 2025

Copy link
Copy Markdown
Member

Closing as explained in #11938 (comment).

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

Labels

external pull request originating outside of the CLI core team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

auth git-credential fails if username provided by git does not match active user

4 participants