usersFile = $usersFile ?? __DIR__ . '/../config/users.json'; // Load JWT secret from environment variable $this->jwtSecret = $_ENV['JWT_SECRET'] ?? getenv('JWT_SECRET'); if (empty($this->jwtSecret)) { throw new Exception('JWT_SECRET environment variable is not configured'); } } public function login(string $username, string $password): array { $users = $this->loadUsers(); foreach ($users['users'] as $user) { if ($user['username'] === $username) { if (password_verify($password, $user['password'])) { $token = $this->generateToken($user); return [ 'success' => true, 'token' => $token, 'user' => [ 'username' => $user['username'], 'role' => $user['role'] ] ]; } return ['success' => false, 'error' => 'Invalid password']; } } return ['success' => false, 'error' => 'User not found']; } public function verifyToken(string $token): ?array { $parts = explode('.', $token); if (count($parts) !== 3) { return null; } list($header, $payload, $signature) = $parts; // Verify signature $expectedSignature = base64_encode( hash_hmac('sha256', "$header.$payload", $this->jwtSecret, true) ); if (!hash_equals($expectedSignature, $signature)) { return null; } // Decode payload $payloadData = json_decode(base64_decode($payload), true); // Check expiry if (isset($payloadData['exp']) && $payloadData['exp'] < time()) { return null; } return $payloadData; } public function requireAuth(string $token): array { $payload = $this->verifyToken($token); if ($payload === null) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; } return $payload; } public function requireAdmin(string $token): array { $payload = $this->requireAuth($token); if ($payload['role'] !== 'admin') { http_response_code(403); echo json_encode(['error' => 'Forbidden']); exit; } return $payload; } private function generateToken(array $user): string { $header = base64_encode(json_encode([ 'alg' => self::JWT_ALGO, 'typ' => 'JWT' ])); $payload = base64_encode(json_encode([ 'username' => $user['username'], 'role' => $user['role'], 'iat' => time(), 'exp' => time() + self::JWT_EXPIRY ])); $signature = base64_encode( hash_hmac('sha256', "$header.$payload", $this->jwtSecret, true) ); return "$header.$payload.$signature"; } private function loadUsers(): array { if (!file_exists($this->usersFile)) { return ['users' => []]; } $content = file_get_contents($this->usersFile); return json_decode($content, true) ?? ['users' => []]; } public function hashPassword(string $password): string { return password_hash($password, PASSWORD_BCRYPT); } }