DEV Community

vast cow
vast cow

Posted on

Using Multiple GitHub Accounts: Automatically Select the Correct `gh` PAT Based on Repo Owner

When using multiple GitHub accounts, the process of selecting the correct Personal Access Token (PAT) for git push or git pull over HTTPS can be a bit cumbersome.

Specifically, when dealing with repositories from multiple owners, such as:

https://github.com/aont/foo.git
https://github.com/another-user/bar.git
Enter fullscreen mode Exit fullscreen mode

Git Credential Manager or gh auth git-credential might default to the currently active account, leading to errors like:

remote: Permission to owner/repo.git denied to other-user.
Enter fullscreen mode Exit fullscreen mode

To address this, the owner portion of the remote URL is interpreted as the GitHub CLI account name, and a credential helper is embedded directly into the .gitconfig file. This helper retrieves the PAT using:

gh auth token --user OWNER
Enter fullscreen mode Exit fullscreen mode

and provides it to Git.

This eliminates the need for external scripts.

Prerequisites

First, ensure that you are logged into your GitHub accounts using gh:

gh auth login
Enter fullscreen mode Exit fullscreen mode

If you have multiple accounts registered, you can verify them with:

gh auth status
Enter fullscreen mode Exit fullscreen mode

With this method, if a repository URL is:

https://github.com/aont/foo.git
Enter fullscreen mode Exit fullscreen mode

the command:

gh auth token --user aont
Enter fullscreen mode Exit fullscreen mode

will be executed.

Therefore, the basic assumption is:

repository owner = gh registered account name
Enter fullscreen mode Exit fullscreen mode

.gitconfig

The configuration should be as follows:

[credential]
    useHttpPath = true

[credential "https://github.com/"]
    helper =
    helper = "!f() { \
        [ \"$1\" = get ] || exit 0; \
        account=''; \
        while IFS='=' read -r k v; do \
            [ \"$k\" = path ] && account=${v%%/*}; \
        done; \
        [ -n \"$account\" ] || exit 0; \
        token=$(gh auth token --user \"$account\") || exit 0; \
        printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
    }; f"

[credential "https://gist.github.com/"]
    helper =
    helper = "!f() { \
        [ \"$1\" = get ] || exit 0; \
        account=''; \
        while IFS='=' read -r k v; do \
            [ \"$k\" = path ] && account=${v%%/*}; \
        done; \
        [ -n \"$account\" ] || exit 0; \
        token=$(gh auth token --user \"$account\") || exit 0; \
        printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
    }; f"
Enter fullscreen mode Exit fullscreen mode

There are three key points:

credential.useHttpPath = true is Important

Typically, Git's credential helper receives only:

protocol=https
host=github.com
Enter fullscreen mode Exit fullscreen mode

However, in this case, we need:

aont/foo.git
Enter fullscreen mode Exit fullscreen mode

the path.

Therefore, set:

[credential]
    useHttpPath = true
Enter fullscreen mode Exit fullscreen mode

This causes the path to be passed to the credential helper's standard input, such as:

protocol=https
host=github.com
path=aont/foo.git
Enter fullscreen mode Exit fullscreen mode

Use the Beginning of the Path as the Account

Inside the helper, the first element of the path is extracted using:

