diff --git a/assets/js/notifications.js b/assets/js/notifications.js
index cb472a4..884c8ac 100644
--- a/assets/js/notifications.js
+++ b/assets/js/notifications.js
@@ -254,6 +254,10 @@
isFetchingEvents = true;
try {
const res = await fetch('/api/notifications.php?action=get_events', { cache: 'no-store' });
+ if (res.status === 401) {
+ window.location.href = '/auth/login.php?expired=1';
+ return;
+ }
if (res.ok) {
const data = await res.json();
if (data && data.success) {
diff --git a/auth/callback_microsoft.php b/auth/callback_microsoft.php
index a045b3a..d397439 100644
--- a/auth/callback_microsoft.php
+++ b/auth/callback_microsoft.php
@@ -29,7 +29,7 @@ if (empty($_SESSION['user_id']) && $stateUserId) {
$_SESSION['is_2fa_verified'] = true;
$_SESSION['auth_date'] = date('Y-m-d');
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
- issue_remember_token((int)$recoveredUser['id']);
+ clear_remember_cookie();
}
} catch (Throwable $e) {
// Foutafhandeling
diff --git a/auth/invite.php b/auth/invite.php
index 915d2f1..d5590d7 100644
--- a/auth/invite.php
+++ b/auth/invite.php
@@ -83,7 +83,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['is_2fa_verified'] = true;
$_SESSION['auth_date'] = date('Y-m-d');
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
- issue_remember_token($newUserId);
+ clear_remember_cookie();
unset($_SESSION['invite_totp_secret']);
header('Location: /planner.php');
diff --git a/auth/login.php b/auth/login.php
index 37d9054..deae950 100644
--- a/auth/login.php
+++ b/auth/login.php
@@ -108,8 +108,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['auth_date'] = date('Y-m-d');
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
- // Activeer dagspecifieke remember token (verloopt vannacht om 23:59:59)
- issue_remember_token((int)$user['id']);
+ // Zorg dat er geen persistente remember cookies worden gebruikt
+ clear_remember_cookie();
unset($_SESSION['pending_2fa_user_id'], $_SESSION['pending_2fa_user_email'], $_SESSION['pending_2fa_user_name']);
diff --git a/auth/setup.php b/auth/setup.php
index ffe70d8..3e008e1 100644
--- a/auth/setup.php
+++ b/auth/setup.php
@@ -70,7 +70,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['is_2fa_verified'] = true;
$_SESSION['auth_date'] = date('Y-m-d');
$_SESSION['session_expires_at'] = strtotime('today 23:59:59');
- issue_remember_token($adminId);
+ clear_remember_cookie();
unset($_SESSION['setup_totp_secret']);
$success = true;
diff --git a/includes/auth.php b/includes/auth.php
index f0b7364..0e65793 100644
--- a/includes/auth.php
+++ b/includes/auth.php
@@ -76,6 +76,9 @@ function ensure_remember_token_columns(PDO $db): void {
$result = $db->query("SHOW COLUMNS FROM `users` LIKE 'remember_token'");
if (!$result || $result->rowCount() === 0) {
$db->exec("ALTER TABLE `users` ADD COLUMN `remember_token` VARCHAR(64) NULL AFTER `backup_codes`, ADD COLUMN `remember_token_expires_at` DATETIME NULL AFTER `remember_token`");
+ } else {
+ // Maak alle bestaande actieve tokens direct leeg zodat geen enkele oude persistente browser-sessie blijft bestaan
+ $db->exec("UPDATE `users` SET `remember_token` = NULL, `remember_token_expires_at` = NULL WHERE `remember_token` IS NOT NULL");
}
} catch (Throwable $e) {
// Stil negeren indien al aanwezig of tijdens migraties
@@ -119,35 +122,23 @@ function ensure_theme_column(PDO $db): void {
}
/**
- * Genereert en bewaart een dagspecifieke login token in MariaDB en plaatst een veilige cookie.
- * Verloopt altijd dezelfde nacht om 23:59:59 zodat sessies niet meerdere dagen blijven staan.
+ * Verouderde functie voor persistent login.
+ * Bewust geneutraliseerd: cookies worden direct gewist en geen tokens meer aangemaakt,
+ * zodat gebruikers iedere dag om 23:59:59 worden uitgelogd en daadwerkelijk opnieuw moeten inloggen.
*/
function issue_remember_token(int $userId): string {
- $rawToken = bin2hex(random_bytes(32));
- $tokenHash = hash('sha256', $rawToken);
- $midnightTs = strtotime('today 23:59:59');
- $expiresAt = date('Y-m-d 23:59:59', $midnightTs);
-
- $db = get_db_connection();
- ensure_remember_token_columns($db);
-
- $stmt = $db->prepare("UPDATE users SET remember_token = ?, remember_token_expires_at = ? WHERE id = ?");
- $stmt->execute([$tokenHash, $expiresAt, $userId]);
-
- $isSecure = is_https_request();
- setcookie('fociz_remember', $rawToken, [
- 'expires' => $midnightTs, // Verloopt vannacht om 23:59:59
- 'path' => '/',
- 'secure' => $isSecure,
- 'httponly' => true,
- 'samesite' => 'Lax'
- ]);
-
- return $rawToken;
+ clear_remember_cookie();
+ try {
+ $db = get_db_connection();
+ ensure_remember_token_columns($db);
+ $stmt = $db->prepare("UPDATE users SET remember_token = NULL, remember_token_expires_at = NULL WHERE id = ?");
+ $stmt->execute([$userId]);
+ } catch (Throwable $e) {}
+ return '';
}
/**
- * Verwijdert de persistente remember cookie
+ * Verwijdert de persistente remember cookie uit de browser
*/
function clear_remember_cookie(): void {
$isSecure = is_https_request();
@@ -158,11 +149,15 @@ function clear_remember_cookie(): void {
'httponly' => true,
'samesite' => 'Lax'
]);
+ if (isset($_COOKIE['fociz_remember'])) {
+ unset($_COOKIE['fociz_remember']);
+ }
}
/**
* Haalt de huidige ingelogde gebruiker op uit de database.
* Garandeert dat de sessie strikt om 23:59:59 gereset wordt en nooit meerdere dagen blijft staan.
+ * Bevat GEEN persistente cookies of auto-login bypass: de gebruiker moet iedere dag daadwerkelijk opnieuw inloggen.
*/
function get_logged_in_user(): ?array {
start_secure_session();
@@ -170,18 +165,22 @@ function get_logged_in_user(): ?array {
$today = date('Y-m-d');
$now = time();
+ // Verwijder altijd eventuele oude of achtergebleven fociz_remember cookies
+ if (isset($_COOKIE['fociz_remember'])) {
+ clear_remember_cookie();
+ }
+
// Controleer of de sessie van een eerdere dag is of voorbij 23:59:59
+ $isExpired = false;
if (!empty($_SESSION['auth_date']) && $_SESSION['auth_date'] !== $today) {
- $_SESSION = [];
- clear_remember_cookie();
- if (session_status() === PHP_SESSION_ACTIVE) {
- @session_regenerate_id(true);
- }
- return null;
+ $isExpired = true;
}
-
if (!empty($_SESSION['session_expires_at']) && $now > (int)$_SESSION['session_expires_at']) {
- $_SESSION = [];
+ $isExpired = true;
+ }
+
+ if ($isExpired) {
+ $_SESSION = ['session_was_expired' => true];
clear_remember_cookie();
if (session_status() === PHP_SESSION_ACTIVE) {
@session_regenerate_id(true);
@@ -189,11 +188,10 @@ function get_logged_in_user(): ?array {
return null;
}
- $db = get_db_connection();
- ensure_remember_token_columns($db);
-
- // 1. Controleer actieve PHP sessie
+ // Controleer actieve PHP sessie (uitsluitend de sessie van vandaag is geldig)
if (!empty($_SESSION['user_id']) && !empty($_SESSION['is_2fa_verified'])) {
+ $db = get_db_connection();
+ ensure_remember_token_columns($db);
$stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, created_at FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
@@ -207,38 +205,6 @@ function get_logged_in_user(): ?array {
clear_remember_cookie();
}
- // 2. Controleer dagspecifieke cookie (uitsluitend geldig voor vandaag tot 23:59:59)
- if (!empty($_COOKIE['fociz_remember'])) {
- $rawToken = trim($_COOKIE['fociz_remember']);
- $tokenHash = hash('sha256', $rawToken);
-
- $stmt = $db->prepare("
- SELECT id, name, email, role, two_factor_enabled, created_at
- FROM users
- WHERE remember_token = ? AND remember_token_expires_at > NOW()
- ");
- $stmt->execute([$tokenHash]);
- $user = $stmt->fetch();
-
- if ($user) {
- // Herstel PHP sessie voor de huidige werkdag
- $_SESSION['user_id'] = $user['id'];
- $_SESSION['user_name'] = $user['name'];
- $_SESSION['user_email'] = $user['email'];
- $_SESSION['user_role'] = $user['role'];
- $_SESSION['is_2fa_verified'] = true;
- $_SESSION['auth_date'] = $today;
- $_SESSION['session_expires_at'] = strtotime('today 23:59:59');
-
- // Verleng uitsluitend tot 23:59:59 van vandaag
- issue_remember_token((int)$user['id']);
-
- return $user;
- } else {
- clear_remember_cookie();
- }
- }
-
return null;
}
@@ -264,7 +230,7 @@ function require_auth(bool $json = false): array {
exit;
}
- // 2. Controleer of gebruiker ingelogd én 2FA geverifieerd is
+ // 2. Controleer of gebruiker ingelogd én 2FA geverifieerd is voor vandaag
$user = get_logged_in_user();
if (!$user) {
if ($isAjax) {
@@ -273,7 +239,13 @@ function require_auth(bool $json = false): array {
echo json_encode(['success' => false, 'error' => 'Niet geauthenticeerd of sessie verlopen']);
exit;
}
- header('Location: /auth/login.php');
+ $wasExpired = !empty($_SESSION['session_was_expired']);
+ unset($_SESSION['session_was_expired']);
+ if ($wasExpired) {
+ header('Location: /auth/login.php?expired=1');
+ } else {
+ header('Location: /auth/login.php');
+ }
exit;
}
diff --git a/includes/header.php b/includes/header.php
index ccec3a2..f1155e1 100644
--- a/includes/header.php
+++ b/includes/header.php
@@ -57,10 +57,20 @@ $currentUserTheme = !empty($currentUser['id']) ? get_user_theme((int)$currentUse
-
+