38438-vm/includes/auth_helper.php
2026-02-15 17:59:20 +00:00

94 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
session_start();
/**
* Authentication Helper
*/
class Auth {
public static function isLoggedIn(): bool {
return isset($_SESSION['user_id']);
}
public static function requireLogin(): void {
if (!self::isLoggedIn()) {
header('Location: login.php');
exit;
}
}
public static function login(int $userId, int $tenantId, string $role): void {
$_SESSION['user_id'] = $userId;
$_SESSION['tenant_id'] = $tenantId;
$_SESSION['role'] = $role;
// Ensure session is saved before any potential issues or redirects
session_write_close();
// Re-open session if we need to write more later (unlikely here but good practice if we were to)
session_start();
$ip = self::getIpAddress();
$country = self::getCountryFromIp($ip);
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;
try {
// Record session
$stmt = db()->prepare("INSERT INTO user_sessions (user_id, ip_address, country, user_agent) VALUES (?, ?, ?, ?)");
$stmt->execute([$userId, $ip, $country, $userAgent]);
// Update user
$stmt = db()->prepare("UPDATE users SET last_login_at = NOW(), last_login_ip = ? WHERE id = ?");
$stmt->execute([$ip, $userId]);
} catch (\Throwable $e) {
// Log error but don't prevent login
error_log("Auth::login tracking error: " . $e->getMessage());
}
}
public static function logout(): void {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$_SESSION = [];
session_destroy();
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 42000, '/');
}
header('Location: login.php', true, 302);
exit;
}
public static function getIpAddress(): string {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
} else {
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
}
public static function getCountryFromIp(string $ip): ?string {
if ($ip === '127.0.0.1' || $ip === '::1') return 'Localhost';
try {
$ctx = stream_context_create(['http' => ['timeout' => 2]]);
$resp = @file_get_contents("http://ip-api.com/json/{$ip}?fields=country", false, $ctx);
if ($resp) {
$data = json_decode($resp, true);
return $data['country'] ?? 'Unknown';
}
} catch (\Throwable $e) {
// Ignore errors for geolocation
}
return 'Unknown';
}
public static function recordResetAttempt(string $email, string $ip): void {
// We could log this to a separate table or activity_log
$stmt = db()->prepare("INSERT INTO activity_log (tenant_id, action, details) VALUES (?, ?, ?)");
$stmt->execute([0, 'Password Reset Attempt', "Email: $email, IP: $ip"]);
}
}