0, // Session-Cookie (endet mit Browser), Remember-me macht "lang" 'path' => '/', 'domain' => '', 'secure' => $secure, 'httponly' => true, 'samesite' => 'Lax', ]); session_start(); require_once "config/config.php"; // -------------------- Auto-Login via remember cookie (if no session) + ROTATION -------------------- if ((!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true) && !empty($_COOKIE['remember'])) { try { // optional housekeeping $pdo->exec("DELETE FROM remember_tokens WHERE expires_at < NOW()"); $oldToken = (string)$_COOKIE['remember']; $oldHash = hash('sha256', $oldToken); $stmt = $pdo->prepare(" SELECT rt.id AS token_id, rt.user_id, UNIX_TIMESTAMP(rt.expires_at) AS exp, u.username, u.name, u.role FROM remember_tokens rt JOIN users u ON u.id = rt.user_id WHERE rt.token_hash = :th LIMIT 1 "); $stmt->execute([':th' => $oldHash]); $row = $stmt->fetch(PDO::FETCH_ASSOC); if ($row && (int)$row['exp'] > time()) { // ✅ valid token -> log in session_regenerate_id(true); $_SESSION["loggedin"] = true; $_SESSION["id"] = (int)$row["user_id"]; $_SESSION["username"] = $row["username"]; $_SESSION["name"] = $row["name"]; $_SESSION["role"] = $row["role"]; // last_login auch bei Auto-Login aktualisieren $upd = $pdo->prepare("UPDATE users SET last_login = :ts WHERE id = :id"); $upd->execute([ ':ts' => time(), ':id' => (int)$row['user_id'] ]); // ✅ TOKEN ROTATION: replace old token with a fresh one & extend expiry to 7 days $newToken = bin2hex(random_bytes(32)); $newHash = hash('sha256', $newToken); $newExp = time() + 7 * 24 * 60 * 60; // 7 days $pdo->beginTransaction(); try { // Update the existing token row (keeps 1 token per "cookie session") $upd = $pdo->prepare(" UPDATE remember_tokens SET token_hash = :new_hash, expires_at = FROM_UNIXTIME(:exp) WHERE id = :tid LIMIT 1 "); $upd->execute([ ':new_hash' => $newHash, ':exp' => $newExp, ':tid' => (int)$row['token_id'], ]); $pdo->commit(); } catch (Throwable $e) { $pdo->rollBack(); // If rotation fails, fail closed-ish: // We'll still keep the session (user is in), but we remove cookie to avoid loops. setcookie('remember', '', [ 'expires' => time() - 3600, 'path' => '/', 'secure' => $secure, 'httponly' => true, 'samesite' => 'Lax', ]); header("location: dashboard.php"); exit; } // Set new cookie setcookie('remember', $newToken, [ 'expires' => $newExp, 'path' => '/', 'secure' => $secure, 'httponly' => true, 'samesite' => 'Lax', ]); header("location: dashboard.php"); exit; } else { // invalid/expired token -> delete cookie setcookie('remember', '', [ 'expires' => time() - 3600, 'path' => '/', 'secure' => $secure, 'httponly' => true, 'samesite' => 'Lax', ]); } } catch (Throwable $e) { // fail closed (do nothing, user stays logged out) } } // If already logged in -> go dashboard if (isset($_SESSION["loggedin"]) && $_SESSION["loggedin"] === true) { header("location: dashboard.php"); exit; } $username = $password = ""; $username_err = $password_err = ""; if ($_SERVER["REQUEST_METHOD"] === "POST") { // Username prüfen if (empty(trim($_POST["username"] ?? ""))) { $username_err = "Bitte Username eingeben."; } else { $username = trim((string)$_POST["username"]); } $password = (string)($_POST["password"] ?? ""); if (empty($username_err) && empty($password_err)) { $sql = " SELECT * FROM users WHERE username = :username LIMIT 1 "; $stmt = $pdo->prepare($sql); $stmt->bindParam(":username", $username, PDO::PARAM_STR); if ($stmt->execute()) { if ($stmt->rowCount() === 1) { $user = $stmt->fetch(PDO::FETCH_ASSOC); $hash = $user['password_hash'] ?? ''; $first = (int)($user['first_login'] ?? 0); // 1️⃣ Nur wenn wirklich First Login ODER kein Hash vorhanden if ($first === 1 || empty($hash)) { $_SESSION['set_pw_user'] = (int)$user['id']; header("location: set_password.php"); exit; } // 2️⃣ Passwort prüfen if (!password_verify($password, $hash)) { $password_err = "Falsches Passwort."; } else { session_regenerate_id(true); $_SESSION["loggedin"] = true; $_SESSION["id"] = (int)$user["id"]; $_SESSION["username"] = $user["username"]; $_SESSION["name"] = $user["name"]; $_SESSION["role"] = $user["role"]; // Remember-me (7 Tage), nur wenn Checkbox gesetzt if (!empty($_POST['remember'])) { $token = bin2hex(random_bytes(32)); $tokenHash = hash('sha256', $token); $expires = time() + 7 * 24 * 60 * 60; // 7 days // optional housekeeping $pdo->exec("DELETE FROM remember_tokens WHERE expires_at < NOW()"); $ins = $pdo->prepare(" INSERT INTO remember_tokens (user_id, token_hash, expires_at) VALUES (:uid, :th, FROM_UNIXTIME(:exp)) "); $ins->execute([ ':uid' => (int)$user['id'], ':th' => $tokenHash, ':exp' => $expires ]); setcookie('remember', $token, [ 'expires' => $expires, 'path' => '/', 'secure' => $secure, 'httponly' => true, 'samesite' => 'Lax', ]); } // last_login setzen $upd = $pdo->prepare("UPDATE users SET last_login = :ts WHERE id = :id"); $upd->execute([ ':ts' => time(), ':id' => (int)$user['id'] ]); header("location: dashboard.php"); exit; } } else { $username_err = "Dieser Benutzer existiert nicht."; } } else { echo "Fehler. Bitte später erneut versuchen."; } } } ?>