Table of Contents
- Two-Factor Authentication (2FA) in Cypht
- Table of contents
- How it works
- Requirements
- Administrator setup
- User guide: enabling 2FA on your account
- Step 1 — Open the 2FA settings
- Step 2 — Scan the QR code
- Step 3 — Verify the code
- Step 4 — Save your backup codes
- Step 5 — Save settings
- Logging in with 2FA
- Recovering access with backup codes
- Disabling 2FA
- Troubleshooting
- Security notes
Two-Factor Authentication (2FA) in Cypht
Cypht supports TOTP-based two-factor authentication (Time-based One-Time Password), which is compatible with any standard authenticator app. When enabled, users must enter a 6-digit code from their app after their password on every login.
Table of contents
- How it works
- Requirements
- Administrator setup
- User guide: enabling 2FA on your account
- Logging in with 2FA
- Recovering access with backup codes
- Disabling 2FA
- Troubleshooting
- Security notes
How it works
Cypht uses the TOTP protocol (RFC 6238). Each user gets a unique secret derived from the server-side APP_2FA_SECRET pepper and their username via PBKDF2. This means:
- 2FA state is stored in the existing user settings
- The server-side secret is never sent to the client; only a QR code encoding each user's derived secret is shown
- Codes are valid for a 30-second window and are validated with HMAC-SHA1
Compatible apps include: Google Authenticator, Authy, Microsoft Authenticator, Bitwarden Authenticator, 1Password, and any other TOTP-compatible app.
Requirements
| Requirement | Details |
|---|---|
| PHP extensions | gd — required by bacon/bacon-qr-code to render the QR code image |
| Composer packages | bacon/bacon-qr-code ^3.0.0, christian-riesen/base32 ^1.3.2 (included in composer.json) |
| Server time | Must be NTP-synchronized — TOTP codes are time-based |
Administrator setup
1. Enable the module
Add 2fa to the CYPHT_MODULES list in your .env file:
CYPHT_MODULES="core,imap,smtp,...,2fa"
Note:
config/app.phpis version-controlled and should not be edited. All module configuration belongs in.env.
2. Set the shared secret
APP_2FA_SECRET is a server-side pepper used to derive each user's individual TOTP secret. It must be at least 10 characters and must remain constant — changing it invalidates all users' enrolled 2FA.
Option A — Automatic (recommended)
Simply leave APP_2FA_SECRET empty in .env. When you run config_gen.php, it detects that the 2fa module is enabled and automatically generates and writes a cryptographically secure 32-character secret:
APP_2FA_SECRET="" # will be filled automatically
$ php scripts/config_gen.php
Generated APP_2FA_SECRET and saved to .env
Option B — Manual
Set it yourself to any random string of at least 10 characters:
APP_2FA_SECRET="your-random-secret-here"
You can generate a strong value with:
openssl rand -base64 32 | tr -d '/+=' | head -c 32
Optional: shorter secrets
By default, user secrets are 64 characters before base32 encoding. If you want shorter secrets that are easier to type manually into an authenticator app, set:
APP_2FA_SIMPLE=true
Warning: Changing
APP_2FA_SIMPLEafter users have enrolled forces all of them to re-enroll using a backup code.
3. Regenerate the site
After updating .env, always run:
php scripts/config_gen.php
This compiles the module into the production site/ directory.
User guide: enabling 2FA on your account
Step 1 — Open the 2FA settings
Log in to Cypht, click the Settings icon, then open the Site tab. Scroll down to the 2 Factor Authentication section and click the section header to expand it.
Step 2 — Scan the QR code
Open your authenticator app and scan the QR code displayed on the page. The code encodes a otpauth://totp/ URI containing your account name and derived secret.
If you cannot scan the QR code, tap Manual entry in your app and type in the long base32 code shown below the QR image (ignore line breaks).
Step 3 — Verify the code
Once your app shows a 6-digit code, enter it in the six individual digit boxes below the QR code and click Verify code.
A green confirmation appears if the code matches. The Enable 2 factor authentication checkbox is automatically checked.
If the code does not match, wait for the next 30-second window and try again. If it consistently fails, check that your server and device clocks are accurate.
Step 4 — Save your backup codes
After successful verification, three 9-digit backup codes are shown. These are your only recovery option if you lose access to your authenticator app.
Store them somewhere safe — a password manager, printed paper in a secure location, etc. Each code can only be used once.
Step 5 — Save settings
Click the Save settings button at the bottom of the settings form. 2FA is now active on your account.
Logging in with 2FA
After entering your username and password on the login page, a second screen appears asking for your 6-digit code.
Open your authenticator app, find the Cypht entry, and enter the current 6-digit code. Codes refresh every 30 seconds.
Click Submit. If the code is correct, you are taken to your home page. If it is wrong, you can retry — there is no lockout, but each code is only valid for one 30-second window.
Recovering access with backup codes
If you have lost your authenticator device, enter one of your saved 9-digit backup codes on the 2FA login screen instead of a 6-digit TOTP code.
After logging in with a backup code:
- Go to Settings → Site → 2 Factor Authentication
- The used code will no longer appear in your list
- Scan the QR code with your new device
- Verify and save — a new set of 3 backup codes is generated
You have 3 backup codes. Once all are used without re-enrolling, you will be locked out. If that happens, an administrator can reset your user settings file.
Disabling 2FA
- Go to Settings → Site → 2 Factor Authentication
- Uncheck Enable 2 factor authentication
- Click Save settings
2FA is immediately disabled. You will not be prompted for a code on your next login.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| QR code does not appear | APP_2FA_SECRET is empty |
Run php scripts/config_gen.php or set the secret manually in .env |
| Code never matches | Server clock is out of sync | Ensure NTP is running on the server (systemctl status ntp or chrony) |
| Code never matches after config change | APP_2FA_SIMPLE was changed |
Each user must log in with a backup code and re-enroll |
| "Unable to generate QR code" message | Composer packages missing | Run composer install |
| Locked out with no backup codes | All backup codes used without re-enrolling | Administrator deletes or resets the user settings file in USER_SETTINGS_DIR |
Security notes
APP_2FA_SECRETis a server-side pepper, not a per-user secret. Keep it confidential and out of version control. Your.envfile should be in.gitignore.- Changing
APP_2FA_SECRETinvalidates all enrolled users — they will need to re-enroll using backup codes. - The derived per-user secret is computed with PBKDF2 (SHA-512, 256 iterations) — it is never stored; it is re-derived on each login.
- Backup codes are single-use 9-digit integers generated with
random_int(). - TOTP validation uses a single 30-second window (no clock drift tolerance beyond the current window). Keep your server clock accurate.
###