[ "$k" = path ] && account=${v%%/*}
Enter fullscreen mode Exit fullscreen mode

For example, if:

path=aont/foo.git
Enter fullscreen mode Exit fullscreen mode

then:

account=aont
Enter fullscreen mode Exit fullscreen mode

After that, the corresponding PAT for that account is retrieved from the GitHub CLI using:

token=$(gh auth token --user "$account")
Enter fullscreen mode Exit fullscreen mode

Therefore, when accessing:

https://github.com/aont/foo.git
Enter fullscreen mode Exit fullscreen mode

the command:

gh auth token --user aont
Enter fullscreen mode Exit fullscreen mode

is automatically executed.

Conversely, for:

https://github.com/another-user/bar.git
Enter fullscreen mode Exit fullscreen mode

the command:

gh auth token --user another-user
Enter fullscreen mode Exit fullscreen mode

is executed.

There is no need to run gh auth switch every time.

Reset Existing Credential Helpers with helper =

Another important point is:

helper =
Enter fullscreen mode Exit fullscreen mode

On Git for Windows, for example, Git Credential Manager or gh auth git-credential might already be configured.

If another helper returns the credentials first, this helper might not be executed.

As a result, you might encounter an error like:

remote: Permission to foo/bar.git denied to wrong-account.
Enter fullscreen mode Exit fullscreen mode

Therefore, use:

helper =
helper = "!f() { ... }; f"
Enter fullscreen mode Exit fullscreen mode

The first empty helper = resets any previously configured credential helpers, and then only this helper is registered.

This is crucial when dealing with multiple GitHub accounts.

Use the Same Mechanism for Gists

The same credential helper can be configured for Gists.

However, the standard Gist clone URL is:

https://gist.github.com/GIST_ID.git
Enter fullscreen mode Exit fullscreen mode

and the username cannot be determined from the URL.

Therefore, in this setup, the Gist remote URL is intentionally formatted as:

https://gist.github.com/USER/GIST_ID.git
Enter fullscreen mode Exit fullscreen mode

For example, if:

https://gist.github.com/aont/0123456789abcdef.git
Enter fullscreen mode Exit fullscreen mode

then, from the credential helper's perspective:

path=aont/0123456789abcdef.git
Enter fullscreen mode Exit fullscreen mode

so, the command:

gh auth token --user aont
Enter fullscreen mode Exit fullscreen mode

is automatically used.

This allows you to treat GitHub repositories and Gists with the same rules.

github.com/USER/REPO
gist.github.com/USER/GIST
                ↓
             USER is extracted
                ↓
gh auth token --user USER
Enter fullscreen mode Exit fullscreen mode

Verification

You can check which credentials Git is actually retrieving using git credential fill.

For example, run:

printf '%s\n' \
  'protocol=https' \
  'host=github.com' \
  'path=aont/foo.git' \
  '' |
git credential fill
Enter fullscreen mode Exit fullscreen mode

The expected output is:

protocol=https
host=github.com
username=aont
password=...
Enter fullscreen mode Exit fullscreen mode

If the username is a different account, you can check if another credential helper is still present using:

git config --show-origin --get-all credential.helper
Enter fullscreen mode Exit fullscreen mode

Why Not Use gh auth git-credential Directly?

The GitHub CLI has a standard command:

gh auth git-credential
Enter fullscreen mode Exit fullscreen mode

This is sufficient for normal single-account usage.

However, when using multiple accounts with the same host (github.com), you might want to explicitly control which gh account is used based on the repository owner.

This helper uses a very simple rule:

remote URL
  ↓
owner is extracted
  ↓
gh auth token --user owner
Enter fullscreen mode Exit fullscreen mode

so you don't need to be aware of which account is currently active in gh.

Advantages of This Configuration

This method does not save the PAT itself in the .gitconfig file.

The PAT is retrieved each time it is needed using:

gh auth token --user ACCOUNT
Enter fullscreen mode Exit fullscreen mode

Therefore, the configuration only saves the rule for which account to use.

Additionally, you do not need to run:

gh auth switch
Enter fullscreen mode Exit fullscreen mode

or set:

git config credential.username ...
Enter fullscreen mode Exit fullscreen mode

for each repository.

The remote URL itself becomes the credential routing information.

Summary

When using multiple GitHub accounts over HTTPS, using the account portion of:

github.com/<account>/<repo>
Enter fullscreen mode Exit fullscreen mode

directly for selecting the gh account can simplify operations.

The mechanism is:

Git remote URL
  ↓
credential.useHttpPath
  ↓
path=owner/repo.git
  ↓
owner is extracted
  ↓
gh auth token --user owner
  ↓
username/password is returned to Git
Enter fullscreen mode Exit fullscreen mode

If you have multiple personal accounts and the relationship:

repository owner = GitHub account
Enter fullscreen mode Exit fullscreen mode

holds true, this method is quite easy to use.

For organization-owned repositories where:

owner != account used for authentication
Enter fullscreen mode Exit fullscreen mode

you will need separate mappings, but this configuration is sufficient for primarily personal accounts.

GitHub複数アカウント運用で、repo ownerから自動的にghのPATを選ぶ

GitHubで複数アカウントを使っていると、HTTPSでのgit pushgit pull時に「どのアカウントのPATを使うか」が地味に面倒です。

特に、

https://github.com/aont/foo.git
https://github.com/another-user/bar.git
Enter fullscreen mode Exit fullscreen mode

のように複数ownerのrepositoryを扱っていると、Git Credential Managerやgh auth git-credentialが現在のアクティブアカウントを使ってしまい、

remote: Permission to owner/repo.git denied to other-user.
Enter fullscreen mode Exit fullscreen mode

のようなエラーになることがあります。

そこで、remote URLのowner部分をそのままGitHub CLIのアカウント名として解釈し、

gh auth token --user OWNER
Enter fullscreen mode Exit fullscreen mode

からPATを取り出してGitに返すcredential helperを.gitconfigへ直接埋め込みます。

外部スクリプトも不要です。

前提

まず、利用するGitHubアカウントはあらかじめghにログインしておきます。

gh auth login
Enter fullscreen mode Exit fullscreen mode

複数アカウントを登録している場合は、

gh auth status
Enter fullscreen mode Exit fullscreen mode

で確認できます。

この方法では、たとえばrepository URLが

https://github.com/aont/foo.git
Enter fullscreen mode Exit fullscreen mode

なら、

gh auth token --user aont
Enter fullscreen mode Exit fullscreen mode

を実行します。

したがって、基本的には

repository owner = ghに登録したaccount名
Enter fullscreen mode Exit fullscreen mode

という運用を前提にします。

.gitconfig

設定は次のようにします。

[credential]
    useHttpPath = true

[credential "https://github.com/"]
    helper =
    helper = "!f() { \
        [ \"$1\" = get ] || exit 0; \
        account=''; \
        while IFS='=' read -r k v; do \
            [ \"$k\" = path ] && account=${v%%/*}; \
        done; \
        [ -n \"$account\" ] || exit 0; \
        token=$(gh auth token --user \"$account\") || exit 0; \
        printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
    }; f"

[credential "https://gist.github.com/"]
    helper =
    helper = "!f() { \
        [ \"$1\" = get ] || exit 0; \
        account=''; \
        while IFS='=' read -r k v; do \
            [ \"$k\" = path ] && account=${v%%/*}; \
        done; \
        [ -n \"$account\" ] || exit 0; \
        token=$(gh auth token --user \"$account\") || exit 0; \
        printf 'username=%s\\npassword=%s\\n' \"$account\" \"$token\"; \
    }; f"
Enter fullscreen mode Exit fullscreen mode

ポイントは3つあります。

credential.useHttpPath = true が重要

通常、Gitのcredential helperには

protocol=https
host=github.com
Enter fullscreen mode Exit fullscreen mode

程度しか渡されません。

しかし今回必要なのは、

aont/foo.git
Enter fullscreen mode Exit fullscreen mode

というpathです。

そこで、

[credential]
    useHttpPath = true
Enter fullscreen mode Exit fullscreen mode

を設定します。

これによってcredential helperの標準入力に、

protocol=https
host=github.com
path=aont/foo.git
Enter fullscreen mode Exit fullscreen mode

のようにpathも渡されます。

pathの先頭をaccountとして使う

helper内では、

[ "$k" = path ] && account=${v%%/*}
Enter fullscreen mode Exit fullscreen mode

として、pathの最初の要素を取得しています。

たとえば、

path=aont/foo.git
Enter fullscreen mode Exit fullscreen mode

なら、

account=aont
Enter fullscreen mode Exit fullscreen mode

になります。

その後、

token=$(gh auth token --user "$account")
Enter fullscreen mode Exit fullscreen mode

で、そのアカウントに対応するPATをGitHub CLIから取得します。

つまり、

https://github.com/aont/foo.git
Enter fullscreen mode Exit fullscreen mode

へのアクセス時は自動的に、

gh auth token --user aont
Enter fullscreen mode Exit fullscreen mode

相当になります。

一方、

https://github.com/another-user/bar.git
Enter fullscreen mode Exit fullscreen mode

なら、

gh auth token --user another-user
Enter fullscreen mode Exit fullscreen mode

になります。

gh auth switchを毎回実行する必要はありません。

helper = で既存credential helperをリセットする

もう一つ重要なのが、

helper =
Enter fullscreen mode Exit fullscreen mode

です。

Git for Windowsなどでは、すでにGit Credential Managerやgh auth git-credentialなどが設定されている場合があります。

たとえば別のhelperが先にcredentialを返すと、今回のhelperまで処理が回ってきません。

その結果、

remote: Permission to foo/bar.git denied to wrong-account.
Enter fullscreen mode Exit fullscreen mode

のようになります。

そこで、

helper =
helper = "!f() { ... }; f"
Enter fullscreen mode Exit fullscreen mode

とします。

最初の空のhelper =で、それ以前に設定されていたcredential helperをリセットし、その後に今回のhelperだけを登録しています。

複数GitHubアカウントを扱う場合には、この部分がかなり重要です。

Gistにも同じ仕組みを使う

Gistにも同じcredential helperを設定できます。

ただし通常のGist clone URLは、

https://gist.github.com/GIST_ID.git
Enter fullscreen mode Exit fullscreen mode

となっていて、URLからユーザー名を判断できません。

そこで、この運用ではGistのremote URLを意図的に、

https://gist.github.com/USER/GIST_ID.git
Enter fullscreen mode Exit fullscreen mode

という形式にします。

たとえば、

https://gist.github.com/aont/0123456789abcdef.git
Enter fullscreen mode Exit fullscreen mode

なら、credential helperから見ると、

path=aont/0123456789abcdef.git
Enter fullscreen mode Exit fullscreen mode

となるので、

gh auth token --user aont
Enter fullscreen mode Exit fullscreen mode

を自動的に使えます。

つまりGitHub repositoryとGistを同じルールで扱えます。

github.com/USER/REPO
gist.github.com/USER/GIST
                ↓
             USERを抽出
                ↓
gh auth token --user USER
Enter fullscreen mode Exit fullscreen mode

動作確認

Gitが実際にどのcredentialを取得するかは、git credential fillで確認できます。

たとえば、

printf '%s\n' \
  'protocol=https' \
  'host=github.com' \
  'path=aont/foo.git' \
  '' |
git credential fill
Enter fullscreen mode Exit fullscreen mode

を実行します。

期待する出力は、

protocol=https
host=github.com
username=aont
password=...
Enter fullscreen mode Exit fullscreen mode

です。

ここでusernameが別アカウントになっている場合は、

git config --show-origin --get-all credential.helper
Enter fullscreen mode Exit fullscreen mode

で、別のcredential helperが残っていないか確認するとよいです。

なぜgh auth git-credentialをそのまま使わないのか

GitHub CLIには標準で、

gh auth git-credential
Enter fullscreen mode Exit fullscreen mode

があります。

通常の単一アカウント運用ならこれで十分です。

ただ、複数アカウントを同一ホストgithub.comで使っている場合、「repository ownerに応じてどのgh accountを使うか」を明示的に制御したくなります。

今回のhelperでは、

remote URL
    ↓
ownerを抽出
    ↓
gh auth token --user owner
Enter fullscreen mode Exit fullscreen mode

という非常に単純な規則にしているため、現在どのアカウントがghでactiveになっているかを意識する必要がありません。

この構成の利点

この方法だとPATそのものを.gitconfigに保存しません。

PATは必要になるたびに、

gh auth token --user ACCOUNT
Enter fullscreen mode Exit fullscreen mode

から取得します。

そのため、設定として保存されるのは「どのアカウントを使うか」というルールだけです。

また、repositoryごとに、

gh auth switch
Enter fullscreen mode Exit fullscreen mode

したり、

git config credential.username ...
Enter fullscreen mode Exit fullscreen mode

を設定したりする必要もありません。

remote URLそのものがcredential routingの情報になります。

まとめ

複数GitHubアカウントをHTTPSで使う場合、

github.com/<account>/<repo>
Enter fullscreen mode Exit fullscreen mode

account部分をそのままghのaccount選択に使うと、かなりシンプルに運用できます。

仕組みとしては、

Git remote URL
  ↓
credential.useHttpPath
  ↓
path=owner/repo.git
  ↓
ownerを抽出
  ↓
gh auth token --user owner
  ↓
username/passwordとしてGitへ返す
Enter fullscreen mode Exit fullscreen mode

だけです。

個人アカウントを複数使っていて、

repository owner = GitHub account
Enter fullscreen mode Exit fullscreen mode

という関係が成立している環境なら、かなり扱いやすい方法だと思います。

Organization配下のrepositoryなどで、

owner != 認証に使うaccount
Enter fullscreen mode Exit fullscreen mode

となる場合だけは別途マッピングが必要ですが、個人アカウント中心ならまずこの構成で十分です。

Top comments (1)

Collapse
 
mrdgh2821 profile image
Mihir Rabade

What about repos in an organization?