diff --git a/admin/index.php b/admin/index.php index ad76944..d3aaae1 100644 --- a/admin/index.php +++ b/admin/index.php @@ -6,58 +6,46 @@ header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); require_once dirname(__DIR__) . '/db.php'; -// ── Cookie-based auth (no PHP sessions — avoids server caching/permission issues) ── -define('AUTH_COOKIE', 'parker_auth'); -define('AUTH_SECRET', hash('sha256', ADMIN_PASS . ADMIN_SESSION_KEY)); // not reversible - -function _authToken(): string { - $t = bin2hex(random_bytes(32)); - $e = time() + 86400; // 24h - $s = hash_hmac('sha256', "$t|$e", AUTH_SECRET); - return "$t.$e.$s"; +// ── URL-token auth (no cookies — works in all browsers) ─────────────────────── +function _createToken(): string { + $token = bin2hex(random_bytes(32)); + db()->prepare("INSERT INTO admin_tokens (token, expires_at) VALUES (?, ?)") + ->execute([$token, date('Y-m-d H:i:s', time() + 86400)]); + db()->exec("DELETE FROM admin_tokens WHERE expires_at < NOW()"); + return $token; } -function _verifyAuth(): bool { - $c = $_COOKIE[AUTH_COOKIE] ?? ''; - $p = explode('.', $c, 3); - if (count($p) !== 3) return false; - [$t, $e, $s] = $p; - if ((int)$e < time()) return false; - return hash_equals(hash_hmac('sha256', "$t|$e", AUTH_SECRET), $s); -} -function _setAuth(): void { - $secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; - setcookie(AUTH_COOKIE, _authToken(), [ - 'expires' => time() + 86400, - 'path' => '/admin/', - 'secure' => $secure, - 'httponly' => true, - 'samesite' => 'Lax', - ]); -} -function _clearAuth(): void { - $secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; - setcookie(AUTH_COOKIE, '', ['expires' => time()-3600, 'path' => '/admin/', 'secure' => $secure, 'httponly' => true, 'samesite' => 'Lax']); +function _verifyToken(string $token): bool { + if (!preg_match('/^[a-f0-9]{64}$/', $token)) return false; + $stmt = db()->prepare("SELECT token FROM admin_tokens WHERE token=? AND expires_at > NOW()"); + $stmt->execute([$token]); + return (bool)$stmt->fetch(); } $isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) || (($_SERVER['HTTP_ACCEPT'] ?? '') === 'application/json'); // ── Auth ────────────────────────────────────────────────────────────────────── if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'login') { - if ($_POST['username'] === ADMIN_USER && password_verify($_POST['password'] ?? '', ADMIN_PASS)) { - _setAuth(); + if (($_POST['username'] ?? '') === ADMIN_USER && password_verify($_POST['password'] ?? '', ADMIN_PASS)) { + $t = _createToken(); + header('Location: /admin/?_t=' . $t); + } else { + header('Location: /admin/?err=1'); } + exit; +} +$rawToken = preg_replace('/[^a-f0-9]/', '', $_GET['_t'] ?? $_POST['_t'] ?? ''); +if (($_GET['action'] ?? '') === 'logout') { + if ($rawToken) db()->prepare("DELETE FROM admin_tokens WHERE token=?")->execute([$rawToken]); header('Location: /admin/'); exit; } -if (($_GET['action'] ?? '') === 'logout') { - _clearAuth(); header('Location: /admin/'); exit; -} -$authed = _verifyAuth(); +$authed = $rawToken !== '' && _verifyToken($rawToken); +$token = $authed ? $rawToken : ''; // ── AJAX handlers ───────────────────────────────────────────────────────────── if ($isAjax && !$authed) { http_response_code(401); header('Content-Type: application/json'); - echo json_encode(['error'=>'Session expired. Please reload and log in again.']); + echo json_encode(['error'=>'Session expired. Please log in again.']); exit; } if ($isAjax) { @@ -280,6 +268,9 @@ button:hover{background:#ea580c}
Slingshot Rentals Management
+ +Invalid username or password.
+