Setting Up GitHub CLI Authentication

The GitHub CLI, or gh, is a command-line interface for interacting with GitHub. It handles authentication differently depending on your operating system and your preference for how tokens are stored. Most people just run the setup command and move on, but the details matter when you're working across multiple machines or in a restricted environment. I'll walk through what actually happens during authentication and the edge cases that trip people up. To get started, you need the tool installed. On macOS it's brew install gh, on Ubuntu/Debian it's sudo apt install gh, and on Windows you can grab it from the GitHub CLI releases page or use winget install GitHub.cli. Once it's there, the setup flow begins with gh auth login. That command opens an interactive prompt asking for your preferred protocol, which host, and how you want to authenticate. The authentication methods available are HTTPS with a personal access token, SSH keys, or browser-based OAuth. HTTPS with a token is the most common. You generate a token directly on GitHub.com under Settings, Developer settings, Personal access tokens. The classic token type gives you the broadest compatibility, while fine-grained tokens let you scope access to specific repositories. For general workflow purposes, a classic token with repo, read:user, and read:network scopes covers most of what you actually need.

When you paste the token into the gh auth login prompt, the CLI stores it in your OS credential store. On macOS that's the Keychain, on Linux it depends on whether libsecret is installed, and on Windows it uses the Credential Manager. If none of those are present, it falls back to a plain-text file at ~/.config/gh/hosts.yml. That fallback is functional but not ideal for shared machines. I've seen this bite people in CI pipelines where the config directory gets pulled from cache without the token being refreshed, leaving them stuck logging into expired sessions. One thing beginners miss is that gh doesn't just authenticate against github.com. If you're using GitHub Enterprise Server, you need to specify that during the setup. The prompt asks which host you use, and selecting "GitHub.com" is the default. Choose "Other" instead, then paste your enterprise domain, and the CLI routes all API calls there rather than to the public GitHub. I spent about forty minutes debugging why my private repos weren't resolving, only to realize the CLI was hitting the wrong host entirely. The fix was rerunning gh auth login with the correct server URL and updating the config file. There's also the option to authenticate non-interactively, which matters for scripted environments and CI. You can pipe a token directly into the command using echo "TOKEN" | gh auth login --with-token. This skips all the prompts. It's faster and repeatable, but the token ends up in your shell history unless you redirect it carefully. I usually wrap it in a script that sources the token from a secret manager or environment variable, never from a plaintext file on disk.

SSH authentication is another path that some teams prefer. Instead of a token, you link an SSH key that already exists on your machine. The CLI validates it against GitHub and stores the reference. This works well if your organization enforces SSH key rotation policies, but it requires the key to be properly configured in your SSH agent first. I ran into an issue once where gh reported the SSH key as valid but git itself was rejecting the connection due to a permissions problem on the private key file. The fix was chmod 600 on the key and reloading the agent with ssh-add. Session duration is controlled by the token's expiration, not by gh itself. Personal access tokens have a maximum lifetime of eight hours for fine-grained tokens unless you refresh them, and up to one year for classic tokens depending on how you created them. If your token expires, gh commands start failing with authentication errors. You can check your current auth status anytime with gh auth status, which shows the authenticated account, the tokens involved, and whether anything has expired. It also tells you which hosts are configured. Sometimes you need to clean up or switch accounts. gh auth logout removes the token for a specific host, and gh auth setup-git configures git to use gh's credentials automatically so you don't have to think about it later. That last command is worth running because it patches your global git config to use the GitHub credential helper. After that, git clone, push, and pull operations authenticate through gh without additional prompts. It's a small step that saves you from repeated login loops when you're working across many repositories.

Get the Full Details

GitHub CLI Tool ⚒
GitHub CLI Tool ⚒

There are real limitations worth acknowledging. The CLI doesn't support two-factor authentication challenges during the login flow in a fully automated way. If your account requires 2FA, the browser-based OAuth method handles it gracefully, but the token method assumes you've already generated the token on the web. There's also a known issue where the CLI can get confused if you authenticate with HTTPS first and then try to add SSH, because the git credential helper conflicts with SSH key usage on the same repository. The workaround is to pick one method per repository and stick with it, or use gh auth setup-git after SSH setup to redirect git traffic appropriately. Another practical concern is that gh tokens don't translate directly into API access for every endpoint. Some endpoints require additional OAuth scopes that aren't included in the default token generation flow. If you're building automation that needs to access GitHub Discussions or Packages, you'll likely need to regenerate the token with the appropriate extra scopes. The CLI won't warn you about missing scopes at login time. It only fails when you run a command that requires them.

What Actually Happens Under the Hood

When you authenticate, gh writes an entry to your config that maps the hostname to an oauth token. For GitHub.com, that's github.com in the config file. The token is stored encrypted on supported systems. When you run any gh command that touches the API, the CLI attaches the token to the Authorization header automatically. No further input required. This is different from the older gh-pages workflow where you had to manually export GITHUB_TOKEN in your shell. The built-in credential management is cleaner but less transparent if something goes wrong. The credential helper integration that gh sets up is what makes the whole thing frictionless for git operations. It intercepts git's authentication requests and supplies the token without prompting. This means you can clone a private repo, push changes, or open a pull request from the CLI and never see a password dialog. The trade-off is that any process with access to your user account can trigger gh commands and inherit those credentials. On shared workstations, that's a real risk. I recommend keeping gh confined to your personal development environment and using scoped tokens with minimal permissions wherever possible. For team workflows, I've found that documenting the exact scope list you need for each use case prevents scope creep on tokens. A developer who only needs to manage issues and read repositories doesn't need write access to everything. Limiting token scope reduces the blast radius if a token leaks. The GitHub UI sometimes makes it easy to accidentally grant broader access than necessary because the default checkboxes are pre-selected. Take the time to uncheck what you don't need before generating the token.

Authentication is really just the first step. Once gh is logged in, you get commands for creating issues, reviewing pull requests, checking CI status, managing workflows, and more. But none of that matters if the initial auth setup is wrong or incomplete. Getting the right token, storing it securely, and verifying it with gh auth status takes about three minutes and prevents an hour of troubleshooting later. The tool is straightforward, but the details around token scoping, credential storage, and host configuration are where things go sideways if you skip them.

How to Set Up GitHub Copilot CLI on Linux
How to Set Up GitHub Copilot CLI on Linux