Two-Factor Authentication with TOTP
TOTP — Time-based One-Time Password — is the six-digit code your authenticator app generates. It's a thirty-second second factor and the realistic minimum for any account that matters. The protocol is small, the threat model is well-understood, and self-hosting is a one-evening project.
How TOTP works
Defined in RFC 6238. A shared secret (typically 160 bits, base32-encoded), HMAC-SHA1 of the secret with the current Unix time divided by 30, truncated to six digits. The server stores the secret and computes the same thing; if it matches, you're in. The "QR code" you scan during enrollment encodes the secret as an otpauth:// URI.
otpauth://totp/Site:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Site
Authenticator apps
Three reasonable choices:
- Aegis (Android) / Raivo (iOS, archived): open-source, encrypted exportable vault. The right answer.
- Bitwarden: TOTP inside your password manager. Convenient; arguably defeats the "second factor" premise.
- Google Authenticator / Authy: closed-source, harder to back up. Avoid.
Back up the seeds
Lose your phone with no backup and you're locked out of every account that requires TOTP. Two patterns:
- Use an authenticator with encrypted export (Aegis exports an encrypted JSON file).
- When enrolling, write down the secret (the base32 string under the QR code) and store it offline. You can re-enroll from this on any TOTP app.
TOTP for SSH and sudo on Linux
The Google PAM module gives you TOTP at login time:
apt install libpam-google-authenticator
# Per-user enrollment
google-authenticator
# Walk through the prompts (time-based: yes, update file: yes, rate-limit: yes)
# Save the QR code or secret to your authenticator
# /etc/pam.d/sshd — add at the top
auth required pam_google_authenticator.so
# /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
AuthenticationMethods publickey,keyboard-interactive means "key AND TOTP" — both required.
A PAM misconfiguration locks you out. Keep an active session open while you verify the next login works.
Self-hosting
If you want a centralized 2FA-aware identity provider for your services, Authentik, Keycloak, or Authelia all support TOTP plus OIDC/SAML. Run one as a forward-auth provider behind your reverse proxy and every internal service gets 2FA without per-service config.
TOTP isn't perfect
TOTP defeats password-only theft and credential stuffing. It does not defeat real-time phishing — a fake login page can relay your code to the real one within the 30-second window. For accounts that genuinely matter (cloud root, code-signing, finance), use a hardware key (WebAuthn / FIDO2 / YubiKey) that signs the actual origin and can't be tricked by a phishing page.