cli / cli

GitHub’s official command line tool
https://cli.github.com
MIT License
36.66k stars 5.6k forks source link

Allow multiple account credentials #326

Open ncmans opened 4 years ago

ncmans commented 4 years ago

Describe the feature or problem you’d like to solve

I have two accounts on github each with access to a different set of private repositories. Currently I use a combination of git's [IncludeIf ...] config and a custom config which overrides the SSH private key I use for certain repositories. E.g.:

In ~/.gitconfig:

[includeIf "gitdir:~/work/"]
  path = ~/work/.gitconfig

and in ~/work/.gitconfig:

[core]
  sshCommand = "ssh -i ~/.ssh/work"

It will be great if the credentials for gh can also be configured per repository as well.

Proposed solution

There are a bunch of options I can think of:

github.com/google:
  - user: gemployee
    oauth_token: ...

github.com/microsoft:
  - user: msemployee
    oauth_token: ...

github.com:
  - user: personal
    oauth_token: ...

How will it benefit CLI and its users?

It will allow Github users with multiple accounts of varying access to seamlessly work on different repositories.

eXamadeus commented 4 years ago

Is there currently a way to work around this by removing the current authentication? I can't seem to find it.

Reinstalling seems to work, but it's very cumbersome.

mislav commented 4 years ago

@eXamadeus GitHub CLI currently has no mechanism for switching between multiple GitHub accounts and I don't really have a workaround to suggest for you at this moment, sorry.

The only approach I could imagine, but would not recommend to anyone, would be to authenticate with 1st account, save a copy of ~/.config/gh/hosts.yml somewhere & delete the original file, authenticate again with the 2nd account, and now you can swap the ~/.config/gh/hosts.yml file with the backup file when you need to switch accounts.

Since this solution involves using SSH for git protocol, make sure both configuration files include the git_protocol: ssh line.

aitchkhan commented 3 years ago

If we can add two entries or as many entries on every gh auth login. It might become easier with something like this:

github.com:
    user: aitchkhan
    oauth_token: xxx
    user: haroonKhan-10p
    oauth_token: xxx
    git_protocol: ssh

If the cli can somehow sense which username the current repository is associated to. It would be easy to find the user in the hosts.yml and rest would be magic.

PS: I have not gone through the codebase. These are my assumptions.

seed-of-apricot commented 3 years ago

Searched and landed here. This feature would help people with two or more accounts (personal/business/others) and use both of them every day (swapping the config file would be too much for them).

fdm1 commented 3 years ago

It's a bit hacky, but I solved this so that I could evaluate if I want to actually use this cli by using the token auth capabilities, putting this into my dotfiles.

alias gh_cli=$(which gh)
function gh() {
  if [[ $(pwd) =~ {PATH_TO_PERSONAL_CODE} ]]; then
    GITHUB_TOKEN=$PERSONAL_GH_CLI_TOKEN
  else
    GITHUB_TOKEN=$WORK_GH_CLI_TOKEN
  fi

  GITHUB_TOKEN=$GITHUB_TOKEN gh_cli $@
  unset GITHUB_TOKEN
}
seed-of-apricot commented 3 years ago

@fdm1 Which config file did you fill that in?

fdm1 commented 3 years ago

@fdm1 Which config file did you fill that in?

Just in my bash dotfiles

tomholford commented 3 years ago

Now that #2179 has been merged, perhaps it will be less effort to implement this feature?

Would be nice to have something like how git handles it: ~/.gitconfig

[includeIf "gitdir:~/dev/"]
  path = ~/.gitconfig.personal
[includeIf "gitdir:~/workspace/"]
  path = ~/.gitconfig.work

~/.gitconfig.work

[user]
  name = example-work-user
  email = example@example.com

edit

Now that I think about it some more, can gh infer which authed account to use from the current git config user.name or email?

yangby-cryptape commented 3 years ago

Since #2444 has been merged, as an alternative solution, set the environment variable GH_CONFIG_DIR to different directories could allow multiple accounts.

dantonyuk commented 3 years ago

I just implemented simple multi-account support based on the remote.origin.url config property (of course GH_HOST is on board as well): #3278

So having hosts.yml as

github.com:
    user: personal-account
    oauth_token: personal-token
github.com/acme:
    user: work-account
    oauth_token: work-token

