Two-Factor Authentication
TOTP enrollment, recovery codes, and login flow.
Two-Factor Authentication
ATHELOS supports TOTP as a second factor. Once enabled, login requires both your password and a 6-digit code from your authenticator app.
What TOTP is
TOTP (Time-based One-Time Password) is a standard defined by RFC 6238. It generates a 6-digit code every 30 seconds, derived from a shared secret and the current time.
Your authenticator app and ATHELOS both compute the same code from the same secret. No network call — the code is derived locally.
Recommended apps
Any TOTP-compatible app works:
- Aegis Authenticator (Android, open source)
- Raivo OTP (iOS, open source)
- 1Password (paid, includes TOTP)
- Bitwarden (paid, includes TOTP)
- Google Authenticator (works, minimal)
- Authy (works, has cloud sync — less private)
For a self-hosted, privacy-first setup, Aegis or Raivo are the best choices.
Enrolling TOTP
Go to Settings → Security. Find the Two-factor authentication card.
If TOTP is not enabled, you see a Set up 2FA button. Click it.
The setup wizard shows:
- QR code — scan with your authenticator app
- Manual entry key — a long string of characters, for manual setup
- Verification field — where you enter the first code
Step 1 — Scan the QR code
Open your authenticator app. Choose Add account or Scan QR code. Point your phone at the QR.
The app adds an entry labeled "ATHELOS" with your account email.
Step 2 — Or enter manually
If you can't scan, use the manual entry key. Paste it into the app's "add manually" flow. Choose TOTP as the type, and SHA-1 as the algorithm. 6 digits, 30-second period.
Step 3 — Verify
Your app generates a 6-digit code. Enter it into the verification field in ATHELOS.
Click Verify and enable.
If the code is correct, TOTP is enabled. You're shown 10 recovery codes.
Recovery codes
Recovery codes are single-use backup codes for when you don't have your authenticator. Each code works once.
They're shown only once. Copy them immediately.
Options:
- Copy individually — click the copy icon next to each code
- Copy all — one click, all ten codes as text
- Download as file — saves
athelos-recovery-codes.txt
Store them:
- In your password manager as a secure note
- Printed on paper, in a safe
- Both, for redundancy
Do not store them in a plain file on the same machine, or in an email draft, or in a note-taking app.
Login flow with TOTP enabled
When you sign in:
- Enter email and password — normal
- If the password is correct, a second prompt appears
- Enter the 6-digit code from your authenticator
- Or, click Use recovery code and enter a recovery code
If the password is wrong, you never see the second prompt. This prevents information leakage about whether an account exists.
Using a recovery code
On the TOTP prompt, click Use recovery code.
Enter one of your ten recovery codes. Each code is single-use — after use, it's marked consumed.
The system tells you how many unused codes remain. If you're running low, generate new ones from Settings.
Regenerating recovery codes
From Settings → Security, click Regenerate recovery codes.
You'll be asked for your password. Enter it and confirm.
Ten new codes are generated. The old codes stop working immediately.
Use this when:
- You've used several codes and want to refresh
- You suspect your old codes were compromised
- You want to rotate them on a schedule
Disabling TOTP
From Settings → Security, click Disable 2FA.
You'll be asked for your password. Enter it and confirm.
TOTP is disabled immediately. Your recovery codes are invalidated. Your next login is password-only.
Disable only if you're switching to WebAuthn, or if you've lost your authenticator and recovery codes and have no other way in.
Combining with WebAuthn
TOTP and WebAuthn can both be enabled. When both are on, ATHELOS offers either as a second factor at login:
- Enter a TOTP code
- Or use a registered WebAuthn device (YubiKey, Touch ID)
The user picks. Both are valid second factors.
See WebAuthn for details.
Behind the scenes
The TOTP secret is encrypted at rest with AES-256-GCM. The encryption key
is TOTP_ENCRYPTION_KEY, set in apps/web/.env.local.
This key is write-once. If you change it after enrollment, existing secrets can't be decrypted and TOTP stops working. Back it up to your password manager along with the other secrets.
See Reference: environment variables for the full list.
Security properties
TOTP has these properties:
- Works offline — no network required
- Single-use codes — each code expires in 30 seconds
- Resistant to replay — a used code can't be reused
- Not phishing-resistant — you can be tricked into entering the code on a fake site
The last point is why WebAuthn exists. If phishing resistance matters, use WebAuthn.
Common issues
Codes always rejected
Check the clock on your machine. TOTP depends on accurate time. If your clock is off by more than 30 seconds, codes fail. Enable NTP sync.
sudo systemctl status systemd-timesyncdLost the QR code before scanning
Cancel enrollment, then start over. A new secret is generated.
Lost the authenticator, still have recovery codes
Sign in with a recovery code. Then either re-enroll TOTP (with a new device) or disable it.
Lost both authenticator and recovery codes
See Troubleshooting: common issues for recovery via direct database access.
"Too many attempts" during verification
Wait 15 minutes. Login attempts are rate-limited.