// ============================================================ // AUTH.JS — Login, Register, 2FA, Session Management // ============================================================ (function() { var authScreen = document.getElementById('auth-screen'); var mainApp = document.getElementById('main-app'); var loginForm = document.getElementById('login-form'); var registerForm = document.getElementById('register-form'); var forgotForm = document.getElementById('forgot-form'); var userName = document.getElementById('user-name'); var TOKEN_KEY = 'ped_scribe_token'; var USER_KEY = 'ped_scribe_user'; // Check existing session var savedToken = localStorage.getItem(TOKEN_KEY); var savedUser = localStorage.getItem(USER_KEY); if (savedToken && savedUser) { try { var user = JSON.parse(savedUser); showApp(user, savedToken); } catch(e) { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); } } // Form toggles document.getElementById('show-register').addEventListener('click', function(e) { e.preventDefault(); loginForm.classList.add('hidden'); registerForm.classList.remove('hidden'); forgotForm.classList.add('hidden'); }); document.getElementById('show-login').addEventListener('click', function(e) { e.preventDefault(); loginForm.classList.remove('hidden'); registerForm.classList.add('hidden'); forgotForm.classList.add('hidden'); }); document.getElementById('show-login-2').addEventListener('click', function(e) { e.preventDefault(); loginForm.classList.remove('hidden'); registerForm.classList.add('hidden'); forgotForm.classList.add('hidden'); }); document.getElementById('show-forgot').addEventListener('click', function(e) { e.preventDefault(); loginForm.classList.add('hidden'); registerForm.classList.add('hidden'); forgotForm.classList.remove('hidden'); }); // Login loginForm.addEventListener('submit', function(e) { e.preventDefault(); var email = document.getElementById('login-email').value; var password = document.getElementById('login-password').value; var totpCode = document.getElementById('login-totp').value; showLoading('Signing in...'); fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, password: password, totpCode: totpCode || undefined }) }) .then(function(r) { return r.json(); }) .then(function(data) { hideLoading(); if (data.requires2FA) { document.getElementById('totp-group').classList.remove('hidden'); showToast('Enter your 2FA code', 'info'); return; } if (data.success) { localStorage.setItem(TOKEN_KEY, data.token); localStorage.setItem(USER_KEY, JSON.stringify(data.user)); showApp(data.user, data.token); showToast('Welcome back, ' + data.user.name + '!', 'success'); } else { showToast(data.error || 'Login failed', 'error'); } }) // Handle email verification needed if (data.needsVerification) { showToast(data.message || 'Please verify your email first', 'error'); // Show resend option var loginLinks = loginForm.querySelector('.auth-links'); if (!document.getElementById('resend-verify-link')) { var resendLink = document.createElement('a'); resendLink.href = '#'; resendLink.id = 'resend-verify-link'; resendLink.textContent = 'Resend verification email'; resendLink.addEventListener('click', function(e) { e.preventDefault(); var email = document.getElementById('login-email').value; if (!email) { showToast('Enter your email first', 'error'); return; } fetch('/api/auth/resend-verification', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email }) }) .then(function(r) { return r.json(); }) .then(function(d) { showToast(d.message || 'Verification email sent!', 'success'); }); }); loginLinks.appendChild(resendLink); } return; } .catch(function(err) { hideLoading(); showToast('Error: ' + err.message, 'error'); }); }); // Register registerForm.addEventListener('submit', function(e) { e.preventDefault(); var name = document.getElementById('reg-name').value; var email = document.getElementById('reg-email').value; var password = document.getElementById('reg-password').value; if (password.length < 8) { showToast('Password must be at least 8 characters', 'error'); return; } showLoading('Creating account...'); fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name, email: email, password: password }) }) .then(function(r) { return r.json(); }) .then(function(data) { hideLoading(); if (data.success) { localStorage.setItem(TOKEN_KEY, data.token); localStorage.setItem(USER_KEY, JSON.stringify(data.user)); showApp(data.user, data.token); showToast('Account created! Welcome, ' + data.user.name, 'success'); } else { showToast(data.error || 'Registration failed', 'error'); } }) .catch(function(err) { hideLoading(); showToast('Error: ' + err.message, 'error'); }); }); // Forgot password forgotForm.addEventListener('submit', function(e) { e.preventDefault(); var email = document.getElementById('forgot-email').value; showLoading('Sending reset link...'); fetch('/api/auth/forgot-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email }) }) .then(function(r) { return r.json(); }) .then(function(data) { hideLoading(); showToast(data.message || 'If account exists, reset email sent', 'success'); loginForm.classList.remove('hidden'); forgotForm.classList.add('hidden'); }) .catch(function(err) { hideLoading(); showToast('Error: ' + err.message, 'error'); }); }); // Logout document.getElementById('btn-logout').addEventListener('click', function() { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); authScreen.classList.remove('hidden'); mainApp.classList.add('hidden'); showToast('Logged out', 'info'); }); function showApp(user, token) { authScreen.classList.add('hidden'); mainApp.classList.remove('hidden'); if (userName) userName.textContent = user.name || user.email; window.AUTH_TOKEN = token; } // Helper: add auth header to all API calls window.getAuthHeaders = function() { return { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + (window.AUTH_TOKEN || localStorage.getItem(TOKEN_KEY) || '') }; }; // Settings modal document.getElementById('btn-settings').addEventListener('click', function() { document.getElementById('settings-modal').classList.remove('hidden'); load2FAStatus(); loadNextcloudStatus(); }); document.getElementById('close-settings').addEventListener('click', function() { document.getElementById('settings-modal').classList.add('hidden'); }); // 2FA Setup function load2FAStatus() { fetch('/api/auth/me', { headers: getAuthHeaders() }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.user) { var status = document.getElementById('2fa-status'); var setupBtn = document.getElementById('btn-setup-2fa'); var disableBtn = document.getElementById('btn-disable-2fa'); if (data.user.totp_enabled) { status.textContent = 'Status: ✅ Enabled'; status.style.color = '#10b981'; setupBtn.classList.add('hidden'); disableBtn.classList.remove('hidden'); } else { status.textContent = 'Status: ❌ Not enabled'; status.style.color = '#ef4444'; setupBtn.classList.remove('hidden'); disableBtn.classList.add('hidden'); } } }); } document.getElementById('btn-setup-2fa').addEventListener('click', function() { fetch('/api/auth/setup-2fa', { method: 'POST', headers: getAuthHeaders() }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { document.getElementById('2fa-qr').src = data.qrCode; document.getElementById('2fa-secret').textContent = data.secret; document.getElementById('2fa-setup').classList.remove('hidden'); } }); }); document.getElementById('btn-verify-2fa').addEventListener('click', function() { var code = document.getElementById('2fa-verify-code').value; fetch('/api/auth/verify-2fa', { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ code: code }) }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { showToast('2FA enabled!', 'success'); document.getElementById('2fa-setup').classList.add('hidden'); load2FAStatus(); } else { showToast(data.error || 'Invalid code', 'error'); } }); }); document.getElementById('btn-disable-2fa').addEventListener('click', function() { var pw = prompt('Enter your password to disable 2FA:'); if (!pw) return; fetch('/api/auth/disable-2fa', { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ password: pw }) }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { showToast('2FA disabled', 'info'); load2FAStatus(); } else { showToast(data.error || 'Failed', 'error'); } }); }); console.log('✅ Auth module loaded'); })();