Skip to content

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

Description

@cavanaug

Describe the bug

I have a scenario where I have both a personal and work github account, each with different usernames/tokens etc.

I wanted to configure things as the following in my .gitconfig

[credential "https://github.com/"]
	username = dilberthome
	helper =
	helper = !gh auth git-credential
[credential "https://github.com/acme-main/"]
	username = dilbertwork_acme
	helper =
	helper = !gh auth git-credential

Affected version

gh version 2.81.0 (2025-09-30)
https://github.com/cli/cli/releases/tag/v2.81.0

Steps to reproduce the behavior

  1. Perform gh auth for github.com with the two different usernames
  2. Perform a checkout "git clone https://github.com/cli/cli.git"
  3. This SHOULD perform a checkout as user dilberthome using my gh token credential. Which it DOES IF my gh active account is dilbert_home.
  4. Perform a checkout "git clone https://github.com/acme-main/supercoolthingy.git"
  5. This SHOULD perform a checkout as user dilberwork using my gh token credential for work. Which it DOES NOT if my gh active account is dilbert_home

Expected vs actual behavior

Given that I have specified the proper username in my gitconfig I would expect that the configured username is used for the git-credential lookup. Which it does not.

In essence I would expect it to behave similarly to

gh auth token --user xxxx --hostname yyyy

Logs

Paste the activity from your command line. Redact if needed.

Activity

  1. added
    enhancementa request to improve CLI
    authrelated to tokens, authentication state, or oauth
    gh-authrelating to the gh auth command
    and removed
    bugSomething isn't working
    on Oct 15, 2025
  2. github-actions commented on Oct 15, 2025

    @github-actions
    Contributor

    Thank you for your issue! We have categorized it as a feature request, and it has been added to our backlog. In doing so, we are not committing to implementing this feature at this time, but, we will consider it for future releases based on community feedback and our own product roadmap.

    Unless you see the help wanted Contributions welcome label, we are not currently looking for external contributions for this feature.

    If you come across this issue and would like to see it implemented, please add a thumbs up! This will help us prioritize the feature. Please only comment if you have additional information or viewpoints to contribute.

  3. cavanaug commented on Oct 16, 2025

    @cavanaug
    Author

    FYI. I also submitted a PR to address this issue. #11937

  4. babakks commented on Oct 22, 2025

    @babakks
    Member

    Hi @cavanaug! 👋 And thanks for opening up an issue! 🙏

    I haven't yet fully digged this, but as far as I can tell from the auth git-credential command implementation, it seems like it doesn't support multiple usernames per host, although gh does support this.

    We need to look more into this and make sure it's possible to improve auth git-credential without breaking any behaviour (See my notes below). As for your PR, I'm afraid I have to close it for now, as an open PR means it has to be reviewed, while we haven't yet decided if/how we want to implement this. That's why we advise against submitting PRs before an issue is fully triaged and marked with help wanted Contributions welcome . Anyway, I appreciate your effort you put into this and that definitely provide insight for later implementation. 🙏

    Now, as a workaround to unblock you, I'm wondering if such a change would help with your case:

    [credential "https://github.com/"]
    	username = dilberthome
    	helper =
    	helper = !gh auth git-credential
    [credential "https://github.com/acme-main/"]
    	username = dilbertwork_acme
    	helper =
    	helper = !GH_TOKEN="$(gh auth token --hostname github.com --user dilbertwork_acme)" gh auth git-credential

    Basically, we're just overriding the behaviour by opting to a temporarily set GH_TOKEN env var.

    Can you please try this and let me know if it helps?

    Notes on potential implementation

    This seems to be slipped through our hands back when multiple account support was added. I'm not sure yet, and I have to dig deeper into the repo history to see if there's a sound reason for this behaviour. Assuming there isn't, this seems like a plausible addition.

    Basically, Git can include a username in the input to auth git-credential and we should return the credentials associated with that username, instead of always picking the active user for the given host. Currently, we bail out if the given username does not match the active user. Needless to say we have to see if there's a specific reason behind such a restriction.

  5. cavanaug commented on Oct 22, 2025

    @cavanaug
    Author

    Just a few comments for future reference.

    The challenge is how to semi-cleanly integrate GH across personal and work in a sane fashion additionally supporting multiple GH enterprise instances. Folks want tools like git/lazygit/etc and gh cli to "just work" for both work and personal situations.

    Ive had a heck of a time making all this work. gh-cli is really centered around tokens and sort of has users as a bit of an afterthought. GitHub itself is not super set up for multiple accounts on the same repos. Ssh is a mess because you cant really dynamically configure things by folder unless you do some magic in .gitconfig to explicitly list which ssh key to use.

    Technically some of this defect could be side stepped with a shell script replacement for git-credential that does the right thing and calls gh cli under the hood. But that is then a private script and integration would always be better.

    The bottom line is securely handling tokens/auth for git & gh-cli and such across multiple accounts and github instances is just sort of broken and its not really anyones fault, things just evolved and we ended up where we are today and folks are trying to glue together some pieces/strategies/conventions that will work for folks that dont have a black belt in linux/bash/git/gh.

    Id love to be able to help with this. Hit me up whenever, my company (HPI) has a large migrations of like 100k repos and thousands of users to GH. I can work on this as part of my company role as well.

  6. changed the title [-]gh auth git-credential doesnt respect username from config file[/-] [+]`auth git-credential` fails if `username` provided by git does not match active user[/+] on Oct 23, 2025
  7. galamdring commented on Apr 10, 2026

    @galamdring

    I came at this problem from a different direction, looking to make it automatic to use my non-personal account for any repo in the non-personal org, and my personal account for everything else. I implemented it in a PR, that was of course automatically closed, but wanted to add it here as a reference as well.
    I set it up so that you can add a mapping of owner -> user, and then any repo with a configured owner will use the mapped user via the factory created http client. If there is not an owner->user match, it will use the globally active one. This allows me to leave it as my personal account in general, but ensures that when I need to interact with a non-personal repo, I'm using the correct credential.

    The solutions suggested here are good solutions if the repos are segregated by directory easily. That wasn't the case for me, so the owner path seemed the most straightforward.

    PR: #13130
    Root Cause Analysis: #12885

    Similar Issues:
    #326
    #9111
    #12145

  8. nonemec-havok commented on Aug 24, 2026

    @nonemec-havok

    Adding a concrete reproduction, since the thread so far describes the behaviour but doesn't demonstrate it. Tested on gh 2.97.0 with two accounts logged in to github.com (user-a, user-b).

    With user-a active:

    $ printf 'protocol=https\nhost=github.com\nusername=user-a\n\n' | gh auth git-credential get
    protocol=https
    host=github.com
    username=user-a
    password=gho_***
    
    $ printf 'protocol=https\nhost=github.com\nusername=user-b\n\n' | gh auth git-credential get
    $ echo $?
    1
    
    $ printf 'protocol=https\nhost=github.com\n\n' | gh auth git-credential get
    # -> returns user-a's credential

    Then gh auth switch -u user-b and repeat:

    $ printf 'protocol=https\nhost=github.com\nusername=user-b\n\n' | gh auth git-credential get
    # -> now succeeds
    
    $ printf 'protocol=https\nhost=github.com\nusername=user-a\n\n' | gh auth git-credential get
    $ echo $?
    1

    So the requested username= is used purely as an assertion against the globally active account, never as a selector. The same request flips from success to exit 1 depending only on global state that the repository has no control over.

    The practical consequence is that the documented per-repo pattern is inert whenever gh is the configured helper:

    [credential "https://github.com"]
        username = user-b

    gh auth setup-git installs a host-scoped helper = !gh auth git-credential entry (preceded by a blank helper = reset), so this affects anyone who ran it and later added a second account — which is the normal path into a multi-account setup.

    Where it happens — pkg/cmd/auth/gitcredential/helper.go:

    // resolves the *active* account, ignoring wants["username"] entirely
    gotToken, source := cfg.ActiveToken(lookupHost)   // ~L115
    gotUser, _ = cfg.ActiveUser(lookupHost)           // ~L124
    
    // username= is only ever compared, never used to look anything up
    if wants["username"] != "" && gotUser != tokenUser && !strings.EqualFold(wants["username"], gotUser) {
        return cmdutil.SilentError                    // ~L134
    }

    AuthConfig.TokenForUser(hostname, user) already exists and does exactly the lookup needed here, so the change is roughly: when wants["username"] is non-empty and doesn't match the active user, try TokenForUser before giving up, and only return SilentError if that also fails. Env-sourced tokens (*_TOKEN, where gotUser == tokenUser) should keep short-circuiting as they do now. That preserves current behaviour in every single-account and env-token case.

    @babakks — this matches your Oct 2025 read that the helper "doesn't support multiple usernames per host, although gh does support this". #11937 was closed pending a wider look; it could be rebased onto the TokenForUser approach if that's the preferred shape.

    For orientation, this is one of two independent halves of the multi-account problem:

    • this issue — git HTTPS operations through the credential helper;
    • #13768 — gh's own commands (gh pr create, gh api) picking an account from context;
    • #12885 — the shared prerequisite, since keyring lookup keys on service only and returns an arbitrary entry when several accounts share a host.

    Worth noting that fixing only #13768 would leave gh pr create using the correct account while git push in the same directory still fails or pushes as the wrong identity — PR #13781 wraps the factory's HTTP token getter, which this helper doesn't go through. This issue needs its own fix either way.

  9. nonemec-havok commented on Aug 27, 2026

    @nonemec-havok

    Follow-up, since I found something that narrows this further: the lookup this issue needs isn't just present internally — it's already exposed as a supported, documented flag, and it already works for a non-active account.

    Retested on gh 2.98.0, two accounts on github.com, with user-a active:

    $ gh auth token --user user-b          # the non-active account
    gho_****************************       # -> exit 0, 40-char token
    
    $ GH_TOKEN=$(gh auth token --user user-b) gh api user --jq .login
    user-b                                 # -> resolves as the right identity
    
    $ gh api user --jq .login              # unset, i.e. the active account
    user-a

    Meanwhile the credential helper, same machine, same moment, same account:

    $ printf 'protocol=https\nhost=github.com\nusername=user-b\n\n' | gh auth git-credential get
    $ echo $?
    1

    That's the whole issue in four commands. gh can already retrieve a non-active account's token on demand and act as that identity; auth git-credential is the one path that can't.

    pkg/cmd/auth/token/token.go (~L83) reaches this via authCfg.TokenForUser(hostname, opts.Username) — the same AuthConfig.TokenForUser (internal/config/config.go:515) I referenced above. So the helper isn't missing a capability, it's just not calling one that already ships, is already user-facing, and is already proven correct on this platform.

    Two things follow:

    • Scope. This is a plumbing gap, not new functionality. The fix reuses an existing primitive rather than introducing a resolution strategy, so it can't regress storage or token-resolution behaviour, and it doesn't depend on the broader design discussion in Better multi-account gh account usage #13768 / Automatic Account Selection for Multi-Account Workflows #12697 landing first.
    • Risk. Every single-account setup and every env-token (*_TOKEN) case keeps hitting the existing active-account path unchanged; the new lookup only runs where the helper returns exit 1 today.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    authrelated to tokens, authentication state, or oauthenhancementa request to improve CLIgh-authrelating to the gh auth commandneeds-triageneeds to be reviewedstale

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions