はじめに
先日GitHubのトークン期限が切れたたため、macOSで git push 時に以下のエラーが出ました。
remote: Invalid username or token.
Password authentication is not supported for Git operations.
fatal: Authentication failed for 'https://github.com/...'
PAT(Personal Access Token)を再発行し、キーチェーンアクセスから古い認証情報を削除しても解決しませんで、「ん?」となりました。
結論、credential helperの優先順位を見落としている可能性が高いです。
この記事では、macOS環境でGitが認証情報をどこから取得しているかを整理し、トークン期限切れ時に迷わず対処できるようにします。
Git の認証の仕組み:credential helper
Git自体はトークンを管理しません。認証が必要になると、credential helperという外部プログラムにトークンの取得を委任します。
現在の環境でどのcredential helperが設定されているかは、以下で確認することができます。
$ git config --show-origin --list | grep credential
macOSでgh CLIをインストールしている環境の、典型的な出力は以下です。
file:/Library/Developer/CommandLineTools/usr/share/git-core/gitconfig credential.helper=osxkeychain
file:/Users/username/.gitconfig credential.helper=osxkeychain
file:/Users/username/.gitconfig credential.https://github.com.helper=
file:/Users/username/.gitconfig credential.https://github.com.helper=!/opt/homebrew/bin/gh auth git-credential
注目すべき点が2つあります。
1. credential helper が複数登録されている
osxkeychain(キーチェーン)とgh auth git-credential(gh CLI)の両方が存在します。ホスト固有の設定(credential.https://github.com.helper)は汎用設定(credential.helper)より優先されるため、github.comへの認証ではgh CLIが使われます。
2. 空の helper でリセットされている
credential.https://github.com.helper=(値が空)の行は、「github.comに対して、これより前に定義された汎用helperをリセットする」という意味です。これによりosxkeychainが無効化され、直後のgh CLIだけが有効になります。
つまり、github.com への認証はキーチェーンを経由しない。
認証情報の保管場所
macOS + gh CLI環境では、認証情報が以下の場所に存在し得ます。
| 保管場所 | 保存するプログラム | キーチェーンでの表示 | 確認コマンド |
|---|---|---|---|
| キーチェーン | git(osxkeychain) | github.com / Internet password | キーチェーンアクセス.app |
| キーチェーン | gh CLI | gh:github.com / application password | キーチェーンアクセス.app |
| gh CLI 内部 | gh CLI | — | gh auth status |
Internet password と application password
キーチェーンアクセスでgithub.comを検索すると、2種類のエントリが見つかることがあります。
- Internet password(
github.com):git が osxkeychain helper 経由で保存したトークン - application password(
gh:github.com):gh CLI が保存したトークン
どちらも PATのキャッシュであり、原本はGitHub側にあります。キーチェーンから削除しても GitHub上のトークン自体は無効化されません。逆に、GitHub上でトークンを失効させても、キーチェーンのキャッシュは残り続けます(ただし認証は失敗します)。
osxkeychainの検索キーとuseHttpPath
osxkeychain helperは通常、ホスト名 + プロトコル(github.com + https)でエントリを検索します。リポジトリ単位ではありません。
ただし、.gitconfig に credential.useHttpPath=true が設定されている場合、リポジトリのパスまで含めてエントリが作られます。この設定があると、キーチェーンに大量のエントリが生まれる原因になります。
$ git config --get credential.useHttpPath
true # この設定があるとリポジトリ単位でエントリが作られる
PAT の種類:ClassicとFine-grained
実はGitHubのPATには2種類あります、、!
| Classic | Fine-grained | |
|---|---|---|
| トークンの prefix | ghp_ | github_pat_ |
| 権限の粒度 | スコープ単位(repo, workflow 等) | リポジトリ + 権限の組み合わせ |
| リポジトリ制限 | 全リポジトリにアクセス可 | 指定したリポジトリのみ |
| 有効期限 | 任意(無期限も可) | 必須(最大1年) |
Fine-grained tokenはリポジトリ単位でアクセスを制限できるため、セキュリティ面で推奨されています。ただし、gh auth loginで使う場合は Classic tokenのほうが設定がシンプルです。
なぜキーチェーンを消しても直らないのか
credential helperの優先順位を踏まえると、原因が見えてきます。
git push
→ credential.https://github.com.helper を参照
→ 空の helper で osxkeychain をリセット
→ gh auth git-credential が応答
→ gh CLI 内部の古いトークンを返す
→ 認証失敗
キーチェーンのInternet passwordを全削除しても、そもそもgithub.comへの認証でキーチェーンは参照されていません。gh CLIが応答しているので、gh CLI側のトークンを更新する必要があります。
実際に使われているトークンを確認する
$ printf "host=github.com\nprotocol=https" | git credential fill
protocol=https
host=github.com
username=your-username
password=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ここで返されるpassword が、git pushで実際に使われるトークンです。GitHubの Token settings でこのトークンが有効か確認できます。
対処方法
gh CLIがcredential helperの場合(大半の環境)
$ gh auth login
? What account do you want to log into? GitHub.com
? What is your preferred protocol for Git operations? HTTPS
? How would you like to authenticate? Paste an authentication token
? Paste your authentication token: ****************************************
✓ Logged in as your-username
正しく更新されたか確認します。
$ gh auth status
github.com
✓ Logged in to github.com account your-username
✓ Git operations for github.com configured to use https protocol.
✓ Token: ghp_****
✓ Token scopes: 'gist', 'read:org', 'repo', 'workflow'
osxkeychainのみの場合
# 古いエントリを削除
$ printf "host=github.com\nprotocol=https\n" | git credential-osxkeychain erase
# 次回の git push でトークン入力を求められ、自動保存される
トークン期限切れ時の対処フロー
毎回迷わないために、以下の3ステップで対処します。
1. credential helperを確認する
$ git config --show-origin --list | grep credential
2. 実際に使われているトークンを確認する
$ printf "host=github.com\nprotocol=https" | git credential fill
3. 該当する helper のトークンを更新する
- gh CLI → gh auth login
- osxkeychain → credential-osxkeychain erase → git push で再入力
まとめ
- Gitはcredential helperに認証を委任しており、自身ではトークンを管理しません
- gh CLIが優先されている環境では、キーチェーンを削除しても認証は変わりません
git config --list | grep credentialで委任先を確認するのが最初の一手です- gh CLI 環境なら
gh auth loginの一行で解決します