// ============================================================ // AUTH.JS — Login, Register, 2FA, Session Management // ============================================================ // Wait for DOM to be fully ready document.addEventListener('DOMContentLoaded', 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'; console.log('[Auth] Initializing...'); console.log('[Auth] authScreen:', !!authScreen); console.log('[Auth] mainApp:', !!mainApp); console.log('[Auth] loginForm:', !!loginForm); console.log('[Auth] registerForm:', !!registerForm); // Make sure forms are visible/hidden correctly on load showLoginForm(); // Check for saved session var savedToken = localStorage.getItem(TOKEN_KEY); if (savedToken) { console.log('[Auth] Found saved token, verifying...'); fetch('/api/auth/me', { headers: { 'Authorization': 'Bearer ' + savedToken } }) .then(function(r) { if (r.ok) return r.json(); throw new Error('expired'); }) .then(function(data) { if (data && data.user) { console.log('[Auth] Token valid, logging in as:', data.user.email); enterApp(data.user, savedToken); } else { console.log('[Auth] Token invalid'); clearSession(); } }) .catch(function() { console.log('[Auth] Token expired or invalid'); clearSession(); }); } // ---- HELPER FUNCTIONS ---- function showLoginForm() { if (loginForm) loginForm.style.display = 'block'; if (registerForm) registerForm.style.display = 'none'; if (forgotForm) forgotForm.style.display = 'none'; } function showRegisterForm() { if (loginForm) loginForm.style.display = 'none'; if (registerForm) registerForm.style.display = 'block'; if (forgotForm) forgotForm.style.display = 'none'; } function showForgotForm() { if (loginForm) loginForm.style.display = 'none'; if (registerForm) registerForm.style.display = 'none'; if (forgotForm) forgotForm.style.display = 'block'; } function enterApp(user, token) { window.AUTH_TOKEN = token; localStorage.setItem(TOKEN_KEY, token); localStorage.setItem(USER_KEY, JSON.stringify(user)); if (authScreen) authScreen.style.display = 'none'; if (mainApp) { mainApp.style.display = 'block'; mainApp.className = mainApp.className.replace('hidden', '').trim(); } if (userName) userName.textContent = user.name || user.email; // Show admin tab for admins var adminTabBtn = document.getElementById('admin-tab-btn'); if (adminTabBtn) { if (user.role === 'admin') { adminTabBtn.classList.remove('hidden'); } else { adminTabBtn.classList.add('hidden'); } } console.log('[Auth] ✅ Entered app as:', user.email, 'Role:', user.role); } function exitApp() { clearSession(); if (authScreen) authScreen.style.display = 'flex'; if (mainApp) mainApp.style.display = 'none'; var adminTabBtn = document.getElementById('admin-tab-btn'); if (adminTabBtn) adminTabBtn.classList.add('hidden'); showLoginForm(); } function clearSession() { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); window.AUTH_TOKEN = null; } // Global auth header helper window.getAuthHeaders = function() { return { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + (window.AUTH_TOKEN || localStorage.getItem(TOKEN_KEY) || '') }; }; // ---- FORM TOGGLE LINKS ---- document.addEventListener('click', function(e) { var target = e.target; // Handle link clicks by ID if (target.id === 'show-register' || target.closest('#show-register')) { e.preventDefault(); showRegisterForm(); } if (target.id === 'show-login' || target.closest('#show-login')) { e.preventDefault(); showLoginForm(); } if (target.id === 'show-login-2' || target.closest('#show-login-2')) { e.preventDefault(); showLoginForm(); } if (target.id === 'show-forgot' || target.closest('#show-forgot')) { e.preventDefault(); showForgotForm(); } // Logout button if (target.id === 'btn-logout' || target.closest('#btn-logout')) { e.preventDefault(); exitApp(); showToast('Logged out', 'info'); } // Settings button if (target.id === 'btn-settings' || target.closest('#btn-settings')) { e.preventDefault(); var modal = document.getElementById('settings-modal'); if (modal) { modal.style.display = 'flex'; modal.className = modal.className.replace('hidden', '').trim(); load2FAStatus(); if (typeof loadNextcloudStatus === 'function') loadNextcloudStatus(); } } // Close settings if (target.id === 'close-settings' || target.closest('#close-settings')) { e.preventDefault(); var modal = document.getElementById('settings-modal'); if (modal) modal.style.display = 'none'; } // Setup 2FA if (target.id === 'btn-setup-2fa' || target.closest('#btn-setup-2fa')) { e.preventDefault(); fetch('/api/auth/setup-2fa', { method: 'POST', headers: getAuthHeaders() }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { var qr = document.getElementById('2fa-qr'); var secret = document.getElementById('2fa-secret'); var setup = document.getElementById('2fa-setup'); if (qr) qr.src = data.qrCode; if (secret) secret.textContent = data.secret; if (setup) { setup.style.display = 'block'; setup.className = setup.className.replace('hidden', '').trim(); } } }); } // Verify 2FA if (target.id === 'btn-verify-2fa' || target.closest('#btn-verify-2fa')) { e.preventDefault(); 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'); var setup = document.getElementById('2fa-setup'); if (setup) setup.style.display = 'none'; load2FAStatus(); } else { showToast(data.error || 'Invalid code', 'error'); } }); } // Disable 2FA if (target.id === 'btn-disable-2fa' || target.closest('#btn-disable-2fa')) { e.preventDefault(); var pw = prompt('Enter 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'); }); } }); // ---- LOGIN FORM SUBMIT ---- if (loginForm) { loginForm.addEventListener('submit', function(e) { e.preventDefault(); e.stopPropagation(); var email = document.getElementById('login-email').value.trim(); var password = document.getElementById('login-password').value; var totpEl = document.getElementById('login-totp'); var totpCode = totpEl ? totpEl.value.trim() : ''; if (!email || !password) { showToast('Enter email and password', 'error'); return false; } console.log('[Auth] Attempting login for:', email); showLoading('Signing in...'); var body = { email: email, password: password }; if (totpCode) body.totpCode = totpCode; fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }) .then(function(r) { return r.json(); }) .then(function(data) { hideLoading(); console.log('[Auth] Login response:', JSON.stringify(data).substring(0, 200)); if (data.requires2FA) { var grp = document.getElementById('totp-group'); if (grp) { grp.style.display = 'block'; grp.className = grp.className.replace('hidden', '').trim(); } showToast('Enter your 2FA code', 'info'); return; } if (data.needsVerification) { showToast('Verify your email first. Check inbox.', 'error'); return; } if (data.success && data.token && data.user) { enterApp(data.user, data.token); showToast('Welcome, ' + data.user.name + '!', 'success'); } else { showToast(data.error || 'Login failed', 'error'); } }) .catch(function(err) { hideLoading(); console.error('[Auth] Login error:', err); showToast('Connection error', 'error'); }); return false; }); } // ---- REGISTER FORM SUBMIT ---- if (registerForm) { registerForm.addEventListener('submit', function(e) { e.preventDefault(); e.stopPropagation(); var name = document.getElementById('reg-name').value.trim(); var email = document.getElementById('reg-email').value.trim(); var password = document.getElementById('reg-password').value; if (!name || !email || !password) { showToast('Fill all fields', 'error'); return false; } if (password.length < 8) { showToast('Password must be 8+ characters', 'error'); return false; } console.log('[Auth] Registering:', email); 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(); console.log('[Auth] Register response:', JSON.stringify(data).substring(0, 200)); if (data.success && data.token && data.user) { enterApp(data.user, data.token); showToast(data.message || 'Account created!', 'success'); } else if (data.success && data.needsVerification) { showToast(data.message || 'Check email to verify', 'success'); showLoginForm(); } else { showToast(data.error || 'Registration failed', 'error'); } }) .catch(function(err) { hideLoading(); console.error('[Auth] Register error:', err); showToast('Connection error', 'error'); }); return false; }); } // ---- FORGOT PASSWORD SUBMIT ---- if (forgotForm) { forgotForm.addEventListener('submit', function(e) { e.preventDefault(); e.stopPropagation(); var email = document.getElementById('forgot-email').value.trim(); if (!email) { showToast('Enter email', 'error'); return false; } showLoading('Sending...'); 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 || 'Check your email', 'success'); showLoginForm(); }) .catch(function(err) { hideLoading(); showToast('Error', 'error'); }); return false; }); } // ---- 2FA STATUS ---- function load2FAStatus() { fetch('/api/auth/me', { headers: getAuthHeaders() }) .then(function(r) { return r.json(); }) .then(function(data) { if (!data || !data.user) return; 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) { if (status) { status.textContent = 'Status: ✅ Enabled'; status.style.color = '#10b981'; } if (setupBtn) setupBtn.style.display = 'none'; if (disableBtn) disableBtn.style.display = 'inline-flex'; } else { if (status) { status.textContent = 'Status: ❌ Not enabled'; status.style.color = '#ef4444'; } if (setupBtn) setupBtn.style.display = 'inline-flex'; if (disableBtn) disableBtn.style.display = 'none'; } }) .catch(function() {}); } // Check registration status fetch('/api/auth/registration-status') .then(function(r) { return r.json(); }) .then(function(data) { if (!data.registrationEnabled) { var link = document.getElementById('show-register'); if (link) link.style.display = 'none'; } }) .catch(function() {}); console.log('[Auth] ✅ Module ready'); });