Account Security, Two-Factor Authentication, SSH Keys, Tokens, and Credential Hygiene: Concepts, Architecture, and Mental Model
Separate authentication from authorization and learn why browser login, Git transport, API clients, and automation use different credentials with different trust boundaries.
Learning objectives
- Separate authentication (proving who or what you are) from authorization (what that identity may do).
-
Distinguish browser sign-in, Git over SSH/HTTPS, GitHub CLI/API
authentication, GitHub App credentials, and workflow
GITHUB_TOKEN. - Explain how 2FA, passkeys, security keys, recovery codes, and recovery planning protect an interactive GitHub account.
- Describe SSH keys, fine-grained PATs, classic PATs, and GitHub App installation tokens by owner, scope, lifespan, storage, and revocation model.
- Explain why a valid credential can still be denied by repository permissions, organization policy, SSO authorization, or resource ownership.
- Build a read-only credential inventory without printing a token, private key, recovery code, or other secret material.
1. The practical problem: one GitHub identity has several authentication doors
Chapter 01 separated local Git state from GitHub-hosted state. Security adds another boundary: signing into github.com in a browser is not the same operation as authenticating a Git push, calling the REST API, or letting a workflow act on a repository. The same human may therefore have several credentials, each with a different purpose and blast radius.
Consider a developer who signs in with a passkey, clones over SSH,
runs gh through an OAuth-backed browser login, and
maintains an automation integration using a GitHub App. All four
paths may ultimately act near the same repository, but GitHub
evaluates different credentials and policies on each path. Treating
them as “my GitHub password” produces weak designs and confusing
failures.
2. Authentication versus authorization
Authentication answers “who are you?” or, for
automation, “which app/installation/job is making this request?”
Authorization answers “may that authenticated
principal perform this action on this resource right now?” A
credential can authenticate perfectly and still receive
403 Forbidden or 404 Not Found because
authorization fails.
| Question | Authentication layer | Authorization layer |
|---|---|---|
| Can I sign in to the web UI? | Password/social login + 2FA, or passkey, depending on account setup. | Repository/org visibility, membership, role, and policy determine what appears after sign-in. |
Can I git push over SSH? |
GitHub maps the presented public key to an account or other supported SSH principal. | That account must have permission to update the target repository/ref, and policy may still reject the update. |
| Can this PAT read a private file? | The token must be valid and belong to a user who still has access. | The token must target the right resource owner/repository and carry the endpoint permission; org/SSO policy can narrow it further. |
| Can a workflow call an API? |
The job receives a GITHUB_TOKEN installation
token.
|
Effective permissions are bounded by repository/org
defaults, workflow permissions, and event
context.
|
3. Credential map: identify the channel before choosing a secret
| Channel | Typical credential | Where it lives | What it should not be confused with |
|---|---|---|---|
| Browser sign-in | Password + 2FA, passkey, or enterprise identity flow | Browser/device plus GitHub account/IdP state | Git commit author email or an SSH private key |
| Git over SSH | SSH private key locally; public key registered with GitHub | Private key on device/agent; public half on GitHub | A REST API bearer token |
| Git over HTTPS | Credential-manager/OAuth flow, or PAT where appropriate | OS credential store or approved secret store | Browser session cookie |
| GitHub CLI | Browser/device OAuth flow by default; environment token for headless cases | System credential store when available; otherwise gh may fall back to a file | GitHub account password |
| REST/GraphQL API | PAT, GitHub App token, OAuth token, or workflow token depending on caller | Secret store/memory; never source control | SSH key used for Git transport |
| GitHub Actions |
Job-specific GITHUB_TOKEN, or another
explicitly supplied credential when required
|
Injected into the job; short-lived | A general-purpose token for unrelated repositories |
The matrix is intentionally redundant with the real world: multiple methods can work. The security question is not “which credential is strongest in the abstract?” It is “which credential proves the intended principal with the least privilege, shortest useful lifetime, smallest storage surface, and clearest revocation path?”
4. Two-factor authentication protects interactive account access
Two-factor authentication (2FA) requires another factor in addition to the primary sign-in secret. GitHub currently supports TOTP authenticator apps and SMS as primary 2FA setup methods, with passkeys, security keys, and GitHub Mobile available as additional methods in supported contexts. GitHub recommends stronger, more reliable methods such as TOTP and security keys over SMS where practical.
A passkey is a WebAuthn credential that can satisfy both password and 2FA requirements for GitHub sign-in. A security key registered as a second factor proves user presence and is used with the password unless registered/used as a passkey. The operational lesson is resilience: configure more than one independent authentication path rather than making one phone the single point of failure.
5. Recovery is part of authentication architecture
Recovery codes are emergency credentials, not screenshots for a runbook. Store them offline or in an approved password/secret manager separate from the device that holds your normal second factor. GitHub also documents other recovery possibilities, including configured passkeys, security keys, verified devices, and in some circumstances previously configured SSH keys or personal access tokens.
The hard boundary matters: GitHub documents that Support cannot restore access to a 2FA-protected account when all configured 2FA credentials and recovery methods are lost. Therefore “we will contact support” is not an account-recovery design. A production team should know who owns important repositories, how ownership survives one person losing an account, and which recovery factors exist before an incident.
6. SSH keys authenticate Git transport without sending your private key
An SSH key pair contains a private key and a public key. The private key stays under your control; GitHub stores the public key. During SSH authentication, your client proves possession of the private key cryptographically. The private key is not uploaded to GitHub.
For a normal developer key, use a modern key type supported by your OpenSSH version and GitHub. Current GitHub documentation uses Ed25519 by default and recommends a passphrase. The SSH agent can hold unlocked key material so you do not repeatedly type the passphrase. A hardware-backed SSH key can further reduce private-key export risk.
7. HTTPS authentication: prefer a credential manager over copying tokens
Git over HTTPS does not require you to keep a PAT in a remote URL. GitHub recommends GitHub CLI or Git Credential Manager (GCM) to handle credentials for HTTPS. These tools can use browser-based authentication and store resulting credentials in an operating-system credential store.
This reduces accidental exposure through shell history, process
listings, screenshots, and .git/config. It also makes
2FA-compatible authentication practical without asking developers to
manually manage a long-lived token for routine Git operations.
https://TOKEN@github.com/OWNER/REPO.git as a teaching
shortcut. If a token appears in a remote URL, treat it as exposed:
revoke/rotate it first, then replace the URL and inspect
shell/history/log surfaces.
8. Personal access tokens: fine-grained first when the scenario supports them
A personal access token (PAT) acts on behalf of the user who created it. It cannot grant powers the user does not already possess. GitHub supports fine-grained PATs and classic PATs. Fine-grained PATs can be restricted to one resource owner, selected repositories, and specific repository/organization/account permissions, so they usually provide a smaller blast radius.
Classic PATs use broader OAuth-style scopes and remain necessary for some scenarios that fine-grained PATs do not yet support. Current GitHub documentation explicitly describes capability gaps for fine-grained PATs, so “fine-grained always works” is incorrect. The safe policy is: use a fine-grained PAT when the documented endpoint/workflow supports it; use a classic PAT only for a documented compatibility requirement; prefer a GitHub App for durable organization automation.
| Property | Fine-grained PAT | Classic PAT |
|---|---|---|
| Resource boundary | One selected resource owner; can narrow to selected repositories. | Scopes can cover broad sets of repositories/resources accessible to the user. |
| Permission model | Fine-grained repository/org/account permissions. |
Broader scopes such as repo or
read:org.
|
| Organization governance | Can be subject to approval/policy. | Can be restricted/blocked by org or enterprise policy. |
| Default recommendation | Prefer when the required operation is supported. | Use only when the documented scenario requires classic-token capability. |
9. Automation identities: GitHub App tokens and GITHUB_TOKEN
Long-lived automation should not automatically inherit one engineer’s personal identity. A GitHub App can authenticate as the app, as an installation, or on behalf of a user. For server-side automation, an installation access token is attractive because its permissions are derived from the App installation and it expires after a short period (currently one hour).
Inside GitHub Actions, the built-in GITHUB_TOKEN is
itself a GitHub App installation access token created for each job.
Its permissions are limited to the workflow repository and further
shaped by repository/organization policy, workflow
permissions, and event context. Chapters 13–20 cover
that execution model deeply; here the point is identity separation:
a workflow token is not your personal PAT.
GITHUB_TOKEN when a workflow only needs its own
repository. Use a GitHub App when durable automation needs a
distinct, governable installation identity. Reach for a PAT only
when the use case genuinely needs a user-bound credential.
10. Credential lifecycle: scope, expiration, rotation, revocation, storage, audit
A credential is a lifecycle, not a string. Before creating one, answer six questions: who owns it, what can it access, how long should it live, where will it be stored, how will activity be attributed, and how will it be revoked? If any answer is “we do not know,” the credential is not production-ready.
| Control | Question to answer | Good operating pattern |
|---|---|---|
| Scope | Which resource and operation are required? | Select only required repositories and read/write permissions. |
| Expiration | How long must this credential exist? | Use the shortest practical lifetime; treat “no expiration” as an exception requiring justification. |
| Rotation | How is replacement introduced without outage? | Document owner, consumers, cutover, and validation before revoking old material. |
| Revocation | What is the emergency kill path? | Owner/admin knows the exact settings/API path and can invalidate it quickly. |
| Storage | Can it leak through source/history/logs? | OS credential manager, secret manager, hardware key, or job-scoped injection; never source control. |
| Auditability | Whose action will GitHub record? | Prefer dedicated app/workflow identity for automation instead of a shared human PAT. |
11. SSO can deny a credential that is otherwise valid
On GitHub Enterprise Cloud organizations that use SAML single sign-on (SSO), a user can have a valid GitHub credential and still be unable to access organization resources until the credential is authorized for that organization. Classic PATs require explicit SSO authorization; fine-grained PAT authorization is integrated into token creation; user SSH keys may also require authorization after the user has a linked external identity.
This is an authorization boundary, not proof that the token/key is “bad.” It also does not apply universally: SSO credential authorization is an Enterprise Cloud concept and GitHub Enterprise Server has a different identity/deployment model. Always read the documentation version that matches the deployment you are operating.
12. Read-only inspection first: prove state without exposing secrets
A safe security session starts by identifying which credentials and helpers exist without printing their secret values. The following commands are deliberately inspection-oriented:
gh auth status --active --hostname github.com
gh auth status --json hosts
git remote -v
git config --show-origin --get-all credential.helper
ssh-add -l
gh auth status tests stored authentication state but
does not reveal the token unless you explicitly request
--show-token. Do not use that flag in
this course. ssh-add -l lists fingerprints for keys
currently loaded in the SSH agent; it does not print private key
contents. git remote -v is also a leak detector: if a
credential was embedded in a URL, stop and revoke it before
continuing.
gh auth status returns exit code 1 when stored accounts
have authentication issues. With --json, it normally
exits zero unless there is a fatal error, so automation must inspect
the JSON state rather than assuming the exit code has identical
meaning.
13. The trust flow: credential validation is only one gate
flowchart TD
C[Caller: human / Git / gh / app / workflow] --> K[Credential presented]
K --> A{Authentication valid?}
A -- no --> E1[401 / SSH auth failure / login failure]
A -- yes --> I[Resolved principal]
I --> P{Resource permission + token scope + policy + SSO?}
P -- no --> E2[403 / 404 / policy rejection]
P -- yes --> O[Requested operation]
O --> V[Audit / verification evidence]
The first decision validates the credential and resolves a principal. The second decision evaluates access to the target resource. GitHub may also apply policy—for example organization PAT policy, SSO requirements, branch/ruleset controls, or workflow token restrictions. This is why “the token works on one repository” does not prove that it should work everywhere.
14. Why this matters in DevOps
Delivery systems amplify credentials. A developer key may be used a few times per hour; an automation token may perform hundreds of API or Git operations without human review. If the credential belongs to a powerful human account, never expires, and is copied into a CI variable, the automation inherits a large and poorly attributable blast radius.
Secure DevOps therefore treats identity as architecture: strong interactive authentication, least-privilege machine identities, short-lived tokens where possible, protected storage, documented rotation/revocation, and independent evidence that the expected principal—not merely “some credential”—performed the operation.
15. Common misconceptions
| Misconception | Better model |
|---|---|
| “2FA protects my PAT if it leaks.” | 2FA protects interactive sign-in. A bearer token that is valid can usually be used without completing your interactive 2FA ceremony again. |
| “SSH is more authorized than HTTPS.” | SSH and HTTPS are transport/authentication choices. Repository authorization is evaluated separately. |
| “A token scope grants me admin access.” | A token can only narrow the owner’s existing access; it cannot elevate the user beyond their role. |
“If ssh -T exits nonzero, SSH is broken.”
|
GitHub’s successful SSH test intentionally exits with code 1 after printing the success banner because shell access is not provided. |
| “A valid token should work in every organization.” | Resource owner, repository selection, fine-grained permission, organization policy, SSO, and user membership can all constrain it. |
| “Deleting a leaked token from Git history fixes the incident.” | Revoke/rotate first. History cleanup reduces future exposure; it does not invalidate a credential already copied by an attacker. |
16. Mini lab: create a secret-free authentication inventory
Do this against your normal GitHub account without changing any credential. The goal is to produce evidence about types and state, not values.
- Open Settings → Password and authentication. Record only: whether 2FA is enabled, which categories of second-factor/recovery methods exist, and the date you reviewed them. Do not open/copy recovery codes.
- Open Settings → SSH and GPG keys. Record the titles and public-key fingerprints shown for authentication keys you recognize. Do not copy private keys.
- Open Developer settings → Personal access tokens. Record token names, type, expiration, and broad purpose only. Do not reveal token values.
- Run the read-only CLI commands from section 12. Record the active GitHub host/account and configured Git credential helper—not any token.
- Write one sentence for each credential answering: “what channel uses this?” and “how would I revoke it?”
17. Verification checklist
- You can explain authentication and authorization without using them as synonyms.
- You can identify which credential type is used for browser, SSH Git, HTTPS Git, gh/API, GitHub Apps, and GitHub Actions.
- Your notes contain no token values, private keys, recovery codes, session cookies, or screenshots containing secrets.
- You know at least two independent account-recovery paths or have identified a recovery gap to fix outside the lab.
- You can explain why SSO can reject a valid PAT/SSH key without calling the credential invalid.
- You know that revocation/rotation comes before repository-history cleanup after a secret leak.
Knowledge check
A fine-grained PAT is valid but returns 404 for a private repository. What should you test before regenerating it with broad permissions?
Confirm the token resource owner, selected repository, endpoint-required permission, the user’s underlying repository role, and any organization/SSO policy. GitHub may use 404 as an authorization failure for private resources.
Does enabling 2FA make a copied bearer token safe?
No. 2FA hardens interactive account authentication; a valid leaked bearer token can authenticate independently until it expires or is revoked.
Why is GITHUB_TOKEN not “the developer’s GitHub
token”?
GitHub creates it for each workflow job as a GitHub App installation access token whose effective permissions are bounded to the workflow repository and policy/event context.
What is the first technical action after confirming a PAT was exposed?
Revoke or rotate the credential so the exposed value stops authenticating. Preserve evidence, then remediate the places where it was copied.
A successful ssh -T git@github.com prints your
username but exits 1. Is authentication broken?
No. GitHub intentionally provides no shell access, and its documented SSH test exits with status 1 even after successful authentication.
18. Summary
GitHub security begins by separating identity channels. Browser
login uses interactive account authentication; Git transport uses
SSH or HTTPS credentials; API clients use tokens appropriate to the
caller; GitHub Apps provide installable automation identities;
Actions jobs receive a repository-scoped GITHUB_TOKEN.
Every credential then passes a separate authorization layer: user/app permissions, resource ownership, selected repositories, token permissions/scopes, organization policy, and possibly SSO. Secure operation means least privilege, strong recovery, protected storage, explicit expiration/rotation/revocation, and secret-free evidence.
Authoritative references
About authentication to GitHub
Configuring two-factor authentication
Configuring 2FA recovery methods
Recovering an account after losing 2FA credentials
Connecting to GitHub with SSH
Managing personal access tokens
Authenticating with a GitHub App
GITHUB_TOKEN
About authentication with single sign-on
gh auth login
gh auth status
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.