mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
The extension relay server authenticates using an HMAC-SHA256 derived
token (`openclaw-extension-relay-v1:<port>`), but the Chrome extension
was sending the raw gateway token. This caused both the WebSocket
connection and the options page validation to fail with 401 Unauthorized.
Additionally, the options page validation request triggered a CORS
preflight (due to the custom `x-openclaw-relay-token` header) which the
relay rejects because OPTIONS requests lack auth headers. The options
page now delegates the check to the background service worker which has
host_permissions and bypasses CORS preflight.
Fixes #23842
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit bbc654b9f0)
49 lines
1.6 KiB
JavaScript
49 lines
1.6 KiB
JavaScript
export function reconnectDelayMs(
|
|
attempt,
|
|
opts = { baseMs: 1000, maxMs: 30000, jitterMs: 1000, random: Math.random },
|
|
) {
|
|
const baseMs = Number.isFinite(opts.baseMs) ? opts.baseMs : 1000;
|
|
const maxMs = Number.isFinite(opts.maxMs) ? opts.maxMs : 30000;
|
|
const jitterMs = Number.isFinite(opts.jitterMs) ? opts.jitterMs : 1000;
|
|
const random = typeof opts.random === "function" ? opts.random : Math.random;
|
|
const safeAttempt = Math.max(0, Number.isFinite(attempt) ? attempt : 0);
|
|
const backoff = Math.min(baseMs * 2 ** safeAttempt, maxMs);
|
|
return backoff + Math.max(0, jitterMs) * random();
|
|
}
|
|
|
|
export async function deriveRelayToken(gatewayToken, port) {
|
|
const enc = new TextEncoder();
|
|
const key = await crypto.subtle.importKey(
|
|
"raw",
|
|
enc.encode(gatewayToken),
|
|
{ name: "HMAC", hash: "SHA-256" },
|
|
false,
|
|
["sign"],
|
|
);
|
|
const sig = await crypto.subtle.sign(
|
|
"HMAC",
|
|
key,
|
|
enc.encode(`openclaw-extension-relay-v1:${port}`),
|
|
);
|
|
return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
}
|
|
|
|
export async function buildRelayWsUrl(port, gatewayToken) {
|
|
const token = String(gatewayToken || "").trim();
|
|
if (!token) {
|
|
throw new Error(
|
|
"Missing gatewayToken in extension settings (chrome.storage.local.gatewayToken)",
|
|
);
|
|
}
|
|
const relayToken = await deriveRelayToken(token, port);
|
|
return `ws://127.0.0.1:${port}/extension?token=${encodeURIComponent(relayToken)}`;
|
|
}
|
|
|
|
export function isRetryableReconnectError(err) {
|
|
const message = err instanceof Error ? err.message : String(err || "");
|
|
if (message.includes("Missing gatewayToken")) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|