Authentication
Git authenticates over two transports — SSH with a key pair, or HTTPS with a token — and GitHub stopped accepting your account password for Git operations years ago. Every push and fetch now rides on either a key or a token, and the only real question is which credential goes where and how tightly it is scoped.
Scope is the whole game. A credential that can reach every repository your account can see is a single point of failure: leak it once and the blast radius is your entire account. A credential bound to one repository with read-only permission leaks into a much smaller hole. The mechanics below all reduce to choosing the narrowest credential that still does the job.
SSH Keys
An SSH key pair lives on one machine: the private half stays on disk (ideally passphrase-protected and held by an agent), and you upload the public half to your account. From then on Git talks to GitHub over git@github.com with no token prompts. This is the right default for an interactive developer machine, where one human owns one device.
Keep one key per machine rather than copying a single private key onto every device. When a laptop is lost or decommissioned, you remove just that one public key from your account and every other machine keeps working — there is nothing to rotate elsewhere.
HTTPS with Tokens
Over HTTPS, Git authenticates with a personal access token in place of the removed password. When a tool prompts for a password on an HTTPS remote, it wants a token, not your login. The token is a bearer credential: whoever holds the string can act as it, so it deserves the same care as a password and a tighter scope.
PAT Classic vs Fine-Grained
A classic PAT picks from coarse scopes like repo or workflow, and those scopes apply to every repository your account can reach. There is no required expiry, so a forgotten classic token is a permanent, account-wide credential. A fine-grained PAT instead targets a chosen set of repositories, grants per-resource permissions (for example contents: read, issues: write), and can be governed by org admins who can enforce a maximum token lifetime. An expiration is strongly encouraged but, since October 2024, no longer mandatory for personal-account tokens.
For anything new, reach for a fine-grained PAT and grant the minimum permissions on the minimum repositories. Treat any remaining classic PAT as a high-blast-radius credential to be retired, not extended.
Deploy Keys
A deploy key is a single SSH key bound to exactly one repository, set read-only or read-write. It is the correct credential for a server or CI job that touches one repo, because it does not ride on any human's account — offboarding a person never silently breaks the deployment, and a leak exposes only that one repository.
Credential Storage
How the credential is stored matters as much as its scope. A token written in plaintext to ~/.git-credentials or a .netrc is readable by any process and trivially committed by accident. Use a credential helper backed by the OS keychain (Git Credential Manager, or the platform keychain helper) so the secret is encrypted at rest and never sits in a flat file in your home directory.
Classic PAT — picks coarse scopes (repo, workflow) that apply to every repository your account can reach, with no required expiry. One leak exposes everything you can access, and a forgotten token stays valid forever.
Fine-grained PAT — targets a chosen set of repositories, grants per-resource permissions, and can be approved or restricted by org admins, who can enforce a maximum token lifetime. The blast radius of a leak is limited to the repositories and permissions you named.
- Using a classic PAT with full
reposcope for a script that only reads one repository — a leak of that token exposes every repository your account can access. - Creating a PAT with no expiration, so a token forgotten in a CI config or a shell history stays valid indefinitely with nothing forcing a rotation.
- Putting a personal SSH key on a shared CI server instead of a repo-scoped deploy key — shared infrastructure now authenticates as one human, and offboarding that person breaks the pipeline.
- Committing a
.netrcor~/.git-credentialscontaining a token, leaking a live credential into history where it survives in every clone. - Granting a deploy key write access when the consumer only needs to read — a compromise of that server can now push to the repository.
- Prefer fine-grained PATs scoped to specific repositories with the minimum permissions and a short expiry for any new token.
- Use a read-only deploy key for any server or CI job that touches a single repository, instead of a personal or account-wide credential.
- Set an expiration on every token and rotate on a schedule so a missed leak cannot stay live forever.
- Use one SSH key per developer machine and remove the public key from your account the moment a device is lost or retired.
- Store credentials in an OS-keychain credential helper, never in a plaintext
.netrcor~/.git-credentials.
Knowledge Check
A script only needs to read one repository. Which credential has the smallest blast radius if leaked?
- A read-only deploy key bound to that single repository
- A classic PAT carrying the full
reposcope across your account - Your reusable GitHub account password sent over HTTPS
- A personal SSH key copied onto the shared CI server
What is the key difference between a classic and a fine-grained PAT?
- A fine-grained PAT scopes to chosen repositories with per-resource permissions, while a classic PAT's coarse scopes reach every repository
- A classic PAT is always stored encrypted at rest, whereas a fine-grained PAT is kept in plain text by the local credential helper on the disk
- A fine-grained PAT works only over the SSH transport, whereas a classic PAT works only over HTTPS
- There is no functional difference between them beyond the name on the settings page
When prompted for a password on an HTTPS GitHub remote, what should you supply?
- A personal access token — account passwords are no longer accepted for Git operations
- Your usual GitHub account login password
- Your SSH private key pasted in at the prompt
- A current six-digit one-time code generated by your two-factor authenticator app on your phone
Why use a deploy key rather than a personal SSH key on a shared CI server?
- A deploy key is bound to one repository and to no human, so a leak stays contained and offboarding never breaks the pipeline
- Deploy keys never expire and cannot be revoked, so once installed they need no rotation or ongoing maintenance
- A deploy key grants the shared CI server standing read and write access to every repository in the whole org at once, the same broad reach a personal account key would carry
- Personal SSH keys are unable to authenticate Git operations over HTTPS at all, so the CI server would silently fall back to unauthenticated clones
You got correct