Skip to content

Commit bbde1e2

Browse files
authored
Merge pull request #27 from link-foundation/issue-26-781e33b48714
docs: add case study and multi-environment usage documentation for token revocation
2 parents c35bcba + ba1d017 commit bbde1e2

File tree

7 files changed

+744
-0
lines changed

7 files changed

+744
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"gh-setup-git-identity": patch
3+
---
4+
5+
Add case study documentation and multi-environment usage warning for GitHub OAuth token limits
6+
7+
- Add comprehensive case study for OAuth token invalidation issue (#26)
8+
- Update README.md with multi-environment usage section documenting the 10-token limit
9+
- Add CLI warning when OAuth device flow authentication is triggered

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,50 @@ Get current git identity configuration.
317317

318318
**Returns:** `Promise<{username: string|null, email: string|null}>`
319319

320+
## Multi-Environment Usage
321+
322+
### Important: GitHub OAuth Token Limits
323+
324+
GitHub limits OAuth tokens to **10 per user/application/scope combination**. If you use `gh-setup-git-identity` on more than 10 machines or environments, the oldest tokens will be automatically revoked by GitHub, causing authentication failures on those machines.
325+
326+
For more details, see the [GitHub documentation on token expiration and revocation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation).
327+
328+
### Recommended: Use Personal Access Tokens (PATs)
329+
330+
For multi-environment setups (Docker containers, CI/CD, multiple servers), we recommend using Personal Access Tokens instead of the OAuth device flow:
331+
332+
1. Create a PAT at https://github.com/settings/tokens with these scopes:
333+
- `repo`, `workflow`, `user`, `read:org`, `gist`
334+
335+
2. Authenticate using the token:
336+
337+
```bash
338+
# Option 1: Via stdin
339+
echo "ghp_your_token_here" | gh-setup-git-identity --with-token
340+
341+
# Option 2: Via environment variable (recommended for automation)
342+
export GH_TOKEN="ghp_your_token_here"
343+
gh-setup-git-identity
344+
```
345+
346+
### Why PATs Are Better for Multi-Environment
347+
348+
- **No token limit**: PATs don't count toward the 10-token OAuth limit
349+
- **Consistent authentication**: Same token works across all environments
350+
- **Explicit control**: You manage the token lifecycle
351+
- **CI/CD friendly**: Easy to inject as a secret
352+
353+
### Revoking Old OAuth Tokens
354+
355+
If you've accumulated OAuth tokens and need to clean up:
356+
357+
1. Go to **GitHub Settings** > **Applications** > **Authorized OAuth Apps**
358+
2. Find "GitHub CLI" in the list
359+
3. Click **Revoke** to remove all OAuth tokens
360+
4. Re-authenticate with `gh-setup-git-identity`
361+
362+
For a detailed case study of this issue, see [docs/case-studies/issue-26/](./docs/case-studies/issue-26/).
363+
320364
## Configuration
321365

322366
### Environment Variables
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
# Case Study: GitHub OAuth Token Invalidation and Prevention Strategies (Issue #26)
2+
3+
## Executive Summary
4+
5+
This case study investigates the GitHub OAuth token invalidation issue experienced when using `gh-setup-git-identity` across multiple environments. The investigation reveals that this behavior is a documented GitHub security feature, not a bug. When more than 10 OAuth tokens exist for the same user/application/scope combination, GitHub automatically revokes the oldest tokens.
6+
7+
This document explores the root causes, reconstructs the timeline of events, and proposes solutions including potential modifications to `gh-setup-git-identity` to mitigate token accumulation.
8+
9+
## Issue Description
10+
11+
**Issue URL:** https://github.com/link-foundation/gh-setup-git-identity/issues/26
12+
13+
**Related Analysis:** https://github.com/link-assistant/hive-mind/pull/1022 (Issue #1021)
14+
15+
**Reported Behavior:**
16+
> At the same time I was executing `gh-setup-git-identity` command on the remote server. And for some reason executing `gh-setup-git-identity` remotely did destroy GitHub Auth session locally in docker. That is strange.
17+
18+
When running `gh-setup-git-identity` on multiple machines, authentication sessions on other machines become invalid. This creates a "musical chairs" effect where the most recent authentication causes the oldest one to fail.
19+
20+
## Root Cause Analysis
21+
22+
### Primary Root Cause: GitHub's OAuth Token Limits
23+
24+
From the [official GitHub documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation):
25+
26+
> "There is a limit of ten tokens that are issued per user/application/scope combination, and a rate limit of ten tokens created per hour. If an application creates more than ten tokens for the same user and the same scopes, the oldest tokens with the same user/application/scope combination are revoked."
27+
28+
### Secondary Cause: gh auth login Token Accumulation
29+
30+
When `gh auth login` is executed (which `gh-setup-git-identity` uses internally), it:
31+
32+
1. Creates a new OAuth token through GitHub's device code flow
33+
2. Stores the new token in the local credentials store
34+
3. **Does NOT revoke the previous token** on GitHub's side
35+
36+
From [GitHub CLI Issue #9233](https://github.com/cli/cli/issues/9233):
37+
> "Whenever an already logged-in user runs a gh auth login or gh auth refresh, a new OAuth app token is generated and replaces the previous token in the credentials store. The problem is that the old token is not revoked during this process."
38+
39+
This is currently a known limitation and marked as "blocked" because the GitHub platform doesn't provide an API for the CLI to revoke its own tokens.
40+
41+
### How These Combine to Cause the Issue
42+
43+
```
44+
Timeline of Token Accumulation:
45+
46+
Machine A (Docker) Machine B (Server) Machine C (Local)
47+
| | |
48+
v v v
49+
Token #1 Token #2 Token #3
50+
| | |
51+
v v v
52+
Token #4 Token #5 Token #6
53+
| | |
54+
v v v
55+
Token #7 Token #8 Token #9
56+
| | |
57+
| v |
58+
| Token #10 |
59+
| | |
60+
| v |
61+
| gh-setup-git-identity |
62+
| (creates Token #11) |
63+
| | |
64+
v v v
65+
Token #1 Token #11 Token #3
66+
REVOKED! (newest - valid) (still valid)
67+
| | |
68+
X | |
69+
Machine A | |
70+
auth FAILS! | |
71+
```
72+
73+
## Timeline of Events
74+
75+
Based on the log file from https://gist.github.com/konard/a430347f7f9aff41b7e0c64a7289712a:
76+
77+
| Timestamp (UTC) | Event | Token State |
78+
|-----------------|-------|-------------|
79+
| 09:05:36 | `solve` command started in Docker | Token valid |
80+
| 09:05:42 | GitHub auth check skipped (`--no-tool-check`) | Token valid |
81+
| 09:05:55 | Git push successful to fork | Token valid |
82+
| 09:06:02 | PR created successfully | Token valid |
83+
| ~09:06-09:12 | (Unknown) Remote server runs `gh-setup-git-identity` | **New token created** |
84+
| 09:12:59 | Git push fails: "Invalid username or token" | **Token revoked** |
85+
| 09:13:06 | `gh auth status` confirms token invalid | Token invalid |
86+
87+
The token was valid at 09:05:55 (successful push) but became invalid by 09:12:59 (approximately 7 minutes later), indicating an external event caused the revocation.
88+
89+
## Evidence from Logs
90+
91+
### Successful Authentication (09:05:55)
92+
```
93+
[2025-12-28T09:05:55.246Z] [INFO] Push exit code: 0
94+
[2025-12-28T09:05:55.246Z] [INFO] Push output: remote:
95+
remote: Create a pull request for 'issue-133-434c3df37b90' on GitHub by visiting:
96+
```
97+
98+
### Authentication Failure (09:12:59)
99+
```
100+
Exit code 128
101+
remote: Invalid username or token. Password authentication is not supported for Git operations.
102+
fatal: Authentication failed for 'https://github.com/konard/andchir-install_scripts.git/'
103+
```
104+
105+
### gh auth status Confirmation (09:13:06)
106+
```
107+
github.com
108+
X Failed to log in to github.com account konard (/home/hive/.config/gh/hosts.yml)
109+
- Active account: true
110+
- The token in /home/hive/.config/gh/hosts.yml is invalid.
111+
- To re-authenticate, run: gh auth login -h github.com
112+
```
113+
114+
## Proposed Solutions
115+
116+
### Question from Issue #26
117+
118+
> Can we maybe use our `gh-setup-git-identity` tool to actually revoke all 10 tokens?
119+
120+
**Answer: Not directly through code modification.**
121+
122+
The GitHub CLI itself cannot revoke OAuth tokens programmatically because GitHub's platform doesn't expose an API endpoint for OAuth apps to revoke their own tokens. This is documented in [GitHub CLI Issue #9233](https://github.com/cli/cli/issues/9233).
123+
124+
However, there are several alternative approaches:
125+
126+
### Solution 1: Use Personal Access Tokens (PATs) Instead of OAuth Flow
127+
128+
**Recommended for multi-environment setups**
129+
130+
PATs don't count toward the 10-token OAuth limit and can be shared across environments.
131+
132+
```bash
133+
# Create a PAT at GitHub Settings > Developer settings > Personal access tokens
134+
# Then authenticate:
135+
echo "ghp_your_token_here" | gh-setup-git-identity --with-token
136+
```
137+
138+
**Pros:**
139+
- No token accumulation
140+
- Same token works across all environments
141+
- Explicit control over token lifecycle
142+
143+
**Cons:**
144+
- Requires manual token creation
145+
- Token management responsibility shifts to user
146+
147+
### Solution 2: Revoke Tokens Manually via GitHub Settings
148+
149+
Users can manually clean up old tokens:
150+
151+
1. Go to **GitHub Settings** > **Applications** > **Authorized OAuth Apps**
152+
2. Find "GitHub CLI" entry
153+
3. Click "Revoke" to remove old authorizations
154+
4. Re-authenticate with `gh-setup-git-identity`
155+
156+
### Solution 3: Use GH_TOKEN/GITHUB_TOKEN Environment Variable
157+
158+
For automated/CI environments:
159+
160+
```bash
161+
export GH_TOKEN="ghp_your_personal_access_token"
162+
# or
163+
export GITHUB_TOKEN="ghp_your_personal_access_token"
164+
165+
# gh-setup-git-identity will use this token
166+
gh-setup-git-identity
167+
```
168+
169+
### Solution 4: Add Warning Documentation to gh-setup-git-identity
170+
171+
Add documentation warning users about the 10-token limit when using multiple environments.
172+
173+
### Solution 5: Implement Token Reuse Check (Code Change)
174+
175+
Modify `gh-setup-git-identity` to:
176+
1. Check if current token is valid before triggering new authentication
177+
2. Skip `gh auth login` if already authenticated (currently implemented but can be enhanced)
178+
3. Warn users when they attempt to re-authenticate unnecessarily
179+
180+
### Solution 6: Add Token Revocation Guidance in CLI Output
181+
182+
When authentication is triggered, output a message like:
183+
184+
```
185+
Note: GitHub limits OAuth tokens to 10 per user/application/scope.
186+
If you use multiple environments, consider using Personal Access Tokens instead.
187+
To revoke old tokens: https://github.com/settings/applications
188+
```
189+
190+
## Recommendations
191+
192+
### For Individual Users
193+
194+
1. **For multi-environment setups**: Use Personal Access Tokens (PATs) instead of OAuth device flow
195+
2. **Regularly audit tokens**: Visit GitHub Settings > Applications > Authorized OAuth Apps
196+
3. **Share tokens across environments**: Use `GH_TOKEN` environment variable with a single PAT
197+
198+
### For gh-setup-git-identity Development
199+
200+
1. **Add documentation** about GitHub's 10-token limit in README.md
201+
2. **Add CLI warning** when triggering authentication about token limits
202+
3. **Enhance `--with-token` mode** documentation for multi-environment usage
203+
4. **Consider adding `--revoke-first` flag** that prompts users to revoke via GitHub settings before re-authenticating
204+
205+
## Conclusion
206+
207+
The token invalidation issue is expected behavior due to GitHub's OAuth token management:
208+
209+
1. **GitHub limits OAuth tokens to 10 per user/application/scope combination**
210+
2. **When the limit is exceeded, the oldest tokens are automatically revoked**
211+
3. **`gh auth login` creates new tokens without revoking old ones**
212+
4. **This is a platform limitation, not a bug in `gh-setup-git-identity`**
213+
214+
The recommended mitigation is to use Personal Access Tokens (PATs) for multi-environment setups, which provides better control over token lifecycle and avoids the 10-token OAuth limit.
215+
216+
## References
217+
218+
- [GitHub Token Expiration and Revocation Documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation)
219+
- [GitHub CLI gh auth login Manual](https://cli.github.com/manual/gh_auth_login)
220+
- [GitHub CLI Issue #9233: Invalidate previous OAuth token](https://github.com/cli/cli/issues/9233)
221+
- [GitHub CLI Issue #5924: Support for short-lived OAuth tokens](https://github.com/cli/cli/issues/5924)
222+
- [Original Issue Analysis (hive-mind #1021)](https://github.com/link-assistant/hive-mind/pull/1022)
223+
- [Original Issue Gist Log](https://gist.github.com/konard/a430347f7f9aff41b7e0c64a7289712a)
224+
225+
## Appendix: Related Files
226+
227+
- `timeline.md` - Detailed event timeline reconstruction
228+
- `solutions.md` - Comprehensive solution comparison matrix
229+
- `raw-logs/` - Redacted log excerpts for reference
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Authentication Failure Log Excerpts
2+
3+
Source: https://gist.github.com/konard/a430347f7f9aff41b7e0c64a7289712a
4+
5+
**Note**: OAuth tokens have been redacted for security.
6+
7+
## Successful Push (09:05:55)
8+
9+
```log
10+
[2025-12-28T09:05:54.123Z] [INFO] 📤 Pushing branch: To remote repository...
11+
[2025-12-28T09:05:54.123Z] [INFO] Push command: git push -u origin issue-133-434c3df37b90
12+
[2025-12-28T09:05:55.245Z] [INFO] Push exit code: 0
13+
[2025-12-28T09:05:55.246Z] [INFO] Push output: remote:
14+
remote: Create a pull request for 'issue-133-434c3df37b90' on GitHub by visiting:
15+
remote: https://github.com/konard/andchir-install_scripts/pull/new/issue-133-434c3df37b90
16+
remote:
17+
To https://github.com/konard/andchir-install_scripts.git
18+
* [new branch] issue-133-434c3df37b90 -> issue-133-434c3df37b90
19+
branch 'issue-133-434c3df37b90' set up to track 'origin/issue-133-434c3df37b90'.
20+
[2025-12-28T09:05:55.247Z] [INFO] ✅ Branch pushed: Successfully to remote
21+
```
22+
23+
## First Authentication Failure (09:12:59)
24+
25+
```log
26+
[2025-12-28T09:12:59.875Z]
27+
"content": "Exit code 128\nremote: Invalid username or token. Password authentication is not supported for Git operations.\nfatal: Authentication failed for 'https://github.com/konard/andchir-install_scripts.git/'",
28+
"is_error": true
29+
```
30+
31+
## gh auth status Confirmation (09:13:06)
32+
33+
```log
34+
[2025-12-28T09:13:06.601Z]
35+
"content": "Exit code 1\ngithub.com\n X Failed to log in to github.com account konard (/home/hive/.config/gh/hosts.yml)\n - Active account: true\n - The token in /home/hive/.config/gh/hosts.yml is invalid.\n - To re-authenticate, run: gh auth login -h github.com\n - To forget about this account, run: gh auth logout -h github.com -u konard",
36+
"is_error": true
37+
```
38+
39+
## Subsequent Retry Failures
40+
41+
### Attempt with git push
42+
```log
43+
"content": "remote: Invalid username or token. Password authentication is not supported for Git operations.\nfatal: Authentication failed for 'https://github.com/konard/andchir-install_scripts.git/'\nPush with gh auth token",
44+
```
45+
46+
### Attempt with git credential helper
47+
```log
48+
"content": "Exit code 128\ngit: 'credential-!/usr/bin/gh' is not a git command. See 'git --help'.\nremote: Invalid username or token. Password authentication is not supported for Git operations.\nfatal: Authentication failed for 'https://github.com/konard/andchir-install_scripts.git/'",
49+
```
50+
51+
## AI Recognition of Issue
52+
53+
```log
54+
"text": "The token might be expired or invalid. Let me try using `gh` to push:"
55+
```
56+
57+
```log
58+
"text": "The GitHub CLI token is invalid. I'll need help from the user to authenticate. Let me comment on the PR with my progress and ask for help:"
59+
```
60+
61+
## Key Observations
62+
63+
1. **Token was valid at 09:05:55** - Push succeeded
64+
2. **Token was invalid by 09:12:59** - ~7 minute gap
65+
3. **No token refresh or re-auth occurred** in the Docker container during this time
66+
4. **The token file existed** but was marked as invalid by GitHub
67+
5. **Multiple retry strategies failed** - The token was revoked server-side, not locally
68+
69+
This confirms that an external event (likely `gh-setup-git-identity` running on another machine) caused GitHub to revoke the token during the ~7 minute window.

0 commit comments

Comments
 (0)