# Two-Factor Authentication (2FA) Implementation ## Overview This document describes the two-factor authentication (2FA) implementation for the DBOIS application. The system supports two methods: 1. **Email OTP**: Users receive a one-time password via email 2. **Google Authenticator**: Users use an authenticator app (Google Authenticator, Authy, etc.) ## Installation Steps ### 1. Database Migration Run the SQL migration to add the necessary database tables and columns: ```bash mysql -u your_username -p your_database < application/migrations/001_add_two_factor_authentication.sql ``` Or manually execute the SQL: ```sql -- Add 2FA columns to users table ALTER TABLE `users` ADD COLUMN `two_factor_enabled` VARCHAR(1) NOT NULL DEFAULT 'F' COMMENT 'T=enabled, F=disabled' AFTER `role`, ADD COLUMN `two_factor_method` VARCHAR(20) NULL DEFAULT NULL COMMENT 'email or authenticator' AFTER `two_factor_enabled`, ADD COLUMN `two_factor_secret` VARCHAR(255) NULL DEFAULT NULL COMMENT 'TOTP secret for Google Authenticator' AFTER `two_factor_method`; -- Create table for email OTP codes CREATE TABLE `user_otp_codes` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT(11) UNSIGNED NOT NULL, `otp_code` VARCHAR(6) NOT NULL, `expires_at` TIMESTAMP NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `used` VARCHAR(1) NOT NULL DEFAULT 'F' COMMENT 'T=used, F=not used', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_expires_at` (`expires_at`), CONSTRAINT `fk_otp_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- Create index for faster lookups CREATE INDEX `idx_users_two_factor` ON `users` (`two_factor_enabled`, `two_factor_method`); ``` ### 2. File Structure The following files have been created/modified: #### New Files: - `application/libraries/TwoFactorAuth.php` - TOTP library for Google Authenticator - `application/models/beheer/UserOtp_model.php` - Model for managing email OTP codes - `application/libraries/beheer/twofactor/SendOtpEmail.php` - Library for sending OTP emails - `application/controllers/beheer/TwoFactorController.php` - Controller for 2FA settings - `application/views/pages/beheer/twofactor/settings_view.php` - 2FA settings page - `application/views/pages/login/verify2fa_view.php` - 2FA verification page during login - `application/views/emails/twofactor_otp_email.php` - Email template for OTP codes - `application/migrations/001_add_two_factor_authentication.sql` - Database migration #### Modified Files: - `application/controllers/Login.php` - Added 2FA verification logic - `public/js/lib/modules/login/loginModule.js` - Updated to handle 2FA redirect ## Usage ### For End Users #### Enabling 2FA 1. Log in to the application 2. Navigate to: `/beheer/TwoFactorController` 3. Select a 2FA method: - **Email**: Receive codes via email - **Google Authenticator**: Use an authenticator app #### Email Method Setup 1. Click on "E-mail" card 2. Click "2FA Inschakelen" (Enable 2FA) 3. Done! You'll receive OTP codes via email on each login #### Google Authenticator Method Setup 1. Click on "Google Authenticator" card 2. Click "2FA Inschakelen" (Enable 2FA) 3. A QR code will appear 4. Scan the QR code with your authenticator app (Google Authenticator, Authy, etc.) 5. Enter the 6-digit code from your app to verify 6. Click "Verifiëren" (Verify) 7. Done! You'll use the authenticator app on each login #### Changing 2FA Method 1. Go to 2FA settings page 2. Select a different method 3. Click "Methode Wijzigen" (Change Method) 4. If switching to authenticator, scan the new QR code and verify #### Disabling 2FA 1. Go to 2FA settings page 2. Click "2FA Uitschakelen" (Disable 2FA) 3. Confirm the action #### Login with 2FA 1. Enter username and password as usual 2. If 2FA is enabled, you'll be redirected to a verification page 3. For email method: - Check your email for the 6-digit code - Enter the code on the verification page - Click "Verifiëren" (Verify) - Can resend code if needed 4. For authenticator method: - Open your authenticator app - Enter the current 6-digit code - Click "Verifiëren" (Verify) ### For Administrators #### Setting up Email Configuration Ensure your CodeIgniter email configuration is properly set up in `application/config/email.php`. The system sends OTP emails from `noreply@dbois.nl`. #### OTP Code Settings OTP codes: - Are 6 digits long - Are valid for 10 minutes - Are automatically cleaned up after use or expiration #### Google Authenticator Settings - TOTP codes are generated using RFC 6238 standard - Codes are 6 digits long - Time step is 30 seconds - Verification allows ±30 seconds time window for clock drift #### Monitoring and Maintenance **Clean up expired OTP codes:** ```php $this->load->model('beheer/UserOtp_model'); $deletedCount = $this->UserOtp_model->cleanupExpiredOTPs(); ``` **Check user's 2FA status:** ```php $user = $this->User_model->get($userId); if ($user->two_factor_enabled === 'T') { // User has 2FA enabled $method = $user->two_factor_method; // 'email' or 'authenticator' } ``` ## API Endpoints ### 2FA Settings Management #### Get 2FA Status ``` GET /beheer/TwoFactorController/status Response: { enabled: boolean, method: string|null } ``` #### Enable 2FA ``` POST /beheer/TwoFactorController/enable Data: { method: 'email'|'authenticator' } Response: { success: boolean, message: string, method: string, secret?: string (if authenticator), qrCodeUrl?: string (if authenticator) } ``` #### Verify Authenticator Setup ``` POST /beheer/TwoFactorController/verifySetup Data: { code: string (6 digits) } Response: { success: boolean, message: string } ``` #### Disable 2FA ``` POST /beheer/TwoFactorController/disable Response: { success: boolean, message: string } ``` #### Change 2FA Method ``` POST /beheer/TwoFactorController/changeMethod Data: { method: 'email'|'authenticator' } Response: { success: boolean, message: string, method: string, secret?: string (if authenticator), qrCodeUrl?: string (if authenticator) } ``` ### Login Flow #### Initial Login ``` POST /login Data: { username: string, password: string } Response (with 2FA): { result: '2FA-REQUIRED', method: string } Response (without 2FA): boolean ``` #### Show 2FA Verification Page ``` GET /login/showVerify2fa Displays verification page with temporary session ``` #### Verify 2FA Code ``` POST /login/verify2fa Data: { code: string (6 digits) } Response: { success: boolean, message: string } ``` #### Resend OTP (Email method only) ``` POST /login/resendOtp Response: { success: boolean, message: string } ``` ## Security Considerations 1. **Session Management**: During 2FA verification, only a temporary session with user ID is created. Full session is created only after successful 2FA verification. 2. **OTP Expiration**: Email OTP codes expire after 10 minutes and are marked as used after verification to prevent replay attacks. 3. **Time-Based Codes**: Google Authenticator codes are time-based with a 30-second window, making them resistant to replay attacks. 4. **Rate Limiting**: Consider implementing rate limiting on: - `/login/verify2fa` endpoint to prevent brute force - `/login/resendOtp` endpoint to prevent email flooding 5. **HTTPS**: The system enforces HTTPS in production (already implemented in MY_Controller). 6. **QR Code Security**: QR codes contain sensitive TOTP secrets. They should only be displayed over HTTPS and users should be advised to never share them. ## Troubleshooting ### Email OTP Not Received 1. Check email configuration in `application/config/email.php` 2. Check spam/junk folders 3. Use "Code opnieuw verzenden" (Resend code) button 4. Check application logs for email send failures ### Authenticator Code Not Working 1. Ensure device time is synchronized (critical for TOTP) 2. Check if user scanned the correct QR code 3. Try codes within ±30 seconds window 4. Re-setup authenticator if issues persist ### Locked Out of Account If a user loses access to their 2FA method: 1. Admin can directly disable 2FA in database: ```sql UPDATE users SET two_factor_enabled = 'F', two_factor_method = NULL, two_factor_secret = NULL WHERE id = [user_id]; ``` 2. Admin should then securely verify user identity before re-enabling 2FA ### Session Issues If users are stuck in 2FA verification: 1. Clear temporary session variables: ```php $this->session->unset_userdata('pending_2fa_user_id'); $this->session->unset_userdata('pending_2fa_method'); ``` 2. User can click "Annuleren" (Cancel) to return to login page ## Testing ### Test Email Method 1. Enable 2FA with email method for test user 2. Log out 3. Log in with test user credentials 4. Verify OTP code received via email 5. Test "resend code" functionality 6. Test expired code (wait 10+ minutes) 7. Test invalid code ### Test Authenticator Method 1. Enable 2FA with authenticator method for test user 2. Scan QR code with Google Authenticator app 3. Verify setup code works 4. Log out 5. Log in with test user credentials 6. Verify authenticator code 7. Test with time-synced codes 8. Test invalid code ### Test Method Switching 1. Enable with one method 2. Switch to another method 3. Verify new method works 4. Disable 2FA 5. Re-enable with different method ## Future Enhancements Possible improvements: 1. **Backup Codes**: Generate one-time backup codes for account recovery 2. **Remember Device**: Option to skip 2FA on trusted devices for X days 3. **SMS OTP**: Add SMS as a third 2FA method 4. **Admin Dashboard**: View 2FA adoption metrics 5. **Rate Limiting**: Implement built-in rate limiting for security 6. **Audit Logs**: Log 2FA events (enable, disable, failed attempts) 7. **Mandatory 2FA**: Require 2FA for specific user roles ## Support For issues or questions, please contact the development team or refer to: - CodeIgniter Documentation: https://codeigniter.com/userguide3/ - Google Authenticator: https://github.com/google/google-authenticator - RFC 6238 (TOTP): https://tools.ietf.org/html/rfc6238