| name | github-auth |
| description | GitHub auth setup: HTTPS tokens, SSH keys, gh CLI login. |
| version | 1.1.0 |
| author | Ouro |
| license | MIT |
| platforms | ["linux","macos","windows"] |
| metadata | {"hermes":{"tags":["GitHub","Authentication","Git","gh-cli","SSH","Setup"],"related_skills":["github-pr-workflow","github-code-review","github-issues","github-repo-management"]}} |
GitHub Authentication Setup
This skill sets up authentication so the agent can work with GitHub repositories, PRs, issues, and CI. It covers two paths:
git (always available) — uses HTTPS personal access tokens or SSH keys
gh CLI (if installed) — richer GitHub API access with a simpler auth flow
Detection Flow
When a user asks you to work with GitHub, run this check first:
git --version
gh --version 2>/dev/null || echo "gh not installed"
gh auth status 2>/dev/null || echo "gh not authenticated"
git config --global credential.helper 2>/dev/null || echo "no git credential helper"
Decision tree:
- If
gh auth status shows authenticated → you're good, use gh for everything
- If
gh is installed but not authenticated → use "gh auth" method below
- If
gh is not installed → use "git-only" method below (no sudo needed)
Method 1: Git-Only Authentication (No gh, No sudo)
This works on any machine with git installed. No root access needed.
Option A: HTTPS with Personal Access Token (Recommended)
This is the most portable method — works everywhere, no SSH config needed.
Step 1: Create a personal access token
Tell the user to go to: https://github.com/settings/tokens
- Click "Generate new token (classic)"
- Give it a name like "ouro-agent"
- Select scopes:
repo (full repository access — read, write, push, PRs)
workflow (trigger and manage GitHub Actions)
read:org (if working with organization repos)
- Set expiration (90 days is a good default)
- Copy the token — it won't be shown again
Step 2: Configure git to store the token
git config --global credential.helper store
git ls-remote https://github.com/<their-username>/<any-repo>.git
After entering credentials once, they're saved and reused for all future operations.
Alternative: cache helper (credentials expire from memory)
git config --global credential.helper 'cache --timeout=28800'
Alternative: set the token directly in the remote URL (per-repo)
git remote set-url origin https://<username>:<token>@github.com/<owner>/<repo>.git
Step 3: Configure git identity
git config --global user.name "Their Name"
git config --global user.email "their-email@example.com"
Step 4: Verify
git ls-remote https://github.com/<their-username>/<any-repo>.git
git config --global user.name
git config --global user.email
Option B: SSH Key Authentication
Good for users who prefer SSH or already have keys set up.
Step 1: Check for existing SSH keys
ls -la ~/.ssh/id_*.pub 2>/dev/null || echo "No SSH keys found"
Step 2: Generate a key if needed
ssh-keygen -t ed25519 -C "their-email@example.com" -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub
Tell the user to add the public key at: https://github.com/settings/keys
- Click "New SSH key"
- Paste the public key content
- Give it a title like "hermes-agent-"
Step 3: Test the connection
ssh -T git@github.com
Step 4: Configure git to use SSH for GitHub
git config --global url."git@github.com:".insteadOf "https://github.com/"
Step 5: Configure git identity
git config --global user.name "Their Name"
git config --global user.email "their-email@example.com"
Method 2: gh CLI Authentication
If gh is installed, it handles both API access and git credentials in one step.
Interactive Browser Login (Desktop)
PITFALL (agent-driven sessions on Windows): when driving gh auth login through a pty background process, answer prompts with process(submit) — never process(write) with a bare \n. Enter on a Windows PTY (ConPTY/pywinpty) is a carriage return; a lone \n is not delivered as a line terminator, so gh's "Press Enter to open the browser" prompt (a blocking line read) silently never returns and the login hangs. Also note the browser may not open on the user's desktop from a background session — if they report that, fall back to the device flow below.
gh auth login
Manual OAuth Device Flow (no TTY needed — PROVEN)
Fallback when interactive login is impractical (agent-driven sessions, no browser launch, headless). Uses gh's public OAuth client id; the user just enters a code at github.com/login/device. Scopes: repo,read:org,gist is the documented minimum for gh auth login --with-token; append ,workflow only if you need to push workflow files.
RESP=$(curl -s -X POST -H "Accept: application/json" \
-d "client_id=178c6fc778ccc68e1d6a&scope=repo,read:org,gist" \
https://github.com/login/device/code)
DEVICE_CODE=$(echo "$RESP" | sed 's/.*"device_code":"\([^"]*\)".*/\1/')
USER_CODE=$(echo "$RESP" | sed 's/.*"user_code":"\([^"]*\)".*/\1/')
INTERVAL=$(echo "$RESP" | sed 's/.*"interval":\([0-9]*\).*/\1/'); INTERVAL=${INTERVAL:-5}
echo "Tell the user: go to https://github.com/login/device and enter code: $USER_CODE"
while true; do
sleep "$INTERVAL"
POLL=$(curl -s -X POST -H "Accept: application/json" \
-d "client_id=178c6fc778ccc68e1d6a&device_code=${DEVICE_CODE}&grant_type=urn:ietf:params:oauth:grant-type:device_code" \
https://github.com/login/oauth/access_token)
case "$POLL" in
*access_token*)
echo "$POLL" | sed 's/.*"access_token":"\([^"]*\)".*/\1/' | 20 gh auth login --with-token \
|| { ; 1; }
gh auth setup-git
gh auth status
; ;;
*authorization_pending*) ;;
*slow_down*) INTERVAL=$((INTERVAL + )) ;;
*expired_token*) ; 1 ;;
*access_denied*) ; 1 ;;
*) ; 1 ;;
Note: on Windows winget installs, gh lands at /c/Program Files/GitHub CLI — add it to PATH in the same shell: export PATH="$PATH:/c/Program Files/GitHub CLI".
PITFALL (headless Linux): gh auth login --with-token can hang forever.
On keyring-less/headless boxes (VPS, containers, no dbus session), gh's
credential storage may block indefinitely waiting on a secret-service
keyring — even with --insecure-storage, and with no output. If the
command doesn't return within ~20s (wrap it in timeout 20 … to detect
this), skip gh's login machinery and write the credential store directly:
mkdir -p ~/.config/gh
LOGIN=$(curl -s -H "Authorization: token $TOKEN" https://api.github.com/user \
| sed 's/.*"login": *"\([^"]*\)".*/\1/')
printf 'github.com:\n users:\n %s:\n oauth_token: %s\n git_protocol: https\n oauth_token: %s\n user: %s\n' \
"$LOGIN" "$TOKEN" "$TOKEN" "$LOGIN" > ~/.config/gh/hosts.yml
chmod 600 ~/.config/gh/hosts.yml
gh auth status
gh auth setup-git
gh auth status and setup-git read the file store without touching the
keyring, so they work immediately. Proven on a headless x86_64 VPS
(gh 2.97.0, Aug 2026) after --with-token hung twice.
Token-Based Login (Headless / SSH Servers)
echo "<THEIR_TOKEN>" | gh auth login --with-token
gh auth setup-git
If --with-token hangs here, use the hosts.yml fallback from the pitfall above.
Verify
gh auth status
Using the GitHub API Without gh
When gh is not available, you can still access the full GitHub API using curl with a personal access token. This is how the other GitHub skills implement their fallbacks.
Setting the Token for API Calls
export GITHUB_TOKEN="<token>"
curl -s -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/user
Extracting the Token from Git Credentials
If git credentials are already configured (via credential.helper store), the token can be extracted:
uv run python "${OURO_HOME:-$HOME/.ouro}/skills/github/github-auth/scripts/git-credential-token.py"
Helper: Detect Auth Method
Use this pattern at the start of any GitHub workflow:
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
echo "AUTH_METHOD=gh"
elif [ -n "$GITHUB_TOKEN" ]; then
echo "AUTH_METHOD=curl"
elif _hermes_env="${OURO_HOME:-$HOME/.ouro}/.env"; [ -f "$_hermes_env" ] && grep -q "^GITHUB_TOKEN=" "$_hermes_env"; then
export GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" "$_hermes_env" | head -1 | cut -d= -f2 | tr -d '\n\r')
echo "AUTH_METHOD=curl"
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
export GITHUB_TOKEN=$(uv run python "${OURO_HOME:-$HOME/.ouro}/skills/github/github-auth/scripts/git-credential-token.py")
echo "AUTH_METHOD=curl"
else
echo "AUTH_METHOD=none"
echo "Need to set up authentication first"
fi
Troubleshooting
| Problem | Solution |
|---|
git push asks for password | GitHub disabled password auth. Use a personal access token as the password, or switch to SSH |
remote: Permission to X denied | Token may lack repo scope — regenerate with correct scopes |
fatal: Authentication failed | Cached credentials may be stale — run git credential reject then re-authenticate |
ssh: connect to host github.com port 22: Connection refused | Try SSH over HTTPS port: add Host github.com with Port 443 and Hostname ssh.github.com to ~/.ssh/config |
| Credentials not persisting | Check git config --global credential.helper — must be store or cache |
| Multiple GitHub accounts | Use SSH with different keys per host alias in ~/.ssh/config, or per-repo credential URLs |
gh: command not found + no sudo | Use git-only Method 1 above — no installation needed |