*Thinking...* > The user is asking about implementing two-factor authentication (2FA) for the CΓΈdex Cheats platform. I need to provide a comprehensive explanation of how to implement 2FA, including different methods, security considerations, user experience, and technical implementation details. I should cover: > > 1. Different types of 2FA methods > 2. Technical implementation approaches > 3. User interface/experience design > 4. Security best practices > 5. Code examples where appropriate > 6. Integration with the existing platform > > I should be thorough but practical, showing both the frontend user experience and backend implementation considerations. # Two-Factor Authentication Implementation for CΓΈdex Cheats ## π Overview of 2FA Implementation Two-Factor Authentication adds an extra security layer by requiring users to provide two different authentication factors: something they know (password) and something they have (phone/app). ## π± 2FA Methods to Implement ### 1. **SMS-Based 2FA** - Send verification codes via text message - Most user-friendly for beginners - Lower security compared to app-based methods ### 2. **TOTP (Time-based One-Time Password)** - Google Authenticator, Authy, Microsoft Authenticator - More secure than SMS - Works offline ### 3. **Email-Based 2FA** - Backup option when phone isn't available - Less secure but more accessible ### 4. **Hardware Tokens (Future)** - YubiKey, hardware security keys - Highest security level ## ποΈ Technical Implementation Architecture ### Backend Components ```javascript // Dependencies needed const speakeasy = require('speakeasy'); const QRCode = require('qrcode'); const twilio = require('twilio'); const nodemailer = require('nodemailer'); const crypto = require('crypto'); ``` ### Database Schema ```sql -- Users table addition ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN DEFAULT FALSE; ALTER TABLE users ADD COLUMN two_factor_secret VARCHAR(255); ALTER TABLE users ADD COLUMN backup_codes TEXT; -- JSON array of backup codes ALTER TABLE users ADD COLUMN phone_number VARCHAR(20); ALTER TABLE users ADD COLUMN phone_verified BOOLEAN DEFAULT FALSE; -- 2FA attempts tracking CREATE TABLE two_factor_attempts ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), method VARCHAR(20), -- 'sms', 'totp', 'email' code_hash VARCHAR(255), created_at TIMESTAMP DEFAULT NOW(), used_at TIMESTAMP, expires_at TIMESTAMP, ip_address INET, user_agent TEXT ); ``` ## π§ Step-by-Step Implementation ### Phase 1: Setup and Enable 2FA #### 1. **2FA Setup Interface** ```html <!-- 2FA Setup Modal --> <div id="setup-2fa-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50"> <div class="flex items-center justify-center min-h-screen p-4"> <div class="bg-white dark:bg-dark-card rounded-xl shadow-2xl max-w-md w-full p-6"> <div class="text-center mb-6"> <h2 class="text-2xl font-bold mb-2">Enable Two-Factor Authentication</h2> <p class="text-gray-600 dark:text-gray-400">Add an extra layer of security to your account</p> </div> <!-- Method Selection --> <div id="method-selection" class="space-y-4"> <button class="w-full p-4 border-2 border-gray-200 dark:border-gray-700 rounded-lg hover:border-primary dark:hover:border-dark-primary transition" onclick="selectMethod('totp')"> <div class="flex items-center"> <div class="w-12 h-12 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center mr-4"> <svg class="w-6 h-6 text-green-600" fill="currentColor" viewBox="0 0 20 20"> <path d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z"/> </svg> </div> <div class="text-left"> <h3 class="font-medium">Authenticator App</h3> <p class="text-sm text-gray-500">Most secure option</p> </div> <div class="ml-auto text-green-600">Recommended</div> </div> </button> <button class="w-full p-4 border-2 border-gray-200 dark:border-gray-700 rounded-lg hover:border-primary dark:hover:border-dark-primary transition" onclick="selectMethod('sms')"> <div class="flex items-center"> <div class="w-12 h-12 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center mr-4"> <svg class="w-6 h-6 text-blue-600" fill="currentColor" viewBox="0 0 20 20"> <path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/> </svg> </div> <div class="text-left"> <h3 class="font-medium">SMS Text Message</h3> <p class="text-sm text-gray-500">Receive codes via text</p> </div> </div> </button> </div> <!-- TOTP Setup --> <div id="totp-setup" class="hidden"> <div class="text-center mb-6"> <h3 class="text-lg font-medium mb-2">Scan QR Code</h3> <p class="text-sm text-gray-600 dark:text-gray-400">Use Google Authenticator, Authy, or similar app</p> </div> <div class="flex justify-center mb-6"> <div id="qr-code" class="bg-white p-4 rounded-lg"></div> </div> <div class="mb-6"> <label class="block text-sm font-medium mb-2">Manual Entry Key:</label> <div class="bg-gray-100 dark:bg-gray-800 p-3 rounded-lg font-mono text-sm break-all" id="manual-key"></div> </div> <div class="mb-6"> <label for="totp-code" class="block text-sm font-medium mb-2">Verification Code</label> <input type="text" id="totp-code" class="w-full px-4 py-2 text-base border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary" placeholder="Enter 6-digit code" maxlength="6"> </div> <button onclick="verifyTotp()" class="w-full bg-primary text-white py-2 rounded-lg hover:bg-secondary transition"> Verify & Enable </button> </div> <!-- SMS Setup --> <div id="sms-setup" class="hidden"> <div class="mb-6"> <label for="phone-number" class="block text-sm font-medium mb-2">Phone Number</label> <input type="tel" id="phone-number" class="w-full px-4 py-2 text-base border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary" placeholder="+1 (555) 123-4567"> </div> <button onclick="sendSmsCode()" class="w-full bg-primary text-white py-2 rounded-lg hover:bg-secondary transition"> Send Verification Code </button> <div id="sms-verification" class="hidden mt-6"> <label for="sms-code" class="block text-sm font-medium mb-2">Verification Code</label> <input type="text" id="sms-code" class="w-full px-4 py-2 text-base border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary" placeholder="Enter 6-digit code" maxlength="6"> <button onclick="verifySms()" class="w-full bg-primary text-white py-2 rounded-lg hover:bg-secondary transition mt-4"> Verify & Enable </button> </div> </div> </div> </div> </div> ``` #### 2. **Backend Setup Endpoints** ```javascript // Generate TOTP secret and QR code app.post('/api/2fa/setup/totp', authenticateUser, async (req, res) => { try { const secret = speakeasy.generateSecret({ name: `CΓΈdex Cheats (${req.user.email})`, issuer: 'CΓΈdex Cheats', length: 32 }); // Store temporary secret (not enabled yet) await db.query( 'UPDATE users SET temp_2fa_secret = $1 WHERE id = $2', [secret.base32, req.user.id] ); // Generate QR code const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url); res.json({ success: true, qrCode: qrCodeUrl, manualKey: secret.base32 }); } catch (error) { console.error('2FA setup error:', error); res.status(500).json({ success: false, message: 'Setup failed' }); } }); // Verify TOTP and enable 2FA app.post('/api/2fa/verify/totp', authenticateUser, async (req, res) => { try { const { code } = req.body; // Get temporary secret const user = await db.query( 'SELECT temp_2fa_secret FROM users WHERE id = $1', [req.user.id] ); if (!user.rows[0].temp_2fa_secret) { return res.status(400).json({ success: false, message: 'No setup in progress' }); } // Verify the code const verified = speakeasy.totp.verify({ secret: user.rows[0].temp_2fa_secret, encoding: 'base32', token: code, window: 2 // Allow 2 time steps tolerance }); if (verified) { // Generate backup codes const backupCodes = generateBackupCodes(); // Enable 2FA await db.query( `UPDATE users SET two_factor_enabled = true, two_factor_secret = $1, backup_codes = $2, temp_2fa_secret = NULL WHERE id = $3`, [user.rows[0].temp_2fa_secret, JSON.stringify(backupCodes), req.user.id] ); res.json({ success: true, message: '2FA enabled successfully', backupCodes: backupCodes }); } else { res.status(400).json({ success: false, message: 'Invalid code' }); } } catch (error) { console.error('2FA verification error:', error); res.status(500).json({ success: false, message: 'Verification failed' }); } }); // Helper function to generate backup codes function generateBackupCodes() { const codes = []; for (let i = 0; i < 10; i++) { codes.push(crypto.randomBytes(4).toString('hex').toUpperCase()); } return codes; } ``` ### Phase 2: Login with 2FA #### 1. **2FA Login Interface** ```html <!-- 2FA Login Modal --> <div id="2fa-login-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50"> <div class="flex items-center justify-center min-h-screen p-4"> <div class="bg-white dark:bg-dark-card rounded-xl shadow-2xl max-w-md w-full p-6"> <div class="text-center mb-6"> <div class="w-16 h-16 bg-primary/10 dark:bg-primary/20 rounded-full flex items-center justify-center mx-auto mb-4"> <svg class="w-8 h-8 text-primary dark:text-dark-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/> </svg> </div> <h2 class="text-2xl font-bold mb-2">Two-Factor Authentication</h2> <p class="text-gray-600 dark:text-gray-400">Enter your verification code to continue</p> </div> <!-- Method Tabs --> <div class="flex mb-6 bg-gray-100 dark:bg-gray-800 rounded-lg p-1"> <button id="tab-totp" class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition bg-white dark:bg-dark-card shadow">Authenticator</button> <button id="tab-sms" class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition text-gray-600 dark:text-gray-400">SMS</button> <button id="tab-backup" class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition text-gray-600 dark:text-gray-400">Backup</button> </div> <!-- TOTP Input --> <div id="totp-input" class="mb-6"> <label for="login-totp-code" class="block text-sm font-medium mb-2">Authenticator Code</label> <input type="text" id="login-totp-code" class="w-full px-4 py-3 text-lg text-center border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary tracking-widest font-mono" placeholder="000000" maxlength="6"> <p class="text-xs text-gray-500 mt-2">Enter the 6-digit code from your authenticator app</p> </div> <!-- SMS Input --> <div id="sms-input" class="hidden mb-6"> <label for="login-sms-code" class="block text-sm font-medium mb-2">SMS Code</label> <input type="text" id="login-sms-code" class="w-full px-4 py-3 text-lg text-center border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary tracking-widest font-mono" placeholder="000000" maxlength="6"> <button onclick="requestSmsCode()" class="text-primary dark:text-dark-primary text-sm hover:underline mt-2"> Didn't receive code? Send again </button> </div> <!-- Backup Code Input --> <div id="backup-input" class="hidden mb-6"> <label for="backup-code" class="block text-sm font-medium mb-2">Backup Code</label> <input type="text" id="backup-code" class="w-full px-4 py-3 text-lg text-center border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary tracking-widest font-mono" placeholder="XXXXXXXX" maxlength="8"> <p class="text-xs text-gray-500 mt-2">Enter one of your backup codes</p> </div> <button onclick="verify2FA()" class="w-full bg-primary text-white py-3 rounded-lg hover:bg-secondary transition font-medium"> Verify Code </button> <div class="text-center mt-4"> <button onclick="showRecoveryOptions()" class="text-sm text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"> Having trouble? Recovery options </button> </div> </div> </div> </div> ``` #### 2. **Backend Login Verification** ```javascript // Modified login endpoint app.post('/api/auth/login', async (req, res) => { try { const { email, password } = req.body; // Verify email and password first const user = await db.query('SELECT * FROM users WHERE email = $1', [email]); if (!user.rows[0] || !await bcrypt.compare(password, user.rows[0].password_hash)) { return res.status(401).json({ success: false, message: 'Invalid credentials' }); } // Check if 2FA is enabled if (user.rows[0].two_factor_enabled) { // Create temporary session const tempToken = jwt.sign( { userId: user.rows[0].id, requires2FA: true }, process.env.JWT_SECRET, { expiresIn: '10m' } // Short expiration for 2FA ); return res.json({ success: true, requires2FA: true, tempToken: tempToken, availableMethods: getAvailable2FAMethods(user.rows[0]) }); } // Regular login if no 2FA const token = generateFullAccessToken(user.rows[0]); res.json({ success: true, token, user: sanitizeUser(user.rows[0]) }); } catch (error) { console.error('Login error:', error); res.status(500).json({ success: false, message: 'Login failed' }); } }); // 2FA verification endpoint app.post('/api/auth/verify-2fa', async (req, res) => { try { const { tempToken, code, method } = req.body; // Verify temp token const decoded = jwt.verify(tempToken, process.env.JWT_SECRET); if (!decoded.requires2FA) { return res.status(401).json({ success: false, message: 'Invalid token' }); } const user = await db.query('SELECT * FROM users WHERE id = $1', [decoded.userId]); if (!user.rows[0]) { return res.status(401).json({ success: false, message: 'User not found' }); } let verified = false; switch (method) { case 'totp': verified = speakeasy.totp.verify({ secret: user.rows[0].two_factor_secret, encoding: 'base32', token: code, window: 2 }); break; case 'sms': verified = await verifySmsCode(decoded.userId, code); break; case 'backup': verified = await verifyBackupCode(decoded.userId, code); break; } if (verified) { // Log successful 2FA await db.query( `INSERT INTO two_factor_attempts (user_id, method, used_at, ip_address, user_agent) VALUES ($1, $2, NOW(), $3, $4)`, [decoded.userId, method, req.ip, req.get('User-Agent')] ); // Generate full access token const token = generateFullAccessToken(user.rows[0]); res.json({ success: true, token, user: sanitizeUser(user.rows[0]), message: '2FA verification successful' }); } else { // Log failed attempt await db.query( `INSERT INTO two_factor_attempts (user_id, method, ip_address, user_agent) VALUES ($1, $2, $3, $4)`, [decoded.userId, method, req.ip, req.get('User-Agent')] ); res.status(401).json({ success: false, message: 'Invalid verification code' }); } } catch (error) { console.error('2FA verification error:', error); res.status(500).json({ success: false, message: 'Verification failed' }); } }); ``` ### Phase 3: SMS Implementation ```javascript // Twilio setup const twilioClient = twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN); // Send SMS code async function sendSmsCode(userId, phoneNumber) { try { // Generate 6-digit code const code = Math.floor(100000 + Math.random() * 900000).toString(); const codeHash = await bcrypt.hash(code, 10); // Store code with expiration await db.query( `INSERT INTO two_factor_attempts (user_id, method, code_hash, expires_at) VALUES ($1, 'sms', $2, NOW() + INTERVAL '5 minutes')`, [userId, codeHash] ); // Send SMS await twilioClient.messages.create({ body: `Your CΓΈdex Cheats verification code is: ${code}. This code expires in 5 minutes.`, from: process.env.TWILIO_PHONE_NUMBER, to: phoneNumber }); return true; } catch (error) { console.error('SMS send error:', error); return false; } } // Verify SMS code async function verifySmsCode(userId, code) { try { const attempts = await db.query( `SELECT * FROM two_factor_attempts WHERE user_id = $1 AND method = 'sms' AND used_at IS NULL AND expires_at > NOW() ORDER BY created_at DESC LIMIT 5`, [userId] ); for (const attempt of attempts.rows) { if (await bcrypt.compare(code, attempt.code_hash)) { // Mark as used await db.query( 'UPDATE two_factor_attempts SET used_at = NOW() WHERE id = $1', [attempt.id] ); return true; } } return false; } catch (error) { console.error('SMS verification error:', error); return false; } } ``` ## π‘οΈ Security Best Practices ### 1. **Rate Limiting** ```javascript const rateLimit = require('express-rate-limit'); // 2FA attempt rate limiting const twoFactorLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 attempts per window message: 'Too many 2FA attempts, try again later', standardHeaders: true, legacyHeaders: false, }); app.use('/api/auth/verify-2fa', twoFactorLimiter); ``` ### 2. **Account Lockout Protection** ```javascript // Check for suspicious activity async function checkSuspiciousActivity(userId) { const recentFailures = await db.query( `SELECT COUNT(*) FROM two_factor_attempts WHERE user_id = $1 AND used_at IS NULL AND created_at > NOW() - INTERVAL '1 hour'`, [userId] ); if (recentFailures.rows[0].count > 10) { // Temporarily lock account await db.query( 'UPDATE users SET account_locked_until = NOW() + INTERVAL \'1 hour\' WHERE id = $1', [userId] ); return true; } return false; } ``` ### 3. **Backup Codes Management** ```javascript // Verify and consume backup code async function verifyBackupCode(userId, code) { try { const user = await db.query('SELECT backup_codes FROM users WHERE id = $1', [userId]); const backupCodes = JSON.parse(user.rows[0].backup_codes || '[]'); const codeIndex = backupCodes.indexOf(code.toUpperCase()); if (codeIndex !== -1) { // Remove used backup code backupCodes.splice(codeIndex, 1); await db.query( 'UPDATE users SET backup_codes = $1 WHERE id = $2', [JSON.stringify(backupCodes), userId] ); return true; } return false; } catch (error) { console.error('Backup code verification error:', error); return false; } } ``` ## π± Frontend JavaScript Implementation ```javascript class TwoFactorAuth { constructor() { this.currentMethod = 'totp'; this.setupEventListeners(); } setupEventListeners() { // Method selection document.getElementById('tab-totp').addEventListener('click', () => this.switchMethod('totp')); document.getElementById('tab-sms').addEventListener('click', () => this.switchMethod('sms')); document.getElementById('tab-backup').addEventListener('click', () => this.switchMethod('backup')); // Auto-submit when code is complete document.getElementById('login-totp-code').addEventListener('input', (e) => { if (e.target.value.length === 6) { this.verify2FA(); } }); } switchMethod(method) { // Hide all inputs document.getElementById('totp-input').classList.add('hidden'); document.getElementById('sms-input').classList.add('hidden'); document.getElementById('backup-input').classList.add('hidden'); // Show selected input document.getElementById(`${method}-input`).classList.remove('hidden'); // Update tab styling document.querySelectorAll('[id^="tab-"]').forEach(tab => { tab.classList.remove('bg-white', 'dark:bg-dark-card', 'shadow'); tab.classList.add('text-gray-600', 'dark:text-gray-400'); }); document.getElementById(`tab-${method}`).classList.add('bg-white', 'dark:bg-dark-card', 'shadow'); document.getElementById(`tab-${method}`).classList.remove('text-gray-600', 'dark:text-gray-400'); this.currentMethod = method; } async setupTotp() { try { const response = await fetch('/api/2fa/setup/totp', { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.success) { document.getElementById('qr-code').innerHTML = `<img src="${data.qrCode}" alt="QR Code">`; document.getElementById('manual-key').textContent = data.manualKey; this.showStep('totp-setup'); } } catch (error) { console.error('TOTP setup error:', error); this.showError('Failed to setup authenticator app'); } } async verify2FA() { let code; switch (this.currentMethod) { case 'totp': code = document.getElementById('login-totp-code').value; break; case 'sms': code = document.getElementById('login-sms-code').value; break; case 'backup': code = document.getElementById('backup-code').value; break; } if (!code || code.length < 6) { this.showError('Please enter a valid code'); return; } try { const response = await fetch('/api/auth/verify-2fa', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tempToken: localStorage.getItem('tempToken'), code: code, method: this.currentMethod }) }); const data = await response.json(); if (data.success) { localStorage.setItem('token', data.token); localStorage.removeItem('tempToken'); window.location.href = '/dashboard'; } else { this.showError(data.message || 'Invalid verification code'); this.clearInputs(); } } catch (error) { console.error('2FA verification error:', error); this.showError('Verification failed. Please try again.'); } } async requestSmsCode() { try { const response = await fetch('/api/2fa/request-sms', { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('tempToken')}`, 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.success) { this.showSuccess('SMS code sent successfully'); } else { this.showError(data.message || 'Failed to send SMS code'); } } catch (error) { console.error('SMS request error:', error); this.showError('Failed to send SMS code'); } } showError(message) { // Create and show error notification const notification = document.createElement('div'); notification.className = 'fixed top-4 right-4 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg z-50'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => notification.remove(), 5000); } showSuccess(message) { // Create and show success notification const notification = document.createElement('div'); notification.className = 'fixed top-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-50'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => notification.remove(), 5000); } clearInputs() { document.getElementById('login-totp-code').value = ''; document.getElementById('login-sms-code').value = ''; document.getElementById('backup-code').value = ''; } } // Initialize 2FA handler const twoFactorAuth = new TwoFactorAuth(); ``` ## π Recovery and Account Management ### Account Recovery Options 1. **Backup Codes**: Pre-generated recovery codes 2. **Account Recovery**: Email-based recovery process 3. **Support Contact**: Manual verification process ### 2FA Management Interface - View enabled methods - Disable 2FA (with password confirmation) - Regenerate backup codes - Change phone number - View recent 2FA activity This comprehensive 2FA implementation provides multiple security layers while maintaining good user experience, following security best practices, and integrating seamlessly with the CΓΈdex Cheats platform. - Follow Up Deployment
verified