Skip to content

Account incorrectly reported active in gh auth status #10136

Description

@med8bra

Describe the bug

Running gh auth status reports account as active, but API calls use another account.

> gh --version
gh version 2.63.0 (1980-01-01)
https://github.com/cli/cli/releases/tag/v2.63.0

Steps to reproduce the behavior

  1. You have two users on same hostname github.com, last active account is user1.
  2. Switch to another folder with custom GH_CONFIG_DIR (via direnv), with a custom config which has one user user2
  3. Run gh auth status
> gh auth status
github.com
  ✓ Logged in to github.com account user2 (keyring)
  - Active account: true
  - Git operations protocol: ssh
  - Token: gho_************************************
  - Token scopes: 'gist', 'read:org', 'repo', 'workflow'
  1. Run gh api /user | jq .name => prints user1, contradicting auth status output

The expected behavior is to use the token for the given host+user of current repository without needing to gh auth switch, and report active account correctly.

Context

Active token is fetched from keyring using only hostname

// ActiveToken will retrieve the active auth token for the given hostname,
// searching environment variables, plain text config, and
// lastly encrypted storage.
func (c *AuthConfig) ActiveToken(hostname string) (string, string) {
if c.tokenOverride != nil {
return c.tokenOverride(hostname)
}
token, source := ghauth.TokenFromEnvOrConfig(hostname)
if token == "" {
var err error
token, err = c.TokenFromKeyring(hostname)
if err == nil {
source = "keyring"
}
}
return token, source
}

Related to #9111 (comment))

Activity

  1. med8bra commented on Dec 23, 2024

    @med8bra
    Author

    Hello @williammartin, for your question about using GH_CONFIG_DIR with direnv, I prefer separating accounts config and I like git includeIf pattern.

    # ~/.gitconfig
    [includeIf "gitdir:~/dev/org1/"]
      path = ~/dev/org1/.gitconfig
    [includeIf "gitdir:~/dev/org2/"]
      path = ~/dev/org2/.gitconfig

    For gh I had to use direnv to achieve this, and also I prefer gh auth status to only report one account, which is the one I'm expecting in my filesystem context.

  2. jtmcg commented on Dec 26, 2024

    @jtmcg
    Contributor

    Interesting issue... I have been able to repro this, and it looks like it actually might be an issue with how GH_CONFIG_DIR is working alongside gh api... It seems that gh api is not respecting the custom config pointed to by the GH_CONFIG_DIR env var. I suspect that it's defaulting to the global config before falling back to the specified config.

    Repro steps

    1. Log out of all accounts
    2. Create a new repo
    3. Create a new config in that repo mkdir .config
    4. Log into account 1 and save creds to the custom config GH_CONFIG_DIR=./.config gh auth login
    5. Confirm this has saved to the new config GH_CONFIG_DIR=./.config gh auth status. Alternatively, you can run cat ./.config/hosts.yml
    6. Hit the api: GH_CONFIG_DIR=./.config gh api /user | jq .login. This should show the same account you are logged in with
    7. Now log in globally with a different account gh auth login
    8. Hit the api again: GH_CONFIG_DIR=./.config api /user | jq .login. This will show the account you've logged into globally and not the one logged into with the specified config.

    I'm not entirely sure what's going on, yet, but I am concerned that this might not be scoped only to gh api. Thanks for pointing this out! We'll discuss and get back to you.

  3. added
    discussFeature changes that require discussion primarily among the GitHub CLI team
    on Dec 26, 2024
  4. williammartin commented on Dec 27, 2024

    @williammartin
    Member

    @jtmcg this is a bit of a funny scenario that has fallen between the Triage rotation cracks. I probably could have been clearer in #9111 (comment) but I'm pretty confident the issue at hand is a disconnect between the way the Active User is determined, and the Active Token is fetched.

    I'll explain those now.

    Active User

    This is the user referenced by the user key under a particular host in the hosts.yml

    Active Token

    This is, in order:

    • The value of the token env vars
    • The value of the oauth_token key under a particular host in the hosts.yml (legacy, insecure)
    • The value in the keyring keyed by a particular host

    So in this case, we're dealing with a changing hosts.yml containing the same host (github.com), but a keyring value that is keyed by a particular host.

    So when the active token is fetched, it returns whatever token happened to be put in there last.

    The reason auth switch resolves this is because auth switching was implemented by also keying tokens in the keyring by host and username, then swapping the token into the host keyed entry to make it active. This was for backwards compatibility reasons.

    I have some thoughts on how we might resolve this but I'm on vacation and just wanted to drop this message so that you don't wild goose chase.

  5. williammartin commented on Dec 27, 2024

    @williammartin
    Member

    @jtmcg this is a bit of a funny scenario that has fallen between the Triage rotation cracks. I probably could have been clearer in #9111 (comment) but I'm pretty confident the issue at hand is a disconnect between the way the Active User is determined, and the Active Token is fetched.

    I'll explain those now.

    Active User

    This is the user referenced by the user key under a particular host in the hosts.yml

    Active Token

    This is, in order:

    • The value of the token env vars
    • The value of the oauth_token key under a particular host in the hosts.yml (legacy, insecure)
    • The value in the keyring keyed by a particular host

    So in this case, we're dealing with a changing hosts.yml containing the same host (github.com), but a keyring value that is keyed by a particular host. When the active token is fetched, it returns whatever token happened to be put in there last.

    The reason auth switch resolves this is because auth switching was implemented by also keying tokens in the keyring by host and username, then swapping the token into the host keyed entry to make it active. This was for backwards compatibility reasons.

    I have some thoughts on how we might resolve this but I'm on vacation and just wanted to drop this message so that you don't wild goose chase.

  6. jtmcg commented on Dec 27, 2024

    @jtmcg
    Contributor

    Ah, this is my bad. I totally missed the linked issue #9111 😅 This did, at least, give me a chance to explore this a bit and I can confirm the behavior! lol

  7. self-assigned this
    on Dec 27, 2024
  8. removed
    discussFeature changes that require discussion primarily among the GitHub CLI team
    on Feb 27, 2025
  9. kyoh86 commented on Apr 15, 2025

    @kyoh86

    I am hoping this issue will be resolved.
    I want to switch the active account of gh for each directory.
    For only my use case, I don't necessarily want to support the GH_CONFIG_DIR switch by direnv, since I only need to be able to specify the active account of gh per directory.

  10. added
    priority-3Affects a small number of users or is largely cosmetic
    on Apr 23, 2025
  11. removed their assignment
    on Apr 23, 2025
  12. added
    needs-designAn engineering task needs design to proceed
    and removed on Apr 23, 2025
  13. williammartin commented on Jun 17, 2025

    @williammartin
    Member

    Acceptance Criteria

    Given I have two accounts on a single github host
    And Given I have each of these accounts as the active user in separate hosts.yml files
    When I set GH_CONFIG_DIR to either of the files
    And When I run gh api /user .login
    Then I see the active user is correct as per the hosts.yml file

  14. Shadow-web-pixel commented on Nov 10, 2025

    @Shadow-web-pixel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workinghelp wantedContributions welcomeneeds-designAn engineering task needs design to proceedpriority-3Affects a small number of users or is largely cosmetic

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions