pdo = $pdo; $this->jwtService = $jwtService ?? new JwtService([ 'secret' => getenv('JWT_SECRET') ?: 'your-secret-key-change-this-in-production', 'algo' => $this->jwtAlgo, 'expiration' => 3600, 'leeway' => 60 ]); $this->checkSession(); } public function checkSession(): void { if (isset($_SESSION['user_id'])) { $user = User::findByUsername($this->pdo, $_SESSION['username']); if (!$user || !$user['is_active']) { $this->logout(); } else { $this->user = $user; } } } public function login(string $username, string $password, string $ip = null): bool { $user = User::findByUsername($this->pdo, $username); if (!$user || !$user['is_active']) { return false; } // Verify password directly using the hash from database if (!password_verify($password, $user['password'])) { return false; } // Set session $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username']; $_SESSION['role'] = $user['role']; // Update last login $this->updateUserLastLogin($user['id'], $ip); $this->user = $user; return true; } public function logout(): void { unset($_SESSION['user_id']); unset($_SESSION['username']); unset($_SESSION['role']); $this->user = null; } public function getCurrentUser(): ?array { return $this->user; } public function isLoggedIn(): bool { return $this->user !== null; } public function isAdmin(): bool { return $this->isLoggedIn() && $this->user['role'] === 'admin'; } public function requireLogin(): void { if (!$this->isLoggedIn()) { header('Location: /login'); exit; } } public function requireAdmin(): void { $this->requireLogin(); if (!$this->isAdmin()) { http_response_code(403); echo 'Access denied. Admin privileges required.'; exit; } } public function generateToken(array $user): array { $now = time(); $payload = [ 'sub' => $user['id'], 'username' => $user['username'], 'role' => $user['role'] ?? 'user', 'iat' => $now, 'exp' => $now + 3600, // 1 hour expiration 'jti' => bin2hex(random_bytes(16)) ]; $token = $this->jwtService->encode($payload); $refreshToken = $this->generateRefreshToken($user['id']); return [ 'token' => $token, 'refresh_token' => $refreshToken, 'expires_in' => 3600, 'token_type' => 'Bearer' ]; } public function refreshToken(string $refreshToken): ?array { // Verify refresh token (you might want to store and validate this in your database) $stmt = $this->pdo->prepare("SELECT user_id FROM refresh_tokens WHERE token = :token AND expires_at > NOW()"); $stmt->execute(['token' => $refreshToken]); $tokenData = $stmt->fetch(PDO::FETCH_ASSOC); if (!$tokenData) { return null; } $user = (new User($this->pdo))->find($tokenData['user_id']); if (!$user) { return null; } // Generate new tokens return $this->generateToken($user); } private function generateRefreshToken(int $userId): string { $token = bin2hex(random_bytes(32)); $expiresAt = date('Y-m-d H:i:s', time() + (30 * 24 * 3600)); // 30 days $stmt = $this->pdo->prepare(" INSERT INTO refresh_tokens (user_id, token, expires_at, created_at) VALUES (:user_id, :token, :expires_at, NOW()) ON DUPLICATE KEY UPDATE token = VALUES(token), expires_at = VALUES(expires_at), created_at = NOW() "); $stmt->execute([ 'user_id' => $userId, 'token' => $token, 'expires_at' => $expiresAt ]); return $token; } public function validateToken(string $token): ?array { try { $payload = $this->jwtService->decode($token); if (!$payload) { return null; } // Optionally check if user still exists and is active $user = (new User($this->pdo))->find($payload['sub']); if (!$user || !$user['is_active']) { return null; } return $payload; } catch (\Exception $e) { return null; } } public function generateCSRFToken(): string { if (!isset($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token']; } public function verifyCSRFToken(string $token): bool { return isset($_SESSION['csrf_token']) && $_SESSION['csrf_token'] === $token; } private function updateUserLastLogin(int $userId, string $ip = null): void { $stmt = $this->pdo->prepare("UPDATE users SET last_login_at = :last_login_at, login_ip = :login_ip WHERE id = :id"); $stmt->execute([ 'id' => $userId, 'last_login_at' => date('Y-m-d H:i:s'), 'login_ip' => $ip ]); } }