139 lines
4.3 KiB
PHP
139 lines
4.3 KiB
PHP
<?php
|
||
// inc/auth_helpers.php – robustes Session-Handling (2025-09-05)
|
||
declare(strict_types=1);
|
||
|
||
/** ===== Session ===== */
|
||
function auth_start_session(array $cfg): void {
|
||
if (session_status() === PHP_SESSION_NONE) {
|
||
$name = $cfg['auth']['session_name'] ?? 'et_session';
|
||
session_name($name);
|
||
|
||
// Defaults
|
||
$cookiePath = $cfg['auth']['cookie_path'] ?? '/';
|
||
$cookieDomain = $cfg['auth']['cookie_domain'] ?? '';
|
||
$cookieSecure = $cfg['auth']['cookie_secure'] ?? true;
|
||
$cookieHttpOnly = $cfg['auth']['cookie_httponly'] ?? true;
|
||
$cookieSameSite = $cfg['auth']['cookie_samesite'] ?? 'Lax';
|
||
|
||
// PHP 7.3+ array API
|
||
session_set_cookie_params([
|
||
'lifetime' => 0,
|
||
'path' => $cookiePath,
|
||
'domain' => $cookieDomain,
|
||
'secure' => $cookieSecure,
|
||
'httponly' => $cookieHttpOnly,
|
||
'samesite' => $cookieSameSite,
|
||
]);
|
||
session_start();
|
||
}
|
||
}
|
||
|
||
/** ===== Auth Core ===== */
|
||
function auth_login(PDO $pdoCustomers, array $cfg, string $email, string $password): array {
|
||
auth_start_session($cfg);
|
||
|
||
$sql = "SELECT cu.id, cu.customer_id, cu.email, cu.password_hash, cu.role,
|
||
c.slug AS customer_slug, c.plan, c.status
|
||
FROM customer_users cu
|
||
JOIN customers c ON c.id = cu.customer_id
|
||
WHERE cu.email = :email AND cu.is_active = 1
|
||
LIMIT 1";
|
||
$st = $pdoCustomers->prepare($sql);
|
||
$st->execute([':email' => $email]);
|
||
$u = $st->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (!$u || !password_verify($password, $u['password_hash'])) {
|
||
return ['ok' => false, 'error' => 'invalid_credentials'];
|
||
}
|
||
if (($u['status'] ?? 'active') !== 'active') {
|
||
return ['ok' => false, 'error' => 'customer_inactive'];
|
||
}
|
||
|
||
// neue Session-ID, alte wird invalidiert
|
||
session_regenerate_id(true);
|
||
|
||
$_SESSION['user'] = [
|
||
'id' => (int)$u['id'],
|
||
'email' => $u['email'],
|
||
'role' => $u['role'],
|
||
'customer_id' => (int)$u['customer_id'],
|
||
'customer_slug' => $u['customer_slug'],
|
||
'plan' => $u['plan'],
|
||
];
|
||
return ['ok' => true, 'user' => $_SESSION['user']];
|
||
}
|
||
|
||
function auth_logout(array $cfg): void {
|
||
auth_start_session($cfg);
|
||
|
||
// Sessiondaten löschen
|
||
$_SESSION = [];
|
||
|
||
// Cookie-Parameter aus der aktiven Session
|
||
$params = session_get_cookie_params();
|
||
$name = session_name();
|
||
|
||
// Kandidaten für Domain/Path, um "falsch" gesetzte Cookies sicher zu treffen
|
||
$host = $_SERVER['HTTP_HOST'] ?? '';
|
||
$cfgDomain = $cfg['auth']['cookie_domain'] ?? '';
|
||
$paths = array_values(array_unique([$params['path'] ?? '/', '/', '']));
|
||
$domains = array_values(array_unique([
|
||
$params['domain'] ?? '',
|
||
$cfgDomain,
|
||
$host,
|
||
ltrim($host, '.'),
|
||
(strpos($host, '.') !== false ? '.' . ltrim($host, '.') : $host),
|
||
]));
|
||
|
||
// Alle Varianten invalidieren (secure/httponly wie gesetzt)
|
||
foreach ($paths as $p) {
|
||
foreach ($domains as $d) {
|
||
if ($d === null) continue;
|
||
setcookie($name, '', time() - 3600, $p, $d, $params['secure'] ?? true, $params['httponly'] ?? true);
|
||
}
|
||
// zusätzlich: ohne Domain (trifft Host-spezifische Cookies)
|
||
setcookie($name, '', time() - 3600, $p, '', $params['secure'] ?? true, $params['httponly'] ?? true);
|
||
}
|
||
|
||
// Session beenden
|
||
session_destroy();
|
||
session_write_close();
|
||
|
||
// In Staging aggressiv: Browser bitten, Cookies zu löschen (nicht jeder Browser respektiert das sofort)
|
||
if (($cfg['env'] ?? 'prod') === 'staging') {
|
||
header('Clear-Site-Data: "cookies"', false);
|
||
}
|
||
}
|
||
|
||
function auth_require(array $cfg): void {
|
||
auth_start_session($cfg);
|
||
if (empty($_SESSION['user'])) {
|
||
http_response_code(401);
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
echo json_encode(['ok' => false, 'error' => 'unauthorized']);
|
||
exit;
|
||
}
|
||
}
|
||
|
||
function require_role(array $cfg, array $roles): void {
|
||
auth_start_session($cfg);
|
||
$r = $_SESSION['user']['role'] ?? null;
|
||
if (!$r || !in_array($r, $roles, true)) {
|
||
http_response_code(403);
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
echo json_encode(['ok' => false, 'error' => 'forbidden']);
|
||
exit;
|
||
}
|
||
}
|
||
|
||
function current_user(array $cfg): ?array {
|
||
auth_start_session($cfg);
|
||
return $_SESSION['user'] ?? null;
|
||
}
|
||
|
||
function current_customer_id(array $cfg): ?int {
|
||
$u = current_user($cfg);
|
||
return $u['customer_id'] ?? null;
|
||
}
|
||
|