Add session management features: implement user session revocation on password change, enhance session validation, and update UI for password change functionality

This commit is contained in:
tom.hempel
2026-06-01 23:11:00 +02:00
parent e4d55f4c90
commit 43c47bca6d
13 changed files with 472 additions and 200 deletions

View File

@ -1,4 +1,5 @@
import { addRoute, startRouter, navigate } from './router.js';
import { ensureValidSession, invalidateSessionCheck } from './api.js';
import { loginPage } from './pages/login.js';
import { homeDashboardPage } from './pages/home.js';
import { questionnairesPage } from './pages/questionnaires.js';
@ -12,6 +13,7 @@ import { translationsPage } from './pages/translations.js';
import { devPage } from './pages/dev.js';
import { insightsPage } from './pages/insights.js';
import { coachesPage } from './pages/coaches.js';
import { openChangeOwnPasswordModal } from './password-modal.js';
// Auth state
export function isLoggedIn() {
@ -34,6 +36,7 @@ export function canEdit() {
}
function clearSession() {
invalidateSessionCheck();
localStorage.removeItem('qdb_token');
localStorage.removeItem('qdb_role');
localStorage.removeItem('qdb_user');
@ -44,12 +47,6 @@ function logout() {
navigate('#/login');
}
function rejectCoachSession() {
clearSession();
showToast('Coach accounts must use the mobile app.', 'error');
navigate('#/login');
}
/** Link to home dashboard (#/). */
export const HOME_HREF = '#/';
@ -97,9 +94,8 @@ export function showToast(message, type = 'info') {
}
function authGuard(handler) {
return (params) => {
if (!isLoggedIn()) { navigate('#/login'); return; }
if (!canAccessWebsite()) { rejectCoachSession(); return; }
return async (params) => {
if (!(await ensureValidSession())) return;
return handler(params);
};
}
@ -139,8 +135,8 @@ function updateNav() {
}
function roleGuard(roles, handler) {
return (params) => {
if (!isLoggedIn()) { navigate('#/login'); return; }
return async (params) => {
if (!(await ensureValidSession())) return;
if (!roles.includes(getRole())) { navigate('#/'); return; }
return handler(params);
};
@ -174,9 +170,12 @@ document.addEventListener('DOMContentLoaded', () => {
bindLogout(document.getElementById('logoutBtn'));
bindLogout(document.getElementById('mobileLogoutBtn'));
if (isLoggedIn() && !canAccessWebsite()) {
clearSession();
}
const openOwnPassword = async () => {
if (!(await ensureValidSession())) return;
openChangeOwnPasswordModal();
};
document.getElementById('changePasswordBtn')?.addEventListener('click', openOwnPassword);
document.getElementById('mobileChangePasswordBtn')?.addEventListener('click', openOwnPassword);
window.addEventListener('hashchange', updateNav);
updateNav();