we use work credentials for all acme repositories and personal credentials for everything else.

~Unfortunately, gh cli uses global settings for "default" host, so I postponed the implementation of gh auth status which uses several credentials at the same time.~ Implemented.

jeremy-ww commented 3 years ago

Maybe this is a workaround if u wanna use multi credentials for a specific command. such as pr view

/usr/local/pr2

#!/usr/bin/python3

import subprocess
import json
import os
import re

with open(os.path.expanduser('~/.pr-config.json')) as f:
  configs = json.load(f)

command = 'gh pr view --web'
remote_url = subprocess.check_output(['git', 'config', '--get', 'remote.origin.url']).strip().decode()

for pattern, config in configs.items():
  is_match = re.match(pattern, remote_url)
  if is_match:
    os.system(
      f'GH_HOST={config["host"]} GH_ENTERPRISE_TOKEN={config["token"]} {command}'
    )
    break

~/.pr-config.json

{
  "git@enterprise.com": {
    "host": "enterprise.com",
    "token": ""
  },
  "git@enterprise1.com": {
    "host": "enterprise1.com",
    "token": ""
  }
}
thecharlesjenkins commented 3 years ago

I am able to have multiple accounts signed in and that is nice, but one thing that I am trying to do is when I am creating a repository I would like to select my account. Doing something like gh repo create my_name/repo_name doesn't seem to work because one of my accounts is enterprise and one is public. Passing in my enterprise name like this causes HTTP 404: Not Found (https://api.github.com/users/my_enterprise_username) when I am logged into both my personal and enterprise GitHub but trying to create a repo for my private Github. This is not like a first-logged into account gets priority, no matter the order in which I log into my GitHub accounts I will receive this error when trying to create a repo for my enterprise account. Is there a way to force gh repo create to use the enterprise API instead of the public one? Right now I must log out of my personal account to create an enterprise repo unless I am doing something wrong.

matthew-cline commented 3 years ago

I've created a bash wrapper script for gh which makes it easier to do multiple accounts. Uses personal access tokens.

camillesf commented 3 years ago

¡Hello!

After having read everything, I believe it'd be more robust not to second-guess anything, and mark the user to use via Git's own configuration mechanism.

From the original submission, the work config would be expanded to specify the username to use for the server in question:

# ~/work/gitconfig
[core]
    sshCommand = "ssh -i ~/.ssh/work"

[gh "github.com"]
    user = workuser

That way, we'd leverage the includeIf mechanism that everybody is already using to customize Git to their needs; hosts.yml could just have an array of stanzas for the same (or different) servers.

$  git config --get gh.github.com.user
workuser

