9.9 KiB
Two-Factor Authentication (2FA) Implementation
Overview
This document describes the two-factor authentication (2FA) implementation for the DBOIS application. The system supports two methods:
- Email OTP: Users receive a one-time password via email
- 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:
mysql -u your_username -p your_database < application/migrations/001_add_two_factor_authentication.sql
Or manually execute the 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 Authenticatorapplication/models/beheer/UserOtp_model.php- Model for managing email OTP codesapplication/libraries/beheer/twofactor/SendOtpEmail.php- Library for sending OTP emailsapplication/controllers/beheer/TwoFactorController.php- Controller for 2FA settingsapplication/views/pages/beheer/twofactor/settings_view.php- 2FA settings pageapplication/views/pages/login/verify2fa_view.php- 2FA verification page during loginapplication/views/emails/twofactor_otp_email.php- Email template for OTP codesapplication/migrations/001_add_two_factor_authentication.sql- Database migration
Modified Files:
application/controllers/Login.php- Added 2FA verification logicpublic/js/lib/modules/login/loginModule.js- Updated to handle 2FA redirect
Usage
For End Users
Enabling 2FA
- Log in to the application
- Navigate to:
/beheer/TwoFactorController - Select a 2FA method:
- Email: Receive codes via email
- Google Authenticator: Use an authenticator app
Email Method Setup
- Click on "E-mail" card
- Click "2FA Inschakelen" (Enable 2FA)
- Done! You'll receive OTP codes via email on each login
Google Authenticator Method Setup
- Click on "Google Authenticator" card
- Click "2FA Inschakelen" (Enable 2FA)
- A QR code will appear
- Scan the QR code with your authenticator app (Google Authenticator, Authy, etc.)
- Enter the 6-digit code from your app to verify
- Click "Verifiëren" (Verify)
- Done! You'll use the authenticator app on each login
Changing 2FA Method
- Go to 2FA settings page
- Select a different method
- Click "Methode Wijzigen" (Change Method)
- If switching to authenticator, scan the new QR code and verify
Disabling 2FA
- Go to 2FA settings page
- Click "2FA Uitschakelen" (Disable 2FA)
- Confirm the action
Login with 2FA
- Enter username and password as usual
- If 2FA is enabled, you'll be redirected to a verification page
- 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
- 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:
$this->load->model('beheer/UserOtp_model');
$deletedCount = $this->UserOtp_model->cleanupExpiredOTPs();
Check user's 2FA status:
$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
-
Session Management: During 2FA verification, only a temporary session with user ID is created. Full session is created only after successful 2FA verification.
-
OTP Expiration: Email OTP codes expire after 10 minutes and are marked as used after verification to prevent replay attacks.
-
Time-Based Codes: Google Authenticator codes are time-based with a 30-second window, making them resistant to replay attacks.
-
Rate Limiting: Consider implementing rate limiting on:
/login/verify2faendpoint to prevent brute force/login/resendOtpendpoint to prevent email flooding
-
HTTPS: The system enforces HTTPS in production (already implemented in MY_Controller).
-
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
- Check email configuration in
application/config/email.php - Check spam/junk folders
- Use "Code opnieuw verzenden" (Resend code) button
- Check application logs for email send failures
Authenticator Code Not Working
- Ensure device time is synchronized (critical for TOTP)
- Check if user scanned the correct QR code
- Try codes within ±30 seconds window
- Re-setup authenticator if issues persist
Locked Out of Account
If a user loses access to their 2FA method:
- Admin can directly disable 2FA in database:
UPDATE users
SET two_factor_enabled = 'F',
two_factor_method = NULL,
two_factor_secret = NULL
WHERE id = [user_id];
- Admin should then securely verify user identity before re-enabling 2FA
Session Issues
If users are stuck in 2FA verification:
- Clear temporary session variables:
$this->session->unset_userdata('pending_2fa_user_id');
$this->session->unset_userdata('pending_2fa_method');
- User can click "Annuleren" (Cancel) to return to login page
Testing
Test Email Method
- Enable 2FA with email method for test user
- Log out
- Log in with test user credentials
- Verify OTP code received via email
- Test "resend code" functionality
- Test expired code (wait 10+ minutes)
- Test invalid code
Test Authenticator Method
- Enable 2FA with authenticator method for test user
- Scan QR code with Google Authenticator app
- Verify setup code works
- Log out
- Log in with test user credentials
- Verify authenticator code
- Test with time-synced codes
- Test invalid code
Test Method Switching
- Enable with one method
- Switch to another method
- Verify new method works
- Disable 2FA
- Re-enable with different method
Future Enhancements
Possible improvements:
- Backup Codes: Generate one-time backup codes for account recovery
- Remember Device: Option to skip 2FA on trusted devices for X days
- SMS OTP: Add SMS as a third 2FA method
- Admin Dashboard: View 2FA adoption metrics
- Rate Limiting: Implement built-in rate limiting for security
- Audit Logs: Log 2FA events (enable, disable, failed attempts)
- 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