secret = $_ENV['JWT_SECRET'] ?? 'change-me'; $this->accessExpiry = (int)($_ENV['JWT_ACCESS_EXPIRY'] ?? 900); $this->refreshExpiry = (int)($_ENV['JWT_REFRESH_EXPIRY'] ?? 604800); } public function issueAccessToken(array $payload): string { $payload['exp'] = time() + $this->accessExpiry; $payload['iat'] = time(); $payload['jti'] = bin2hex(random_bytes(16)); return JWT::encode($payload, $this->secret, 'HS256'); } public function issueRefreshToken(string $userId): string { // Refresh token is a random string prefixed with userId for lookup $random = bin2hex(random_bytes(32)); return $userId . '.' . $random; } public function verifyToken(string $token): array { try { $decoded = JWT::decode($token, new Key($this->secret, 'HS256')); return (array) $decoded; } catch (Exception $e) { throw new Exception("Invalid or expired token: " . $e->getMessage()); } } }