(The hosts.yml format would have to be adjusted to be able to take multiple user stanzas, of course. A bit, but not quite, like https://github.com/cli/cli/issues/326#issuecomment-694676958.)

Edited to add: sadly, it would not work for things like gh repo clone, because all includeIf directives only when already in a repo.


Replying now to https://github.com/cli/cli/issues/326#issuecomment-745597925:

Now that I think about it some more, can gh infer which authed account to use from the current git config user.name or email?

I'm not sure that's a viable approach, since some users may not alter user.* variables (see the original submission where only sshCommand is changed).

lucatpa commented 3 years ago

I strongly recommend using direnv in conjunction with GH_CONFIG_DIR to use multiple accounts.

trkoch commented 3 years ago

This is by far the easiest solution. If you don't like/cannot use direnv, you can still manually prefix GH_CONFIG_DIR. Shell wrappers above are clever, but this gets the job done just fine. @lucatpa Thanks for sharing!

I strongly recommend using direnv in conjunction with GH_CONFIG_DIR to use multiple accounts.

ghost commented 3 years ago

Similar to @lucatpa I use a tool to manage my GH_CONFIG_DIR but I use OnDir instead. The advantage of this is that I don't have to setup multiple .envrc files in each repo

So I have this setup in my .config/gh folder

├── config.yml
├── org1
│   └── hosts.yml
└── org2
    └── hosts.yml

and I have this setup in my ~/.ondirrc config file

enter ~/source/org1/([^/]+)
  export GH_CONFIG_DIR="/Users/username/.config/gh/org1"

leave ~/source/org1/([^/]+)
  unset GH_CONFIG_DIR

enter ~/source/org2/([^/]+)
  export GH_CONFIG_DIR="/Users/username/.config/gh/org2"

leave ~/source/org2/([^/]+)
  unset GH_CONFIG_DIR

HTH

(Just to make it super clear, all my code is in /Users/username/source/)

redasamik commented 3 years ago

في خميس، 10 يونيو، 2021 في 11:01 ص، كتب Think Stack Limited < @.***>:

Similar to @lucatpa https://github.com/lucatpa I use a tool to manage my GH_CONFIG_DIR but I use OnDir https://github.com/alecthomas/ondir instead So I have this setup in my .config/gh folder

├── config.yml

├── org1

│ └── hosts.yml

└── org2

└── hosts.yml

and I have this setup in my ~/.ondirrc config file

enter ~/source/org1/([^/]+)

export GH_CONFIG_DIR="/Users/username/.config/gh/org1"

leave ~/source/org1/([^/]+)

unset GH_CONFIG_DIR

enter ~/source/org2/([^/]+)

export GH_CONFIG_DIR="/Users/username/.config/gh/org2"

leave ~/source/org2/([^/]+)

unset GH_CONFIG_DIR

HTH

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/cli/cli/issues/326#issuecomment-858404368, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOW7T7BNNBFFEE4FJEHFXCLTSBWO5ANCNFSM4KRIJKRA .

-- reda

Lilokais commented 2 years ago

Shingle.org.com

camh- commented 2 years ago

What about using git credential fill to get the creds and delegate it all to git-credential-helpers? If gh did this, it would work perfectly for my use case (two GitHub accounts with git already set up to use the right creds for various repos depending on repo org/user). It also means that gh does not need to store tokens in plaintext on the filesystem.

ezzra commented 2 years ago

I want to share my little approach that I build after reading this issue here some time ago, it does just link the hosts.yml config to user-specific hosts-username.yml files (which you need to create first), depending on a git repo setting (that you have to config).

However, it makes it possible to use use specific gh for specific Github users that you setup per local repo.

https://gist.github.com/ezzra/ec5a575a6cab368179bbd6a95e6f7fe6

(using git credential fill might be worth to use here, I didn't know about this when I created my workaround)

eabase commented 2 years ago

Just linking to git credential for easy ref. [1] https://git-scm.com/docs/git-credential/2.35.0 [2] https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage

voltuer commented 2 years ago

seriously? 2 years and github still won't listen to such an obvious feature?

camh- commented 2 years ago

@tr4g please be kind. i know it's frustrating, but there's so much work that can be done, not everything will get done. I personally want to take a crack at this, using git credential, but time keeps ticking.

For what it's worth, I have a switchhub script that symlinks a -<username> file to the main config file for hub and gh. It's a little manual, but eases the pain a bit for now. Attached. switchhub.txt (renamed to .txt to be able to attach)

eabase commented 2 years ago

@camh- What does caller do here?

camh- commented 2 years ago

@eabase the last line of the script is the same as

# Only run main if executed as a script and not "sourced".
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then main "$@"; fi

I've been doing this a long time before ${BASH_SOURCE} worked for this and caller was the only way I found to do it. It is just inspecting the function call stack. I should update my script template.

sueydev commented 2 years ago

This is a terrible Dev experience. Someone please fix this.

j33pguy commented 2 years ago

Too bad this did not work something like Kubectx. Just run a Kubectx command (which is pointed at all the Kube certs you have), and select which Kube cluster you want to work with. Made my life soo much easier!

https://github.com/ahmetb/kubectx

Not hating on the devs, just though I would chime in, Keep up the good work devs!

j33pguy commented 2 years ago

I strongly recommend using direnv in conjunction with GH_CONFIG_DIR to use multiple accounts.

I have never head of direnv...you have started a new rabbithole and have saved me so much work...Thank you!

ghost commented 2 years ago

I have never head of direnv...you have started a new rabbithole and have saved me so much work...Thank you!

@j33pguy if you are down a rabbithole, you might as well try ondir 😁

Similar to @lucatpa I use a tool to manage my GH_CONFIG_DIR but I use OnDir instead. The advantage of this is that I don't have to setup multiple .envrc files in each repo

yermulnik commented 2 years ago

Until multiple accounts are not implemented in gh, I've ended up with such aliases to switch between regular GitHub and Company's GitHub (thank God gh supports aliases 😝): https://gist.github.com/yermulnik/017837c01879ed3c7489cc7cf749ae47

Griffin641498 commented 2 years ago

Describe the feature or problem you’d like to solve

I have two accounts on github each with access to a different set of private repositories. Currently I use a combination of git's [IncludeIf ...] config and a custom config which overrides the SSH private key I use for certain repositories. E.g.:

In ~/.gitconfig:

[includeIf "gitdir:~/work/"]
  path = ~/work/.gitconfig

and in ~/work/.gitconfig:

[core]
  sshCommand = "ssh -i ~/.ssh/work"

It will be great if the credentials for gh can also be configured per repository as well.

Proposed solution

There are a bunch of options I can think of:

  • Allow overriding gh config by a file in a local clone's .git directory
  • Allow defining rules in the gh config which defines which repos/orgs should use which credential. E.g.
github.com/google:
  - user: gemployee
    oauth_token: ...

github.com/microsoft:
  - user: msemployee
    oauth_token: ...

github.com:
  - user: personal
    oauth_token: ...

How will it benefit CLI and its users?

It will allow Github users with multiple accounts of varying access to seamlessly work on different repositories.

Looklili commented 2 years ago

[includeIf` "gitdir:~/work/"]

path = ~/work/.gitconfig

marcelocra commented 2 years ago

@thinkstack

I haven't used any of those, but seems like direnv supports exactly the same thing (print from their docs below), by searching parent folders for a .envrc (or .env) file.

So I guess you don't really need to put one file per directory if you organize your directories appropriately.

Direnv docs:

image


@yermulnik

Thanks! That's a very lightweight alternative if we don't want to install anything else.

marcelocra commented 2 years ago

How do you folks manage your credentials with those solutions?

QUGL-98-06-15 commented 2 years ago

1618365730

leonhfr commented 2 years ago

@marcelocra

How do you folks manage your credentials with those solutions?

Not sure this is what you're asking but I use 1password and chezmoi.

github.com:
    user: leon-work
    oauth_token: {{ (onepasswordItemFields "2qaegfeuivaudgbx5zmdjtcjeu").credential.v }}
    git_protocol: ssh
partiallyordered commented 2 years ago

Somewhat related: today I attempted to use gh pr create and was rebuffed because the GITHUB_TOKEN in my env was a token with very limited permissions. I'm using said token to authenticate with a GH package registry, so it has read-only permissions. It's loaded into my env by direnv. gh preferred the GITHUB_TOKEN env var over its other auth method. It would be useful to have a mechanism to ignore the environment variables. Or specify preferred ordering of auth method, i.e. first: try token negotiated with gh auth; then: try GITHUB_TOKEN etc.

gabe565 commented 1 year ago

Until this is officially supported, I have created a simple gh extension to manage multiple profiles. Check it out at gabe565/gh-profile.

ghost commented 1 year ago

Describe the feature or problem you’d like to solve

I have two accounts on github each with access to a different set of private repositories. Currently I use a combination of git's [IncludeIf ...] config and a custom config which overrides the SSH private key I use for certain repositories. E.g.:

In ~/.gitconfig:

[includeIf "gitdir:~/work/"]
  path = ~/work/.gitconfig

and in ~/work/.gitconfig:

[core]
  sshCommand = "ssh -i ~/.ssh/work"

It will be great if the credentials for gh can also be configured per repository as well.

Proposed solution

There are a bunch of options I can think of:

  • Allow overriding gh config by a file in a local clone's .git directory
  • Allow defining rules in the gh config which defines which repos/orgs should use which credential. E.g.
github.com/google:
  - user: gemployee
    oauth_token: ...

github.com/microsoft:
  - user: msemployee
    oauth_token: ...

github.com:
  - user: personal
    oauth_token: ...

How will it benefit CLI and its users?

It will allow Github users with multiple accounts of varying access to seamlessly work on different repositories.

toan90 commented 1 year ago

Duplicate of #

Smiley23b commented 1 year ago

@eXamadeus GitHub CLI currently has no mechanism for switching between multiple GitHub accounts and I don't really have a workaround to suggest for you at this moment, sorry.

The only approach I could imagine, but would not recommend to anyone, would be to authenticate with 1st account, save a copy of ~/.config/gh/hosts.yml somewhere & delete the original file, authenticate again with the 2nd account, and now you can swap the ~/.config/gh/hosts.yml file with the backup file when you need to switch accounts.

Since this solution involves using SSH for git protocol, make sure both configuration files include the git_protocol: ssh line.

nmnduy commented 1 year ago

Since we can use GH_CONFIG_DIR to specify different gh config dir, I use direnv to set that env var for different projects.

After logging in with gh auth login, I copy .config/gh to my project dir, then use direnv to set GH_CONFIG_DIR to that location.

gabe565 commented 1 year ago

Since we can use GH_CONFIG_DIR to specify different gh config dir, I use direnv to set that env var for different projects.

After logging in with gh auth login, I copy .config/gh to my project dir, then use direnv to set GH_CONFIG_DIR to that location.

I did this too for a while! My gh extension actually takes advantage of direnv, but it sets up the .envrc file for you 😊

thehappycheese commented 1 year ago

Here to add that this is a much desired feature please. constant nightmare switching accounts

ConnorIngold commented 1 year ago

Would also love to see a purpose-built solution added to the CLI please 🙏

cknowles commented 1 year ago

@leonhfr was playing around with this today along with the 1Password CLI op. This seems to have worked for me:

  1. Run gh auth setup-git to get the helpers structure written to ~/.gitconfig
  2. Edit ~/.gitconfig so the helpers point instead at op as follows: helper = !op plugin run -- gh auth git-credential
  3. After that when you want to run the git CLI, it passes you directly through to 1Password - no templating in the gh hosts.yaml needed and multiple accounts stored in 1Password.

The current gh CLI command in item 1 above writes absolute paths to the gh binary without considering aliases that op adds.

ghost commented 1 year ago

Not sure what all the fuss is about. I got this to work fairly easily after purchasing a new computer for each Github account I have. Never had a problem since...

electriquo commented 1 year ago

The issue is opened for a long time and still there is no native resolution. Maybe gh cli extension could be handy, here is one that I found https://github.com/gabe565/gh-profile.

Probably a gh cli extension that updates symlinks for the configuration files is easy to achieve. E.g.

There are many ways to solve it, but I would love to see a native support without any wrappers.

SalBakraa commented 1 year ago

Since no one has brought it up, I would like to bring up the approach Gitlab's cli, glab, took to allow multiple accounts. I don't know if their approach was designed for this, but I found that it works very nicely with way I have already configured ssh.

glab allows you to define an api_host for each host you use. The api_host is simply the hostname of the Gitlab instance glab communicates with. The hostname is only used to for git communication. So if you set the git_protocol as ssh, ssh is what resolves the specified hostname to the correct hostname. The following configuration might clarify what I mean.

glab-cli/config.yml:

hosts:
    work.gitlab.com:
        api_host: gitlab.com
        user: <work user>
        token: <work_token>
        git_protocol: ssh

    personal.gitlab.com:
        api_host: gitlab.com
        user: <personal user>
        token: <personal_token>
        git_protocol: ssh

.ssh/config:

Host personal.gitlab*
    IdentityFile ~/.ssh/ed25519_personal

Host work.gitlab*
    IdentityFile ~/.ssh/ed25519_work

Host *gitlab*
    Hostname gitlab.com
    User git
    IdentitiesOnly yes

In your work projects you would set the remote url as:

git@work.gitlab.com:Work/work_project.git

And in your personal projects you would set the remote url as:

git@personal.gitlab.com:Persnoal/personal_project.git

If you want to define a default user for gitlab, you would then simply have to add gitlab.com as a host into both .ssh/config and hosts.yml.

As you can see, when glab and ssh see the domain name personal.gitlab.com, they communicate with gitlab.com, but they have correctly setup the authentication for the personal user. I think gh would benefit from implementing such a separation between the api hostname and the specified hostname.

As a bonus, if you don't want to type git@personal.gitlab.com everytime you set a remote url and instead prefer persnoal@github.com, add the following lines to your git/config

[url "git@personal.gitlab.com"]
    insteadOf = personal@gitlab.com

This way git auto expands the url personal@gitlab.com to git@personal.github.com.