Chapter 02Lesson 02~130 minutes

Account Security, Two-Factor Authentication, SSH Keys, Tokens, and Credential Hygiene: Guided Hands-On Workflow and Core Operations

Build a disposable authentication workflow: inspect account protection, create and remove a lab SSH key, authenticate GitHub CLI through the browser flow, and verify each identity without exposing secrets.

SSHGitHub CLISafe labVerification

Learning objectives

  • Inspect account security configuration without revealing second-factor secrets or recovery material.
  • Generate a disposable Ed25519 SSH key with a dedicated filename, inspect its public fingerprint, register only the public key, test the resolved GitHub identity, and remove the key afterward.
  • Use the supported GitHub CLI browser/device flow and inspect gh auth status without displaying the stored token.
  • Perform a read-only authenticated API identity query and distinguish the caller identity from repository authorization.
  • Explain when a temporary PAT is justified and how to create, store, test, and revoke it without copying it into source/history.
  • Verify every change with before/after evidence and leave no lab credential behind.
Availability: Everything required in this lesson works with a personal GitHub.com account and free local tools. The temporary PAT exercise is optional; the mandatory API path uses the credential already managed by GitHub CLI.

1. Scenario and safety rules

You are onboarding a developer to an imaginary service called Atlas. The task is not to “make authentication work by any means.” It is to prove exactly which credential is used, confirm the resolved GitHub identity, avoid secret exposure, and remove disposable credentials at the end.

Lab rule: never run cat, type, Get-Content, gh auth token, or gh auth status --show-token against a private key/token. The only key file you display is the .pub public key.

Use a dedicated key filename so there is no risk of overwriting an existing id_ed25519. If your account is employer-managed or governed by enterprise policy, do not add a test key without authorization; use the read-only path and the synthetic examples instead.

2. Preflight: identify tools, host, and current state

git --version
ssh -V
gh --version
gh auth status --active --hostname github.com
git config --show-origin --get-all credential.helper
ssh-add -l

If gh auth status reports no authenticated account, that is not a reason to create a PAT. The supported default gh auth login flow opens or directs you to a browser and stores the resulting token in the system credential store when one is available. We will use that flow in section 9.

Record only tool versions, active hostname/account (if any), credential-helper name, and loaded SSH fingerprints. Do not include secret values in your lab notes.

3. Inspect account protection without revealing recovery material

  1. In GitHub, open Settings → Password and authentication. Confirm whether 2FA is enabled and which method categories are configured.
  2. Confirm that recovery codes have been generated/stored, but do not open, regenerate, screenshot, or paste them for this lab.
  3. If passkeys or security keys are already configured, record only their descriptive names and that a backup factor exists.
  4. Open your account security log if available and identify recent legitimate authentication events by time/type without copying private account data into a public report.
Recovery design check: a strong account has independent recovery paths. If all factors live on one phone/laptop, note the gap and remediate it deliberately after the lesson—not by experimenting with factor deletion now.

4. Generate a disposable SSH authentication key

Create the key under ~/.ssh with a lab-specific filename. Let ssh-keygen prompt for a passphrase; use a real passphrase for a credential you would keep. A disposable exercise is still the right place to learn the secure default.

Git Bash, Bash, or zsh

mkdir -p "$HOME/.ssh"
ssh-keygen -t ed25519 -C "github-auth-lab" -f "$HOME/.ssh/github_auth_lab_ed25519"
ssh-keygen -lf "$HOME/.ssh/github_auth_lab_ed25519.pub"

PowerShell

New-Item -ItemType Directory -Force "$HOME/.ssh" | Out-Null
ssh-keygen -t ed25519 -C "github-auth-lab" -f "$HOME/.ssh/github_auth_lab_ed25519"
ssh-keygen -lf "$HOME/.ssh/github_auth_lab_ed25519.pub"

The ssh-keygen -lf "$HOME/.ssh/github_auth_lab_ed25519.pub" output is safe to record as a public-key fingerprint. It should show the key size, a SHA256: fingerprint, the comment, and algorithm. The private-key file has no .pub suffix; never print it.

5. Register only the public key with GitHub

Before mutation, open Settings → SSH and GPG keys and note the existing authentication-key titles/fingerprints. Then select New SSH key, choose an authentication key, use a title such as github-auth-lab-temp, and paste only the contents of the .pub file.

