fix: openclaw dashboard auth — add gateway.auth.mode and use fragment token (#2617)

OpenClaw 2026.3.7+ requires an explicit `gateway.auth.mode: "token"` field
when `gateway.auth.token` is set. Without it the gateway rejects auth and the
dashboard shows "Unauthorized".

Additionally, pass the token via URL fragment (`#token=`) instead of query
parameter (`?token=`) to match the updated auth flow and avoid leaking the
token in server logs / Referer headers (GHSA-rchv-x836-w7xp).

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
A 2026-03-14 11:48:34 -07:00 committed by GitHub
parent 6988ac7acf
commit c323f0e2e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 7 additions and 5 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "0.17.19",
"version": "0.17.20",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -353,6 +353,7 @@ async function setupOpenclawConfig(
gateway: {
mode: "local",
auth: {
mode: "token",
token: gatewayToken,
},
},
@ -414,17 +415,18 @@ async function setupOpenclawConfig(
logWarn("Browser config setup failed (non-fatal)");
}
// Re-assert gateway auth token after browser config set calls — each `openclaw config set`
// Re-assert gateway auth mode + token after browser config set calls — each `openclaw config set`
// does a read-modify-write on the config file and may drop fields written by uploadConfigFile.
// Re-setting the token here ensures it survives those cycles and the gateway starts authenticated.
// Re-setting both here ensures they survive those cycles and the gateway starts authenticated.
const gatewayTokenResult = await asyncTryCatchIf(isOperationalError, () =>
runner.runServer(
"export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " +
"openclaw config set gateway.auth.mode token >/dev/null; " +
`openclaw config set gateway.auth.token ${shellQuote(gatewayToken)} >/dev/null`,
),
);
if (!gatewayTokenResult.ok) {
logWarn("Gateway token re-assertion failed (non-fatal) — dashboard may show Unauthorized");
logWarn("Gateway auth re-assertion failed (non-fatal) — dashboard may show Unauthorized");
}
// Channel pairing (Telegram/WhatsApp) happens in orchestrate.ts after the gateway starts.
@ -695,7 +697,7 @@ function createAgents(runner: CloudRunner): Record<string, AgentConfig> {
"source ~/.spawnrc 2>/dev/null; export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; openclaw tui",
tunnel: {
remotePort: 18791,
browserUrl: (localPort: number) => `http://localhost:${localPort}/?token=${dashboardToken}`,
browserUrl: (localPort: number) => `http://localhost:${localPort}/#token=${dashboardToken}`,
},
};
})(),