Files
dbois/TWO_FACTOR_AUTH_README.md

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:

  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:

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 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:

$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

  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:
UPDATE users
SET two_factor_enabled = 'F',
    two_factor_method = NULL,
    two_factor_secret = NULL
WHERE id = [user_id];
  1. 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:
$this->session->unset_userdata('pending_2fa_user_id');
$this->session->unset_userdata('pending_2fa_method');
  1. 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: