Add comprehensive session security to admin panel
Security improvements: - Auto-logout after 30 minutes of inactivity - Session warning 5 minutes before expiry with option to extend - Activity-based session extension on user interaction - Session validation on page load and API calls - Periodic session validity checks every minute - Secure cleanup of tokens and timers on logout - Protection against expired session usage This prevents unauthorized access if admin leaves session open or if tokens are compromised.
This commit is contained in:
parent
d9559f71fe
commit
570fd92d00
1 changed files with 117 additions and 9 deletions
126
public/admin.js
126
public/admin.js
|
@ -2,6 +2,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
let authToken = localStorage.getItem('adminToken');
|
||||
let allLocations = [];
|
||||
let editingRowId = null;
|
||||
let sessionTimeout = null;
|
||||
let warningTimeout = null;
|
||||
|
||||
// Session timeout settings (in milliseconds)
|
||||
const SESSION_DURATION = 30 * 60 * 1000; // 30 minutes
|
||||
const WARNING_TIME = 5 * 60 * 1000; // Show warning 5 minutes before expiry
|
||||
|
||||
const loginSection = document.getElementById('login-section');
|
||||
const adminSection = document.getElementById('admin-section');
|
||||
|
@ -11,10 +17,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
const refreshBtn = document.getElementById('refresh-btn');
|
||||
const locationsTableBody = document.getElementById('locations-tbody');
|
||||
|
||||
// Check if already logged in
|
||||
// Check if already logged in and session is still valid
|
||||
if (authToken) {
|
||||
showAdminSection();
|
||||
loadLocations();
|
||||
const loginTime = localStorage.getItem('adminLoginTime');
|
||||
const now = Date.now();
|
||||
|
||||
if (loginTime && (now - parseInt(loginTime)) < SESSION_DURATION) {
|
||||
showAdminSection();
|
||||
loadLocations();
|
||||
startSessionTimer();
|
||||
} else {
|
||||
// Session expired
|
||||
logout('Session expired. Please log in again.');
|
||||
}
|
||||
}
|
||||
|
||||
// Login form handler
|
||||
|
@ -34,8 +49,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
if (response.ok) {
|
||||
authToken = data.token;
|
||||
localStorage.setItem('adminToken', authToken);
|
||||
localStorage.setItem('adminLoginTime', Date.now().toString());
|
||||
showAdminSection();
|
||||
loadLocations();
|
||||
startSessionTimer();
|
||||
} else {
|
||||
showMessage(loginMessage, data.error, 'error');
|
||||
}
|
||||
|
@ -46,9 +63,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
|
||||
// Logout handler
|
||||
logoutBtn.addEventListener('click', () => {
|
||||
authToken = null;
|
||||
localStorage.removeItem('adminToken');
|
||||
showLoginSection();
|
||||
logout('Logged out successfully.');
|
||||
});
|
||||
|
||||
// Refresh button handler
|
||||
|
@ -78,6 +93,101 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
function hideMessage(element) {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
|
||||
// Session management functions
|
||||
function startSessionTimer() {
|
||||
clearSessionTimers();
|
||||
|
||||
// Set warning timer (5 minutes before expiry)
|
||||
warningTimeout = setTimeout(() => {
|
||||
showSessionWarning();
|
||||
}, SESSION_DURATION - WARNING_TIME);
|
||||
|
||||
// Set logout timer (30 minutes)
|
||||
sessionTimeout = setTimeout(() => {
|
||||
logout('Session expired due to inactivity.');
|
||||
}, SESSION_DURATION);
|
||||
}
|
||||
|
||||
function clearSessionTimers() {
|
||||
if (sessionTimeout) {
|
||||
clearTimeout(sessionTimeout);
|
||||
sessionTimeout = null;
|
||||
}
|
||||
if (warningTimeout) {
|
||||
clearTimeout(warningTimeout);
|
||||
warningTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
function showSessionWarning() {
|
||||
const extend = confirm('Your admin session will expire in 5 minutes. Click OK to extend your session, or Cancel to log out now.');
|
||||
if (extend) {
|
||||
extendSession();
|
||||
} else {
|
||||
logout('Session ended by user.');
|
||||
}
|
||||
}
|
||||
|
||||
function extendSession() {
|
||||
// Update login time and restart timers
|
||||
localStorage.setItem('adminLoginTime', Date.now().toString());
|
||||
startSessionTimer();
|
||||
console.log('Admin session extended for 30 minutes');
|
||||
}
|
||||
|
||||
function logout(message = 'Logged out.') {
|
||||
clearSessionTimers();
|
||||
authToken = null;
|
||||
localStorage.removeItem('adminToken');
|
||||
localStorage.removeItem('adminLoginTime');
|
||||
showLoginSection();
|
||||
if (message) {
|
||||
showMessage(loginMessage, message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Reset session timer on user activity
|
||||
function resetSessionTimer() {
|
||||
if (authToken && adminSection.style.display !== 'none') {
|
||||
const loginTime = localStorage.getItem('adminLoginTime');
|
||||
const now = Date.now();
|
||||
|
||||
// Only reset if we're within the session duration
|
||||
if (loginTime && (now - parseInt(loginTime)) < SESSION_DURATION) {
|
||||
localStorage.setItem('adminLoginTime', now.toString());
|
||||
startSessionTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for user activity to reset session timer
|
||||
const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];
|
||||
let activityTimer = null;
|
||||
|
||||
activityEvents.forEach(event => {
|
||||
document.addEventListener(event, () => {
|
||||
// Throttle activity detection to once per minute
|
||||
if (!activityTimer) {
|
||||
activityTimer = setTimeout(() => {
|
||||
resetSessionTimer();
|
||||
activityTimer = null;
|
||||
}, 60000); // 1 minute throttle
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
|
||||
// Check session validity periodically
|
||||
setInterval(() => {
|
||||
if (authToken) {
|
||||
const loginTime = localStorage.getItem('adminLoginTime');
|
||||
const now = Date.now();
|
||||
|
||||
if (!loginTime || (now - parseInt(loginTime)) >= SESSION_DURATION) {
|
||||
logout('Session expired.');
|
||||
}
|
||||
}
|
||||
}, 60000); // Check every minute
|
||||
|
||||
async function loadLocations() {
|
||||
try {
|
||||
|
@ -87,9 +197,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
|
||||
if (response.status === 401) {
|
||||
// Token expired or invalid
|
||||
authToken = null;
|
||||
localStorage.removeItem('adminToken');
|
||||
showLoginSection();
|
||||
logout('Session expired. Please log in again.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue