From adf1365fa2b1dd90fe6ee132dbd8873a349e5e97 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 9 Apr 2026 02:33:00 +0200 Subject: [PATCH] Fix session revocation bug that could log out current device - Fix: DELETE all other sessions query used empty string fallback when req.sessionId was undefined, causing id != '' to match ALL rows (including current session). Now skips deletion if sessionId unknown. - Fix: Revoke All endpoint returns error if current session not identified - Fix: var confirm shadowing window.confirm in password change handler --- public/js/auth.js | 8 ++++---- src/routes/auth.js | 4 +++- src/routes/sessions.js | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/public/js/auth.js b/public/js/auth.js index 89cdecb..fc97b82 100644 --- a/public/js/auth.js +++ b/public/js/auth.js @@ -594,13 +594,13 @@ document.addEventListener('DOMContentLoaded', function() { e.preventDefault(); var current = document.getElementById('pw-current'); var newPw = document.getElementById('pw-new'); - var confirm = document.getElementById('pw-confirm'); + var confirmPw = document.getElementById('pw-confirm'); var status = document.getElementById('pw-change-status'); - if (!current || !newPw || !confirm) return; + if (!current || !newPw || !confirmPw) return; if (!current.value || !newPw.value) { showToast('Fill in all fields', 'error'); return; } if (newPw.value.length < 8) { showToast('New password must be 8+ characters', 'error'); return; } - if (newPw.value !== confirm.value) { showToast('Passwords do not match', 'error'); return; } + if (newPw.value !== confirmPw.value) { showToast('Passwords do not match', 'error'); return; } if (status) { status.textContent = 'Changing...'; status.style.color = 'var(--g500)'; } @@ -614,7 +614,7 @@ document.addEventListener('DOMContentLoaded', function() { if (data.success) { showToast(data.message || 'Password changed', 'success'); if (data.passwordWarning) setTimeout(function() { showToast(data.passwordWarning, 'warning'); }, 1500); - current.value = ''; newPw.value = ''; confirm.value = ''; + current.value = ''; newPw.value = ''; confirmPw.value = ''; if (status) { status.textContent = ''; } if (typeof loadSessions === 'function') loadSessions(); } else { diff --git a/src/routes/auth.js b/src/routes/auth.js index 23f33d4..6829565 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -449,7 +449,9 @@ router.post('/change-password', authMiddleware, async (req, res) => { await db.run('UPDATE users SET password = ? WHERE id = ?', [hash, req.user.id]); // Destroy all OTHER sessions (keep current one) - try { await db.run('DELETE FROM user_sessions WHERE user_id = ? AND id != ?', [req.user.id, req.sessionId || '']); } catch (e) { /* best effort */ } + if (req.sessionId) { + try { await db.run('DELETE FROM user_sessions WHERE user_id = ? AND id != ?', [req.user.id, req.sessionId]); } catch (e) { /* best effort */ } + } await db.run('INSERT INTO audit_log (user_id, action, ip_address) VALUES (?, ?, ?)', [req.user.id, 'password_changed', req.ip]); diff --git a/src/routes/sessions.js b/src/routes/sessions.js index 98c1af6..5070ad9 100644 --- a/src/routes/sessions.js +++ b/src/routes/sessions.js @@ -38,9 +38,10 @@ router.delete('/:id', async function(req, res) { // Revoke all other sessions router.delete('/', async function(req, res) { try { + if (!req.sessionId) return res.status(400).json({ error: 'Current session not identified. Log out and log back in.' }); var result = await db.run( 'DELETE FROM user_sessions WHERE user_id = ? AND id != ?', - [req.user.id, req.sessionId || ''] + [req.user.id, req.sessionId] ); res.json({ success: true, revoked: result.changes }); } catch (e) {