Spaces:
Running
Running
*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
Browse files- index.html +289 -3
index.html
CHANGED
|
@@ -117,6 +117,61 @@
|
|
| 117 |
</a>
|
| 118 |
</div>
|
| 119 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
<!-- Mobile menu -->
|
| 122 |
<div id="mobile-menu" class="hidden md:hidden bg-white dark:bg-gray-800 shadow-lg">
|
|
@@ -143,11 +198,17 @@
|
|
| 143 |
Cødex Cheats revolutionizes coding education with interactive execution, AI assistance, and a vibrant community.
|
| 144 |
</p>
|
| 145 |
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
| 146 |
-
<a href="#signup" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-lg font-medium text-center transition-colors">
|
| 147 |
Start Learning Free
|
|
|
|
|
|
|
|
|
|
| 148 |
</a>
|
| 149 |
-
<a href="#demo" class="border border-blue-500 text-blue-500 hover:bg-blue-50 dark:hover:bg-gray-800 px-6 py-3 rounded-lg font-medium text-center transition-colors">
|
| 150 |
Live Demo <i class="fas fa-arrow-right ml-2"></i>
|
|
|
|
|
|
|
|
|
|
| 151 |
</a>
|
| 152 |
</div>
|
| 153 |
<div class="mt-8 flex items-center space-x-4">
|
|
@@ -202,6 +263,92 @@ console.log(greet("Cødex User"));
|
|
| 202 |
</div>
|
| 203 |
</section>
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
<!-- Features Section -->
|
| 206 |
<section id="features" class="py-20">
|
| 207 |
<div class="container mx-auto px-4">
|
|
@@ -706,14 +853,69 @@ console.log("Fibonacci of 5 is:", fibonacci(5));
|
|
| 706 |
<p class="text-xl mb-8 max-w-2xl mx-auto">
|
| 707 |
Join thousands of developers who are already learning smarter with Cødex Cheats.
|
| 708 |
</p>
|
| 709 |
-
<a href="#" class="inline-block bg-white text-blue-600 hover:bg-gray-100 px-8 py-4 rounded-lg font-bold text-lg shadow-lg transform hover:scale-105 transition-transform">
|
| 710 |
Start Learning Free Today
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 711 |
</a>
|
| 712 |
<p class="mt-6 text-blue-200">
|
| 713 |
No credit card required. Cancel anytime.
|
| 714 |
</p>
|
| 715 |
</div>
|
| 716 |
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
</main>
|
| 718 |
|
| 719 |
<footer class="bg-gray-800 dark:bg-gray-900 text-gray-300 py-12">
|
|
@@ -786,6 +988,79 @@ console.log("Fibonacci of 5 is:", fibonacci(5));
|
|
| 786 |
</footer>
|
| 787 |
|
| 788 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 789 |
// Theme toggle
|
| 790 |
const themeToggle = document.getElementById('theme-toggle');
|
| 791 |
const html = document.documentElement;
|
|
@@ -863,6 +1138,17 @@ console.log("Fibonacci of 5 is:", fibonacci(5));
|
|
| 863 |
}
|
| 864 |
});
|
| 865 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 866 |
// Smooth scrolling for anchor links
|
| 867 |
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
| 868 |
anchor.addEventListener('click', function (e) {
|
|
|
|
| 117 |
</a>
|
| 118 |
</div>
|
| 119 |
</div>
|
| 120 |
+
|
| 121 |
+
<!-- 2FA Login Modal -->
|
| 122 |
+
<div id="2fa-login-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50">
|
| 123 |
+
<div class="flex items-center justify-center min-h-screen p-4">
|
| 124 |
+
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl max-w-md w-full p-6">
|
| 125 |
+
<div class="text-center mb-6">
|
| 126 |
+
<div class="w-16 h-16 bg-blue-500/10 rounded-full flex items-center justify-center mx-auto mb-4">
|
| 127 |
+
<i class="fas fa-lock text-blue-500 text-2xl"></i>
|
| 128 |
+
</div>
|
| 129 |
+
<h2 class="text-2xl font-bold mb-2">Two-Factor Authentication</h2>
|
| 130 |
+
<p class="text-gray-600 dark:text-gray-400">Enter your verification code to continue</p>
|
| 131 |
+
</div>
|
| 132 |
+
|
| 133 |
+
<!-- Method Tabs -->
|
| 134 |
+
<div class="flex mb-6 bg-gray-100 dark:bg-gray-700 rounded-lg p-1">
|
| 135 |
+
<button id="tab-totp" class="flex-1 py-2 px-4 rounded-md text-sm font-medium transition bg-white dark:bg-gray-800 shadow">Authenticator</button>
|
| 136 |
+
<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>
|
| 137 |
+
<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>
|
| 138 |
+
</div>
|
| 139 |
+
|
| 140 |
+
<!-- TOTP Input -->
|
| 141 |
+
<div id="totp-input" class="mb-6">
|
| 142 |
+
<label for="login-totp-code" class="block text-sm font-medium mb-2">Authenticator Code</label>
|
| 143 |
+
<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-blue-500 tracking-widest font-mono" placeholder="000000" maxlength="6">
|
| 144 |
+
<p class="text-xs text-gray-500 mt-2">Enter the 6-digit code from your authenticator app</p>
|
| 145 |
+
</div>
|
| 146 |
+
|
| 147 |
+
<!-- SMS Input -->
|
| 148 |
+
<div id="sms-input" class="hidden mb-6">
|
| 149 |
+
<label for="login-sms-code" class="block text-sm font-medium mb-2">SMS Code</label>
|
| 150 |
+
<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-blue-500 tracking-widest font-mono" placeholder="000000" maxlength="6">
|
| 151 |
+
<button onclick="requestSmsCode()" class="text-blue-500 text-sm hover:underline mt-2">
|
| 152 |
+
Didn't receive code? Send again
|
| 153 |
+
</button>
|
| 154 |
+
</div>
|
| 155 |
+
|
| 156 |
+
<!-- Backup Code Input -->
|
| 157 |
+
<div id="backup-input" class="hidden mb-6">
|
| 158 |
+
<label for="backup-code" class="block text-sm font-medium mb-2">Backup Code</label>
|
| 159 |
+
<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-blue-500 tracking-widest font-mono" placeholder="XXXXXXXX" maxlength="8">
|
| 160 |
+
<p class="text-xs text-gray-500 mt-2">Enter one of your backup codes</p>
|
| 161 |
+
</div>
|
| 162 |
+
|
| 163 |
+
<button onclick="verify2FA()" class="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600 transition font-medium">
|
| 164 |
+
Verify Code
|
| 165 |
+
</button>
|
| 166 |
+
|
| 167 |
+
<div class="text-center mt-4">
|
| 168 |
+
<button onclick="showRecoveryOptions()" class="text-sm text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
|
| 169 |
+
Having trouble? Recovery options
|
| 170 |
+
</button>
|
| 171 |
+
</div>
|
| 172 |
+
</div>
|
| 173 |
+
</div>
|
| 174 |
+
</div>
|
| 175 |
|
| 176 |
<!-- Mobile menu -->
|
| 177 |
<div id="mobile-menu" class="hidden md:hidden bg-white dark:bg-gray-800 shadow-lg">
|
|
|
|
| 198 |
Cødex Cheats revolutionizes coding education with interactive execution, AI assistance, and a vibrant community.
|
| 199 |
</p>
|
| 200 |
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
| 201 |
+
<a href="#signup" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-lg font-medium text-center transition-colors group">
|
| 202 |
Start Learning Free
|
| 203 |
+
<div class="mt-2 text-sm font-normal opacity-0 group-hover:opacity-100 transition-opacity">
|
| 204 |
+
Get instant access to our interactive coding environment
|
| 205 |
+
</div>
|
| 206 |
</a>
|
| 207 |
+
<a href="#demo" class="border border-blue-500 text-blue-500 hover:bg-blue-50 dark:hover:bg-gray-800 px-6 py-3 rounded-lg font-medium text-center transition-colors group">
|
| 208 |
Live Demo <i class="fas fa-arrow-right ml-2"></i>
|
| 209 |
+
<div class="mt-2 text-sm font-normal opacity-0 group-hover:opacity-100 transition-opacity">
|
| 210 |
+
Try our code editor without signing up
|
| 211 |
+
</div>
|
| 212 |
</a>
|
| 213 |
</div>
|
| 214 |
<div class="mt-8 flex items-center space-x-4">
|
|
|
|
| 263 |
</div>
|
| 264 |
</section>
|
| 265 |
|
| 266 |
+
<!-- 2FA Setup Modal -->
|
| 267 |
+
<div id="setup-2fa-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50">
|
| 268 |
+
<div class="flex items-center justify-center min-h-screen p-4">
|
| 269 |
+
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl max-w-md w-full p-6">
|
| 270 |
+
<div class="text-center mb-6">
|
| 271 |
+
<h2 class="text-2xl font-bold mb-2">Enable Two-Factor Authentication</h2>
|
| 272 |
+
<p class="text-gray-600 dark:text-gray-400">Add an extra layer of security to your account</p>
|
| 273 |
+
</div>
|
| 274 |
+
|
| 275 |
+
<!-- Method Selection -->
|
| 276 |
+
<div id="method-selection" class="space-y-4">
|
| 277 |
+
<button class="w-full p-4 border-2 border-gray-200 dark:border-gray-700 rounded-lg hover:border-blue-500 dark:hover:border-blue-500 transition" onclick="selectMethod('totp')">
|
| 278 |
+
<div class="flex items-center">
|
| 279 |
+
<div class="w-12 h-12 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center mr-4">
|
| 280 |
+
<i class="fas fa-mobile-alt text-green-600"></i>
|
| 281 |
+
</div>
|
| 282 |
+
<div class="text-left">
|
| 283 |
+
<h3 class="font-medium">Authenticator App</h3>
|
| 284 |
+
<p class="text-sm text-gray-500">Most secure option</p>
|
| 285 |
+
</div>
|
| 286 |
+
<div class="ml-auto text-green-600">Recommended</div>
|
| 287 |
+
</div>
|
| 288 |
+
</button>
|
| 289 |
+
|
| 290 |
+
<button class="w-full p-4 border-2 border-gray-200 dark:border-gray-700 rounded-lg hover:border-blue-500 dark:hover:border-blue-500 transition" onclick="selectMethod('sms')">
|
| 291 |
+
<div class="flex items-center">
|
| 292 |
+
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center mr-4">
|
| 293 |
+
<i class="fas fa-sms text-blue-600"></i>
|
| 294 |
+
</div>
|
| 295 |
+
<div class="text-left">
|
| 296 |
+
<h3 class="font-medium">SMS Text Message</h3>
|
| 297 |
+
<p class="text-sm text-gray-500">Receive codes via text</p>
|
| 298 |
+
</div>
|
| 299 |
+
</div>
|
| 300 |
+
</button>
|
| 301 |
+
</div>
|
| 302 |
+
|
| 303 |
+
<!-- TOTP Setup -->
|
| 304 |
+
<div id="totp-setup" class="hidden">
|
| 305 |
+
<div class="text-center mb-6">
|
| 306 |
+
<h3 class="text-lg font-medium mb-2">Scan QR Code</h3>
|
| 307 |
+
<p class="text-sm text-gray-600 dark:text-gray-400">Use Google Authenticator, Authy, or similar app</p>
|
| 308 |
+
</div>
|
| 309 |
+
|
| 310 |
+
<div class="flex justify-center mb-6">
|
| 311 |
+
<div id="qr-code" class="bg-white p-4 rounded-lg"></div>
|
| 312 |
+
</div>
|
| 313 |
+
|
| 314 |
+
<div class="mb-6">
|
| 315 |
+
<label class="block text-sm font-medium mb-2">Manual Entry Key:</label>
|
| 316 |
+
<div class="bg-gray-100 dark:bg-gray-800 p-3 rounded-lg font-mono text-sm break-all" id="manual-key"></div>
|
| 317 |
+
</div>
|
| 318 |
+
|
| 319 |
+
<div class="mb-6">
|
| 320 |
+
<label for="totp-code" class="block text-sm font-medium mb-2">Verification Code</label>
|
| 321 |
+
<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-blue-500" placeholder="Enter 6-digit code" maxlength="6">
|
| 322 |
+
</div>
|
| 323 |
+
|
| 324 |
+
<button onclick="verifyTotp()" class="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition">
|
| 325 |
+
Verify & Enable
|
| 326 |
+
</button>
|
| 327 |
+
</div>
|
| 328 |
+
|
| 329 |
+
<!-- SMS Setup -->
|
| 330 |
+
<div id="sms-setup" class="hidden">
|
| 331 |
+
<div class="mb-6">
|
| 332 |
+
<label for="phone-number" class="block text-sm font-medium mb-2">Phone Number</label>
|
| 333 |
+
<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-blue-500" placeholder="+1 (555) 123-4567">
|
| 334 |
+
</div>
|
| 335 |
+
|
| 336 |
+
<button onclick="sendSmsCode()" class="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition">
|
| 337 |
+
Send Verification Code
|
| 338 |
+
</button>
|
| 339 |
+
|
| 340 |
+
<div id="sms-verification" class="hidden mt-6">
|
| 341 |
+
<label for="sms-code" class="block text-sm font-medium mb-2">Verification Code</label>
|
| 342 |
+
<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-blue-500" placeholder="Enter 6-digit code" maxlength="6">
|
| 343 |
+
<button onclick="verifySms()" class="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition mt-4">
|
| 344 |
+
Verify & Enable
|
| 345 |
+
</button>
|
| 346 |
+
</div>
|
| 347 |
+
</div>
|
| 348 |
+
</div>
|
| 349 |
+
</div>
|
| 350 |
+
</div>
|
| 351 |
+
|
| 352 |
<!-- Features Section -->
|
| 353 |
<section id="features" class="py-20">
|
| 354 |
<div class="container mx-auto px-4">
|
|
|
|
| 853 |
<p class="text-xl mb-8 max-w-2xl mx-auto">
|
| 854 |
Join thousands of developers who are already learning smarter with Cødex Cheats.
|
| 855 |
</p>
|
| 856 |
+
<a href="#" class="inline-block bg-white text-blue-600 hover:bg-gray-100 px-8 py-4 rounded-lg font-bold text-lg shadow-lg transform hover:scale-105 transition-transform group relative">
|
| 857 |
Start Learning Free Today
|
| 858 |
+
<div class="absolute left-0 right-0 -bottom-16 opacity-0 group-hover:opacity-100 transition-opacity">
|
| 859 |
+
<div class="bg-white text-gray-700 p-4 rounded-lg shadow-lg max-w-md mx-auto">
|
| 860 |
+
<h4 class="font-bold mb-2">What happens next?</h4>
|
| 861 |
+
<ol class="text-left list-decimal list-inside space-y-1 text-sm">
|
| 862 |
+
<li>Quick onboarding to assess your skill level</li>
|
| 863 |
+
<li>Personalized learning path creation</li>
|
| 864 |
+
<li>Immediate access to interactive coding exercises</li>
|
| 865 |
+
<li>Join our supportive developer community</li>
|
| 866 |
+
</ol>
|
| 867 |
+
</div>
|
| 868 |
+
</div>
|
| 869 |
</a>
|
| 870 |
<p class="mt-6 text-blue-200">
|
| 871 |
No credit card required. Cancel anytime.
|
| 872 |
</p>
|
| 873 |
</div>
|
| 874 |
</section>
|
| 875 |
+
<!-- Post-Signup Experience -->
|
| 876 |
+
<section class="py-20 bg-gray-50 dark:bg-gray-800">
|
| 877 |
+
<div class="container mx-auto px-4">
|
| 878 |
+
<div class="text-center mb-16">
|
| 879 |
+
<h2 class="text-3xl md:text-4xl font-bold mb-4">Your Learning Journey Starts Here</h2>
|
| 880 |
+
<p class="text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
| 881 |
+
Here's what you can expect after signing up
|
| 882 |
+
</p>
|
| 883 |
+
</div>
|
| 884 |
+
|
| 885 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto">
|
| 886 |
+
<div class="bg-white dark:bg-gray-700 p-8 rounded-xl shadow-lg">
|
| 887 |
+
<div class="text-blue-500 text-4xl mb-4">
|
| 888 |
+
<i class="fas fa-user-graduate"></i>
|
| 889 |
+
</div>
|
| 890 |
+
<h3 class="text-xl font-bold mb-3">1. Onboarding</h3>
|
| 891 |
+
<p class="text-gray-600 dark:text-gray-300">
|
| 892 |
+
Quick assessment to personalize your learning experience based on your current skill level and goals.
|
| 893 |
+
</p>
|
| 894 |
+
</div>
|
| 895 |
+
|
| 896 |
+
<div class="bg-white dark:bg-gray-700 p-8 rounded-xl shadow-lg">
|
| 897 |
+
<div class="text-purple-500 text-4xl mb-4">
|
| 898 |
+
<i class="fas fa-road"></i>
|
| 899 |
+
</div>
|
| 900 |
+
<h3 class="text-xl font-bold mb-3">2. Learning Path</h3>
|
| 901 |
+
<p class="text-gray-600 dark:text-gray-300">
|
| 902 |
+
Curated curriculum with interactive exercises, projects, and challenges tailored just for you.
|
| 903 |
+
</p>
|
| 904 |
+
</div>
|
| 905 |
+
|
| 906 |
+
<div class="bg-white dark:bg-gray-700 p-8 rounded-xl shadow-lg">
|
| 907 |
+
<div class="text-green-500 text-4xl mb-4">
|
| 908 |
+
<i class="fas fa-hands-helping"></i>
|
| 909 |
+
</div>
|
| 910 |
+
<h3 class="text-xl font-bold mb-3">3. Community Access</h3>
|
| 911 |
+
<p class="text-gray-600 dark:text-gray-300">
|
| 912 |
+
Join discussions, get code reviews, and collaborate on projects with fellow learners and mentors.
|
| 913 |
+
</p>
|
| 914 |
+
</div>
|
| 915 |
+
</div>
|
| 916 |
+
</div>
|
| 917 |
+
</section>
|
| 918 |
+
|
| 919 |
</main>
|
| 920 |
|
| 921 |
<footer class="bg-gray-800 dark:bg-gray-900 text-gray-300 py-12">
|
|
|
|
| 988 |
</footer>
|
| 989 |
|
| 990 |
<script>
|
| 991 |
+
// 2FA Functions
|
| 992 |
+
function selectMethod(method) {
|
| 993 |
+
document.getElementById('method-selection').classList.add('hidden');
|
| 994 |
+
document.getElementById('totp-setup').classList.add('hidden');
|
| 995 |
+
document.getElementById('sms-setup').classList.add('hidden');
|
| 996 |
+
|
| 997 |
+
if (method === 'totp') {
|
| 998 |
+
document.getElementById('totp-setup').classList.remove('hidden');
|
| 999 |
+
setupTotp();
|
| 1000 |
+
} else {
|
| 1001 |
+
document.getElementById('sms-setup').classList.remove('hidden');
|
| 1002 |
+
}
|
| 1003 |
+
}
|
| 1004 |
+
|
| 1005 |
+
async function setupTotp() {
|
| 1006 |
+
try {
|
| 1007 |
+
const response = await fetch('/api/2fa/setup/totp', {
|
| 1008 |
+
method: 'POST',
|
| 1009 |
+
headers: {
|
| 1010 |
+
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
| 1011 |
+
'Content-Type': 'application/json'
|
| 1012 |
+
}
|
| 1013 |
+
});
|
| 1014 |
+
|
| 1015 |
+
const data = await response.json();
|
| 1016 |
+
if (data.success) {
|
| 1017 |
+
document.getElementById('qr-code').innerHTML = `<img src="${data.qrCode}" alt="QR Code">`;
|
| 1018 |
+
document.getElementById('manual-key').textContent = data.manualKey;
|
| 1019 |
+
}
|
| 1020 |
+
} catch (error) {
|
| 1021 |
+
console.error('TOTP setup error:', error);
|
| 1022 |
+
showNotification('Failed to setup authenticator app', 'error');
|
| 1023 |
+
}
|
| 1024 |
+
}
|
| 1025 |
+
|
| 1026 |
+
async function verifyTotp() {
|
| 1027 |
+
const code = document.getElementById('totp-code').value;
|
| 1028 |
+
if (!code || code.length !== 6) {
|
| 1029 |
+
showNotification('Please enter a valid 6-digit code', 'error');
|
| 1030 |
+
return;
|
| 1031 |
+
}
|
| 1032 |
+
|
| 1033 |
+
try {
|
| 1034 |
+
const response = await fetch('/api/2fa/verify/totp', {
|
| 1035 |
+
method: 'POST',
|
| 1036 |
+
headers: {
|
| 1037 |
+
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
| 1038 |
+
'Content-Type': 'application/json'
|
| 1039 |
+
},
|
| 1040 |
+
body: JSON.stringify({ code })
|
| 1041 |
+
});
|
| 1042 |
+
|
| 1043 |
+
const data = await response.json();
|
| 1044 |
+
if (data.success) {
|
| 1045 |
+
showNotification('2FA enabled successfully!', 'success');
|
| 1046 |
+
document.getElementById('setup-2fa-modal').classList.add('hidden');
|
| 1047 |
+
// Show backup codes to user
|
| 1048 |
+
showBackupCodes(data.backupCodes);
|
| 1049 |
+
} else {
|
| 1050 |
+
showNotification(data.message || 'Invalid code', 'error');
|
| 1051 |
+
}
|
| 1052 |
+
} catch (error) {
|
| 1053 |
+
console.error('TOTP verification error:', error);
|
| 1054 |
+
showNotification('Verification failed', 'error');
|
| 1055 |
+
}
|
| 1056 |
+
}
|
| 1057 |
+
|
| 1058 |
+
function showBackupCodes(codes) {
|
| 1059 |
+
// Implement backup codes display modal
|
| 1060 |
+
console.log('Backup codes:', codes);
|
| 1061 |
+
// In a real implementation, show these to the user and allow download
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
// Theme toggle
|
| 1065 |
const themeToggle = document.getElementById('theme-toggle');
|
| 1066 |
const html = document.documentElement;
|
|
|
|
| 1138 |
}
|
| 1139 |
});
|
| 1140 |
|
| 1141 |
+
function showNotification(message, type) {
|
| 1142 |
+
const notification = document.createElement('div');
|
| 1143 |
+
notification.className = `fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 ${
|
| 1144 |
+
type === 'error' ? 'bg-red-500' : 'bg-green-500'
|
| 1145 |
+
} text-white`;
|
| 1146 |
+
notification.textContent = message;
|
| 1147 |
+
document.body.appendChild(notification);
|
| 1148 |
+
|
| 1149 |
+
setTimeout(() => notification.remove(), 5000);
|
| 1150 |
+
}
|
| 1151 |
+
|
| 1152 |
// Smooth scrolling for anchor links
|
| 1153 |
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
| 1154 |
anchor.addEventListener('click', function (e) {
|