fix(control-ui): share token scope across loopback hosts

This commit is contained in:
ssdiwu 2026-03-11 22:19:50 +08:00
parent 62d5df28dc
commit 894ba946e4
2 changed files with 95 additions and 1 deletions

View File

@ -207,6 +207,90 @@ describe("loadSettings default gateway URL derivation", () => {
});
});
it("reuses a session token across loopback host aliases", async () => {
setTestLocation({
protocol: "http:",
host: "127.0.0.1:18789",
pathname: "/",
});
const { loadSettings, saveSettings } = await import("./storage.ts");
saveSettings({
gatewayUrl: "ws://127.0.0.1:18789",
token: "loopback-token",
sessionKey: "main",
lastActiveSessionKey: "main",
theme: "system",
chatFocusMode: false,
chatShowThinking: true,
splitRatio: 0.6,
navCollapsed: false,
navGroupsCollapsed: {},
});
localStorage.setItem(
"openclaw.control.settings.v1",
JSON.stringify({
gatewayUrl: "ws://localhost:18789",
sessionKey: "main",
lastActiveSessionKey: "main",
theme: "system",
chatFocusMode: false,
chatShowThinking: true,
splitRatio: 0.6,
navCollapsed: false,
navGroupsCollapsed: {},
}),
);
expect(loadSettings()).toMatchObject({
gatewayUrl: "ws://localhost:18789",
token: "loopback-token",
});
});
it("does not reuse a loopback token across different ports", async () => {
setTestLocation({
protocol: "http:",
host: "127.0.0.1:18789",
pathname: "/",
});
const { loadSettings, saveSettings } = await import("./storage.ts");
saveSettings({
gatewayUrl: "ws://127.0.0.1:18789",
token: "loopback-token",
sessionKey: "main",
lastActiveSessionKey: "main",
theme: "system",
chatFocusMode: false,
chatShowThinking: true,
splitRatio: 0.6,
navCollapsed: false,
navGroupsCollapsed: {},
});
localStorage.setItem(
"openclaw.control.settings.v1",
JSON.stringify({
gatewayUrl: "ws://localhost:19999",
sessionKey: "main",
lastActiveSessionKey: "main",
theme: "system",
chatFocusMode: false,
chatShowThinking: true,
splitRatio: 0.6,
navCollapsed: false,
navGroupsCollapsed: {},
}),
);
expect(loadSettings()).toMatchObject({
gatewayUrl: "ws://localhost:19999",
token: "",
});
});
it("does not persist gateway tokens when saving settings", async () => {
setTestLocation({
protocol: "https:",

View File

@ -43,9 +43,19 @@ function normalizeGatewayTokenScope(gatewayUrl: string): string {
? `${location.protocol}//${location.host}${location.pathname || "/"}`
: undefined;
const parsed = base ? new URL(trimmed, base) : new URL(trimmed);
const host = parsed.hostname.trim().toLowerCase();
const isLoopbackHost =
host === "localhost" ||
host === "127.0.0.1" ||
host === "::1" ||
host === "[::1]" ||
host.startsWith("127.");
const pathname =
parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/+$/, "") || parsed.pathname;
return `${parsed.protocol}//${parsed.host}${pathname}`;
const hostPort = isLoopbackHost
? `127.0.0.1${parsed.port ? `:${parsed.port}` : ""}`
: parsed.host;
return `${parsed.protocol}//${hostPort}${pathname}`;
} catch {
return trimmed;
}