SSH-Based Multi-Account GitHub Authentication
- Context: Manjaro Linux with two GitHub accounts (personal + EMU) requiring tooling-independent auth
Problem Statement¶
With two GitHub accounts - a personal one and a work EMU (Enterprise Managed User) account tied to an organisation FOO_ORG - authentication needs to route to the correct account depending on the repository URL. Neither gh auth (supports only one account at a time) nor ksshaskpass for HTTPS (not picked up by all Git GUIs) provides a reliable, tooling-independent solution.
Additionally, GitHub does not allow the same SSH key to be registered on more than one account, so a dedicated key per account is mandatory. SSH keys also have an advantage over fine-grained PATs for EMU organisations: they can be SSO-authorised by the user directly via the GitHub UI, whilst fine-grained PATs require manual approval by an enterprise owner.
Requirements:
- Automatic account selection based on repository URL
- Works with any Git tooling (CLI, Git GUI, IDE, lazygit, etc.)
- No interactive prompts or credential helpers that only work in terminals
Solution (Implemented)¶
Dedicated SSH keys per account, an SSH config with host aliases for key selection and a Git insteadOf rewrite to transparently route URLs.
1. Generate SSH keys¶
Create one key per account, using distinct filenames:
# Personal account
ssh-keygen -t ed25519 -C "$(whoami)@$(uname -n)-$(date -I)" -f ~/.ssh/github
# FOO_ORG EMU account
ssh-keygen -t ed25519 -C "$(whoami)@$(uname -n)-$(date -I)" -f ~/.ssh/github_foo_org
Ensure the keys have correct permissions:
chmod 600 ~/.ssh/github ~/.ssh/github_foo_org
chmod 644 ~/.ssh/github.pub ~/.ssh/github_foo_org.pub
2. Register public keys on GitHub¶
Add each public key to the corresponding GitHub account under Settings → SSH and GPG keys → New SSH key:
# Copy personal key to clipboard
xclip -selection clipboard < ~/.ssh/github.pub
# Copy EMU key to clipboard
xclip -selection clipboard < ~/.ssh/github_foo_org.pub
3. Configure SSH host aliases¶
Add to ~/.ssh/config:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github
IdentitiesOnly yes
Host github-foo-org
HostName github.com
User git
IdentityFile ~/.ssh/github_foo_org
IdentitiesOnly yes
IdentitiesOnly yes prevents the SSH agent from offering other keys, ensuring only the specified key is used for each host alias.
4. Configure Git URL rewriting¶
Add to ~/.gitconfig (or use git config --global):
[url "git@github.com:"]
insteadOf = https://github.com/
[url "git@github-foo-org:FOO_ORG/"]
insteadOf = https://github.com/FOO_ORG/
insteadOf = git@github.com:FOO_ORG/
The first rule rewrites all https://github.com/ URLs to SSH. The second rule is more specific and takes precedence for FOO_ORG URLs (Git applies the longest-matching insteadOf), routing them through the github-foo-org host alias to select the EMU key. All other github.com URLs use the default SSH host entry with the personal key.
This ensures that submodules, dependencies and any other tooling that generates HTTPS URLs will transparently use SSH authentication.
Verification¶
# Test personal key authentication
ssh -T github.com
# → Hi <personal-username>! You've successfully authenticated ...
# Test EMU key authentication
ssh -T github-foo-org
# → Hi <emu-username>! You've successfully authenticated ...
# Verify URL rewriting for an EMU repo
git ls-remote https://github.com/FOO_ORG/some-repo.git
# Should authenticate as the EMU account
# Verify personal repos still work
git ls-remote git@github.com:your-username/some-repo.git
# Should authenticate as the personal account
Extending for Additional Organisations¶
For further organisations on the same EMU account, add insteadOf lines:
[url "git@github.com:"]
insteadOf = https://github.com/
[url "git@github-foo-org:FOO_ORG/"]
insteadOf = https://github.com/FOO_ORG/
insteadOf = git@github.com:FOO_ORG/
[url "git@github-foo-org:ANOTHER_FOO_ORG/"]
insteadOf = https://github.com/ANOTHER_FOO_ORG/
insteadOf = git@github.com:ANOTHER_FOO_ORG/
For a separate account with its own key, add another SSH host alias and corresponding insteadOf block.
How It Works¶
The mechanism operates at two layers:
- Git layer (
insteadOf): Before any network operation, Git rewrites the remote URL. A generic HTTPS URL likehttps://github.com/user/repo.gitbecomesgit@github.com:user/repo.git, whilst a more specific match likehttps://github.com/FOO_ORG/repo.gitbecomesgit@github-foo-org:FOO_ORG/repo.git(Git applies the longest-matchinginsteadOf). - SSH layer (host alias): SSH resolves
github-foo-orgvia~/.ssh/config, connecting togithub.combut presenting the EMU key.
Because this happens at the transport layer, it is invisible to any tooling built on top of Git.
Trade-offs¶
- No per-repo configuration: Account selection is determined solely by the organisation in the URL. Repositories outside the configured organisations always use the personal key.
- HTTPS remotes are transparently converted to SSH: If a workflow strictly requires HTTPS (e.g. behind a corporate proxy that blocks SSH), this approach will not work. In that case, consider Git Credential Manager with its multi-account support.
- SSH agent optional: The keys can be loaded into
ssh-agentfor passphrase caching, but the setup works without it - SSH reads the key files directly.
References¶
- Arch Wiki: SSH keys - key generation, permissions, agent configuration
- GitHub Docs: Generating a new SSH key
- GitHub Docs: Adding a new SSH key to your account
- Git Documentation: url.\<base>.insteadOf
- GitHub Docs: About Enterprise Managed Users
Backlinks¶
The following pages link to this page: