Authentication & MFA
Authentication System
Section titled “Authentication System”LeafLock uses JWT-based authentication with Argon2id password hashing and optional TOTP MFA.
Implementation:
- Password hashing:
backend/crypto/password.go(Argon2id, 64MB memory, 3 iterations) - JWT middleware:
backend/middleware/jwt.go - Auth handlers:
backend/handlers/auth.go - Session storage: Redis with encrypted metadata
Endpoints:
POST /api/v1/auth/register- Create accountPOST /api/v1/auth/login- Authenticate (returns JWT or MFA challenge)POST /api/v1/auth/logout- Invalidate sessionPOST /api/v1/admin-recovery- Admin account recovery
Multi-Factor Authentication (MFA)
Section titled “Multi-Factor Authentication (MFA)”LeafLock implements RFC 6238 TOTP MFA with encrypted secret storage.
Configuration
Section titled “Configuration”TOTP Settings:
- Algorithm: SHA-1 (RFC standard)
- Time step: 30 seconds
- Code length: 6 digits
- Secret storage: ChaCha20-Poly1305 encrypted in
mfa_secret_encryptedcolumn
Backup Codes:
- Count: 10 codes per user
- Format: 16 characters (XXXX-XXXX-XXXX-XXXX)
- Storage: Argon2id hashed in
mfa_backup_codes[]array - One-time use, tracked in
mfa_backup_codes_used[]
Rate Limits (backend/middleware/rate_limit.go):
- TOTP verification: 5 attempts per 15 minutes
- Backup code verification: 3 attempts per 15 minutes
- MFA setup: 10 attempts per hour
API Endpoints
Section titled “API Endpoints”# Get MFA statusGET /api/v1/auth/mfa/statusResponse: { "enabled": true, "backup_codes_remaining": 8 }
# Begin MFA setup (returns QR code)POST /api/v1/auth/mfa/beginResponse: { "secret": "...", "qr_code": "otpauth://...", "backup_codes": [...] }
# Enable MFAPOST /api/v1/auth/mfa/enableBody: { "code": "123456" }
# Disable MFAPOST /api/v1/auth/mfa/disableBody: { "code": "123456" }# Step 1: Login with passwordPOST /api/v1/auth/loginBody: { "email": "user@example.com", "password": "..." }Response (MFA enabled): { "mfa_required": true, "session": "temp_id" }
# Step 2: Verify TOTP codePOST /api/v1/auth/mfa/verifyBody: { "session": "temp_id", "code": "123456" }Response: { "token": "jwt_token", "user_id": "..." }
# Alternative: Use backup codePOST /api/v1/auth/mfa/verifyBody: { "session": "temp_id", "backup_code": "XXXX-XXXX-XXXX-XXXX" }User Flow
Section titled “User Flow”Setup: Settings → Security → Enable MFA → Scan QR → Verify Code → Save Backup Codes Login: Email/Password → MFA Prompt → Enter TOTP or Backup Code → Authenticated Disable: Settings → Security → Disable MFA → Enter TOTP Code → Disabled
Admin Operations
Section titled “Admin Operations”Reset MFA (requires database access):
UPDATE usersSET mfa_enabled = false, mfa_secret_encrypted = NULL, mfa_backup_codes = NULL, mfa_backup_codes_used = NULLWHERE id = 'user_uuid';Check MFA adoption:
SELECT COUNT(*) FILTER (WHERE mfa_enabled = true) * 100.0 / COUNT(*) as percentageFROM usersWHERE deleted_at IS NULL;Find users without MFA:
SELECT id, email_encrypted, last_loginFROM usersWHERE mfa_enabled = false AND deleted_at IS NULLORDER BY last_login DESC NULLS LAST;Admin panel: Navigate to /admin/users for MFA management UI
Troubleshooting
Section titled “Troubleshooting”“Invalid MFA code”:
- Verify device time is synchronized (TOTP requires accurate time)
- Wait for next 30-second window
- Check correct account in authenticator app
- Rate limit: wait 15 minutes after 5 failed attempts
Lost authenticator device:
- Use saved backup code to login
- Go to Settings → Security → Regenerate Backup Codes
- Or request admin to reset MFA via SQL
Backup codes exhausted:
- Login with TOTP from authenticator app
- Navigate to Settings → Security → Regenerate Backup Codes
- Save new codes immediately
Redis rate limit check:
redis-cli GET "mfa_rate_limit:totp:USER_ID"redis-cli DEL "mfa_rate_limit:totp:USER_ID" # Emergency resetDatabase Schema
Section titled “Database Schema”Users table MFA columns:
mfa_enabled(BOOLEAN) - MFA active flagmfa_secret_encrypted(BYTEA) - ChaCha20-Poly1305 encrypted TOTP secretmfa_backup_codes(BYTEA[]) - Argon2id hashed backup codes (10 total)mfa_backup_codes_used(BYTEA[]) - Hashes of used codes
Audit log events:
mfa.enabled,mfa.disabled- MFA state changesmfa.verification_success,mfa.verification_failed- TOTP attemptsmfa.backup_code_used,mfa.backup_code_failed- Backup code usagemfa.backup_codes_regenerated- New codes generated
See backend/database/schema.go for complete schema.
Environment Variables
Section titled “Environment Variables”# RequiredJWT_SECRET=<64-character-base64-secret>SERVER_ENCRYPTION_KEY=<32-character-base64-key>REDIS_PASSWORD=<redis-password>
# Optional (defaults shown)MFA_TOTP_RATE_LIMIT=5MFA_BACKUP_RATE_LIMIT=3MFA_RATE_WINDOW_MINUTES=15Security Notes
Section titled “Security Notes”- MFA secrets encrypted at rest with
SERVER_ENCRYPTION_KEY - Backup codes hashed with Argon2id (irreversible)
- Session tokens stored in Redis with IP/user agent tracking
- All MFA events logged to audit table with encrypted metadata
- Admin MFA reset requires direct database access (no API endpoint for security)
For API details see REST API.