94 lines
3.2 KiB
PHP
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"]);
|
|
}
|
|
}
|