fix: persistent sessiebeheer (remember-me), HTTPS-cookie herstel en Microsoft OAuth signed state flow

This commit is contained in:
2026-09-08 15:28:57 +02:00
parent e1f50740aa
commit acef94536d
12 changed files with 278 additions and 29 deletions
+38 -6
View File
@@ -7,22 +7,54 @@
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/integrations/microsoft.php';
$user = require_auth();
start_secure_session();
// 1. CSRF State validatie & Sessieherstel
$receivedState = $_GET['state'] ?? '';
$stateUserId = !empty($receivedState) ? verify_oauth_state($receivedState) : null;
// Als de sessie verloren is gegaan tijdens de externe cross-site redirect vanaf Microsoft,
// herstellen we de gebruiker veilig aan de hand van de cryptografisch ondertekende OAuth state!
if (empty($_SESSION['user_id']) && $stateUserId) {
try {
$db = get_db_connection();
$stmtUser = $db->prepare("SELECT id, name, email, role, two_factor_enabled, created_at FROM users WHERE id = ?");
$stmtUser->execute([$stateUserId]);
$recoveredUser = $stmtUser->fetch();
if ($recoveredUser) {
$_SESSION['user_id'] = $recoveredUser['id'];
$_SESSION['user_name'] = $recoveredUser['name'];
$_SESSION['user_email'] = $recoveredUser['email'];
$_SESSION['user_role'] = $recoveredUser['role'];
$_SESSION['is_2fa_verified'] = true;
issue_remember_token((int)$recoveredUser['id']);
}
} catch (Throwable $e) {
// Foutafhandeling
}
}
// 2. Controleer authenticatie
$user = get_logged_in_user();
if (!$user) {
header('Location: /auth/login.php?error=' . urlencode('Sessie verlopen tijdens Microsoft koppeling. Log opnieuw in en probeer het nogmaals.'));
exit;
}
$db = get_db_connection();
// 1. Foutafhandeling vanuit Microsoft
// 3. Foutafhandeling vanuit Microsoft (bijv. gebruiker weigerde machtiging)
if (!empty($_GET['error'])) {
$errorDescription = $_GET['error_description'] ?? $_GET['error'];
header('Location: /integrations.php?error=' . urlencode("Microsoft melding: {$errorDescription}"));
exit;
}
// 2. CSRF State validatie
$receivedState = $_GET['state'] ?? '';
// 4. Valideer de state (via HMAC handtekening of fallback sessiestate)
$expectedState = $_SESSION['ms_oauth_state'] ?? '';
unset($_SESSION['ms_oauth_state']);
unset($_SESSION['ms_oauth_state'], $_SESSION['ms_oauth_user_id']);
if (empty($receivedState) || !hash_equals($expectedState, $receivedState)) {
if (!$stateUserId && (empty($receivedState) || empty($expectedState) || !hash_equals($expectedState, $receivedState))) {
header('Location: /integrations.php?error=' . urlencode('Ongeldige sessie of CSRF-controle mislukt. Probeer opnieuw in te loggen via Microsoft.'));
exit;
}
+1
View File
@@ -81,6 +81,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['user_email'] = $invite['email'];
$_SESSION['user_role'] = $invite['role'];
$_SESSION['is_2fa_verified'] = true;
issue_remember_token($newUserId);
unset($_SESSION['invite_totp_secret']);
header('Location: /planner.php');
+6 -2
View File
@@ -12,8 +12,9 @@ if (!has_admin_user()) {
exit;
}
// Als al volledig ingelogd en 2FA geverifieerd, ga naar planner
if (!empty($_SESSION['user_id']) && !empty($_SESSION['is_2fa_verified'])) {
// Als al ingelogd (via actieve sessie of persistente cookie), ga direct naar planner
$currentUser = get_logged_in_user();
if ($currentUser) {
header('Location: /planner.php');
exit;
}
@@ -96,6 +97,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['user_role'] = $user['role'];
$_SESSION['is_2fa_verified'] = true;
// Activeer persistente remember token (30 dagen blijf ingelogd)
issue_remember_token((int)$user['id']);
unset($_SESSION['pending_2fa_user_id'], $_SESSION['pending_2fa_user_email'], $_SESSION['pending_2fa_user_name']);
header('Location: /planner.php');
+15
View File
@@ -6,6 +6,21 @@
require_once __DIR__ . '/../includes/auth.php';
start_secure_session();
// Vernietig persistente login in database
if (!empty($_SESSION['user_id'])) {
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([$_SESSION['user_id']]);
} catch (Throwable $e) {
// Stil negeren bij uitloggen
}
}
// Verwijder remember cookie
clear_remember_cookie();
// Vernietig alle sessie variabelen
$_SESSION = [];
+3 -3
View File
@@ -31,10 +31,10 @@ if (empty($clientId)) {
// Bepaal de dynamische of geconfigureerde redirect URI
$redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
// Genereer unieke CSRF state
$state = bin2hex(random_bytes(16));
// Genereer cryptografisch ondertekende OAuth state waarin de user_id en timestamp veilig zijn opgeslagen
$state = generate_oauth_state((int)$user['id']);
$_SESSION['ms_oauth_state'] = $state;
$_SESSION['ms_oauth_user_id'] = $user['id'];
$_SESSION['ms_oauth_user_id'] = (int)$user['id'];
// Stuur gebruiker door naar Microsoft login
$authUrl = microsoft_get_auth_url($clientId, $redirectUri, $tenantId, $state);
+1
View File
@@ -68,6 +68,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['user_email'] = $email;
$_SESSION['user_role'] = 'ADMIN';
$_SESSION['is_2fa_verified'] = true;
issue_remember_token($adminId);
unset($_SESSION['setup_totp_secret']);
$success = true;