Tips for working with multiple GitHub accounts
I use GitHub for my work and personal projects with different profiles for
each. Because it's a good security/privacy practice, each profile has its
own distinct SSH key. However, this causes problems because the git CLI
will always try to use the first SSH key that maps to the github.com
domain even if that key has no permissions for the target repository. The
other more straightforward problem with multiple accounts is that the
GitHub.com browser cookie asserts that you are only logged into one account
at a time.
My solutions for these problems are direnv and Firefox
Containers, respectively. These use cases are straightforward
applications of these technologies, so I'm not claiming any innovation here,
but rather it took me a long time to identify these solutions, and I hope
this saves others some time. If you're not familiar with these tools, read
on for details.
Solution 1: Managing multiple GitHub SSH keys with direnv
The git CLI respects a GIT_SSH_COMMAND environment variable, and the ssh
CLI takes a -i flag to specify an "identity file" or private key. If I run
GIT_SSH_COMMAND=$HOME/.ssh/github-personal git push, it will try to push the
current repo using my personal github token. However, I don't want to have to
manage setting and unsetting that variable as I switch between work and
personal repositories; I want that setting to "stick" to each repository.
By-and-large, direnv serves this purpose. It looks for .envrc files
in the shell's current working directory and its ancestor directories and
sources them automatically as the shell's working directory changes or as the
relevant .envrc files are changed. It's widely supported and very easy to
install (brew install direnv) and configure:
# Make sure to add .envrc to your .gitignore!
cd $PERSONAL_REPO && \
echo 'GIT_SSH_COMMAND="ssh -i $HOME/.ssh/github-personal"' >> .envrc && \
direnv allow`
Limitation
This doesn't work if you're using git -C to run commands on a git repository
outside that is not an ancestor of your current working directory, e.g., if you
are in $HOME and your personal repo is $HOME/personal with a direnv envrc
file at $HOME/personal/.envrc, running git -C $HOME/personal push will not
trigger direnv to load your .envrc file because direnv hooks into your
shell, not into the git CLI. To support the git -C usecase, you'll need to
pass the GIT_SSH_COMMAND env var to the git subprocess.
Solution 1 Alternative: the SSH config trick
For a while, I was using the SSH config trick which told ssh to use my
personal SSH key for requests to the host github-personal while using my work
key as the default for requests to github.com. This required me to change my
git config for my private repos to use the github-personal host. This worked
reasonably well until I wanted to write scripts that worked on my MacBook as
well as in a CI environment--I didn't want to parameterize the host because
that's a weird thing to do--it's always going to GitHub, and any tool that
wants to make authenticated requests to GitHub on my behalf would also have to
support this kind of host parameterization.
Solution 2: Using Firefox Containers for multiple accounts
Firefox has a feature called "Containers" which are basically collections of tabs that share the same cookies, history, etc. Each container is sort of its own browser, in a sense--containers are isolated from each other, as the name implies. So I when I log into GitHub using my personal account in the "Personal" container, I can simultaneously be logged into my work GitHub account on the "Work" container (or rather, the default container, as appropriate) because the two containers don't share cookie jars. When I open a new Personal container tab and navigate to github.com, I'm already signed into my personal GH account, and vice versa for my work account in Work container tabs.
Conclusion
I use these tools for lots of other applications as well, including AWS
(including using direnv to set AWS_DEFAULT_PROFILE and
AWS_DEFAULT_REGION for the aws CLI) and the Google suite.
If you have other solutions that you've used for similar problems, or feedback or other suggestions, I'd love to hear them. Reach out via Twitter or email (weberc2 / gmail).