Display the public half only

cat "$HOME/.ssh/github_auth_lab_ed25519.pub"

On PowerShell, Get-Content "$HOME/.ssh/github_auth_lab_ed25519.pub" is equivalent. The line starts with the public key type (for example ssh-ed25519) and is intentionally shareable. Confirm the newly listed GitHub fingerprint matches your local ssh-keygen -lf result.

Security-sensitive change: adding an SSH authentication key grants whoever controls the matching private key the ability to authenticate as your GitHub account for SSH. Use only this disposable key and schedule its removal in section 8.

6. Test the exact key and understand GitHub’s unusual exit code

Git Bash, Bash, or zsh

ssh -i "$HOME/.ssh/github_auth_lab_ed25519" -o IdentitiesOnly=yes -T git@github.com
      printf 'ssh exit=%s
' "$?"

PowerShell

ssh -i "$HOME/.ssh/github_auth_lab_ed25519" -o IdentitiesOnly=yes -T git@github.com
"ssh exit=$LASTEXITCODE"

On first contact, OpenSSH may ask you to confirm the server host key. Do not accept an arbitrary fingerprint. Compare it with GitHub’s published SSH host-key fingerprints first. After successful authentication, GitHub prints a greeting containing the resolved username and explains that shell access is not provided.

Expected result: GitHub documents that this test exits with code 1 even after successful authentication. Therefore the username/success message proves identity; a generic “nonzero means authentication failed” script is wrong for this specific test.

7. Prove the key can read a repository without pushing anything

A functional Git transport test is stronger than a banner check. Choose a repository you are allowed to read and use GIT_SSH_COMMAND to force this exact key for one command without rewriting your permanent SSH config:

GIT_SSH_COMMAND='ssh -i "$HOME/.ssh/github_auth_lab_ed25519" -o IdentitiesOnly=yes'         git ls-remote git@github.com:OWNER/REPO.git HEAD

The output should contain an object ID followed by HEAD. No branch is modified; git ls-remote asks the remote repository which refs it advertises. Replace OWNER/REPO with a repository you legitimately control or can read. PowerShell learners can set $env:GIT_SSH_COMMAND for the single terminal session, run git ls-remote, then remove the environment variable.

Do not test write access by force-pushing or changing a protected branch. Authentication can be proved with read-only transport; authorization to write belongs in later collaboration/ruleset chapters.

8. Remove the disposable SSH key and prove revocation

Return to Settings → SSH and GPG keys, locate github-auth-lab-temp by title/fingerprint, and delete it. Then repeat the exact-key SSH test. It should no longer authenticate with that key. Only after hosted revocation is verified should you remove the local files.

Git Bash, Bash, or zsh

rm -f "$HOME/.ssh/github_auth_lab_ed25519" "$HOME/.ssh/github_auth_lab_ed25519.pub"

PowerShell

Remove-Item -Force "$HOME/.ssh/github_auth_lab_ed25519","$HOME/.ssh/github_auth_lab_ed25519.pub" -ErrorAction SilentlyContinue

If the private key had been loaded into an SSH agent, remove that identity from the agent as well. Do not use broad commands that unload every key unless that is genuinely your intent.

9. Authenticate GitHub CLI with the supported browser flow

If gh is not authenticated, use the web flow rather than manually creating a PAT:

gh auth login --hostname github.com --web
gh auth status --active --hostname github.com
gh auth status --json hosts

Current GitHub CLI documentation describes browser-based login as the default authentication mode. When a system credential store is available, gh stores the resulting token there. If no suitable credential store is found, gh can fall back to a plain-text file and reports the stored location via auth status; that fallback is a reason to review local file permissions/credential-store setup, not to print the token.

Never add --show-token and do not run gh auth token for this exercise. The goal is to verify authentication state, not extract the bearer credential.

10. Use the managed gh credential for a read-only API identity check

gh api /user --jq '{login: .login, id: .id}'
gh api /rate_limit --jq '{core: .resources.core, graphql: .resources.graphql}'

The first request proves which GitHub user the CLI credential represents. The second shows rate-limit state without mutating anything. Do not publish the numeric account ID as sensitive information in a public report unless you have a reason; for the lab, it is enough to compare it locally with the expected account.

