isAuthenticated()) { if ($this->expectsJson($request)) { return new JsonResponse([ 'success' => false, 'message' => __('auth.unauthenticated'), 'timestamp' => time() ], 401); } // Redirect to login page for web requests return new Response('', 302, [ 'Location' => url('/login') ]); } // Check if user session is still valid if (!$this->isSessionValid()) { $this->logout(); if ($this->expectsJson($request)) { return new JsonResponse([ 'success' => false, 'message' => __('auth.session_expired'), 'timestamp' => time() ], 401); } return new Response('', 302, [ 'Location' => url('/login?expired=1') ]); } // Set user in request for easy access $request->attributes->set('user', $this->getCurrentUser()); return $next($request); } /** * Check if user is authenticated. * * @return bool */ protected function isAuthenticated(): bool { if (isset($_SESSION['user_id']) && !empty($_SESSION['user_id'])) { return true; } if (!empty($_COOKIE['remember_token'])) { $secret = $_ENV['APP_KEY'] ?? ''; $parts = explode(':', $_COOKIE['remember_token']); if (count($parts) === 3) { [$id, $token, $sig] = $parts; $payload = $id . ':' . $token; $expected = hash_hmac('sha256', $payload, $secret); if (hash_equals($expected, $sig)) { try { $user = User::find((int)$id); if ($user && $user->users_remember_token === $token) { $_SESSION['user_id'] = $user->users_id; $_SESSION['user_idbus'] = $user->users_bus_id; $_SESSION['user_role'] = $user->users_role; $_SESSION['last_activity'] = time(); return true; } } catch (\Throwable $e) {} } } } return false; } /** * Check if session is still valid. * * @return bool */ protected function isSessionValid(): bool { if (!isset($_SESSION['last_activity'])) { return false; } $timeoutSetting = 120; try { if (class_exists('\App\Models\Module')) { $module = \App\Models\Module::byName('auth')->first(); if ($module) { $timeoutSetting = (int)($module->getSetting('session_timeout', 120)); } } } catch (\Throwable $e) {} $sessionTimeout = (int)($_ENV['SESSION_LIFETIME'] ?? $timeoutSetting) * 60; $lastActivity = $_SESSION['last_activity']; $currentTime = time(); // Check if session has expired if (($currentTime - $lastActivity) > $sessionTimeout) { return false; } // Update last activity time $_SESSION['last_activity'] = $currentTime; return true; } /** * Get current authenticated user. * * @return User|null */ protected function getCurrentUser(): ?User { if (!$this->isAuthenticated()) { return null; } try { return User::find($_SESSION['user_id']); } catch (\Exception $e) { // Log error and invalidate session error_log("Error loading user: " . $e->getMessage()); $this->logout(); return null; } } /** * Check if request expects JSON response. * * @param Request $request * @return bool */ protected function expectsJson(Request $request): bool { return $request->headers->get('Accept') === 'application/json' || $request->headers->get('Content-Type') === 'application/json' || str_contains($request->getPathInfo(), '/api/'); } /** * Logout user and destroy session. * * @return void */ protected function logout(): void { // Unset all session variables unset($_SESSION['user_id']); unset($_SESSION['user_idbus']); unset($_SESSION['user_role']); unset($_SESSION['last_activity']); unset($_SESSION['csrf_token']); // Destroy session session_destroy(); // Start new session for possible flash messages if (session_status() === PHP_SESSION_NONE) { session_start(); } } }