When you build raw REST clients later, send Accept: application/vnd.github+json and an explicit X-GitHub-Api-Version: 2026-03-10 header. This chapter uses gh api for the mandatory path because it avoids teaching learners to paste a token into a command line.

11. Optional PAT exercise: create one only for a concrete need

You do not need a PAT merely because APIs exist. If you want to practice token lifecycle, create a fine-grained PAT in Settings → Developer settings → Personal access tokens → Fine-grained tokens with a descriptive name, short expiration, your personal account as resource owner, and only one disposable repository selected.

Choose only the read permission needed by the planned endpoint. Do not paste the token into a command, remote URL, source file, screenshot, terminal recording, or chat. Store it temporarily in a secure local mechanism, make one read-only request, and delete the token from GitHub immediately afterward.

Why optional? GitHub explicitly recommends GitHub CLI/Git Credential Manager for command-line use and GITHUB_TOKEN or GitHub Apps for many automation cases. Creating a PAT without a concrete requirement teaches the wrong habit.

12. Challenge: choose the correct authentication surface

Need Best first choice Why
Interactive website sign-in with phishing-resistant UX Passkey or strong 2FA configuration Protects the human account; not a Git transport credential.
Routine Git over SSH from one laptop Device SSH key with passphrase / hardware-backed key Private key stays local; public key is registered to the account.
Routine Git over HTTPS GCM or GitHub CLI credential integration Avoids manually managing a PAT for ordinary Git use.
Interactive gh use gh auth login --web Browser/device flow plus secure local credential storage.
Workflow needs only its repository Built-in GITHUB_TOKEN Job-scoped, repository-bounded GitHub App installation token.
Long-running organization integration GitHub App installation token Distinct app identity, install scope, short-lived installation tokens.

13. Small failure drills

Drill A — SSH greeting names the wrong account. Stop. Do not add more keys at random. Run the exact-key test with -i and IdentitiesOnly=yes, inspect ~/.ssh/config host aliases, and verify which public key is registered to which account.

Drill B — gh auth status reports an invalid stored credential. Preserve the host/account name, then reauthenticate with the browser flow or log out the stale account intentionally. Do not use --show-token to “debug” by exposing the value.

Drill C — HTTPS Git asks for a password. GitHub account passwords are not the modern Git-over-HTTPS credential. Configure GitHub CLI/GCM or use a PAT only when appropriate; do not place the PAT in the remote URL.

14. Verification checklist

  • The lab SSH key used a dedicated filename and never overwrote an existing key.
  • Only the .pub file was displayed or uploaded; the private key was never printed.
  • The local public-key fingerprint matched the fingerprint shown by GitHub.
  • The SSH greeting resolved to the intended GitHub username and you understood the documented exit code 1.
  • The hosted SSH key was removed and removal was verified before local deletion.
  • gh auth status was used without --show-token, and API identity was verified through gh api /user.
  • No PAT was created unless you had a specific read-only exercise and an explicit revocation step.

Knowledge check

Why use a custom SSH key filename in a lab?

What does ssh -i KEY -o IdentitiesOnly=yes prove more clearly than a plain ssh -T?

Why is gh auth login --web preferable to manually creating a PAT for routine interactive CLI use?

A successful SSH test exits 1. What should a functional read-only Git test use instead?

What must happen before deleting a lab private key from disk?

15. Summary

A secure onboarding workflow is observable: inspect first, create one credential for one purpose, verify the resolved identity, perform the smallest functional test, and remove disposable access with proof. No step requires printing secret material.

You now have operational experience with the three most common developer paths: account protection in the web UI, Git transport over SSH, and GitHub CLI/API access through a managed browser authentication flow. Lesson 03 turns these operations into durable selection policy.

Next lesson

Choose credentials by tradeoff, not by habit

Lesson 03 compares SSH and HTTPS, fine-grained and classic PATs, GitHub App installation tokens, expiration, storage, SSO, and least-privilege automation design.

Authoritative references

 Configuring two-factor authentication
 Configuring 2FA recovery methods
 Generating a new SSH key and adding it to the ssh-agent
 Adding a new SSH key to your GitHub account
 Testing your SSH connection
 gh auth login
 gh auth status
 Caching GitHub credentials in Git
 Managing personal access tokens
 Authenticating to the REST API
 REST API versions

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.