theclawbay 0.3.51 → 0.3.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/device-session-auth.js +48 -11
- package/package.json +1 -1
|
@@ -5,11 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createBrowserLinkedDeviceSession = createBrowserLinkedDeviceSession;
|
|
7
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
9
|
const node_http_1 = __importDefault(require("node:http"));
|
|
9
10
|
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
12
|
const node_child_process_1 = require("node:child_process");
|
|
13
|
+
const paths_1 = require("./config/paths");
|
|
11
14
|
const BROWSER_SETUP_TIMEOUT_MS = 15 * 60000;
|
|
12
15
|
const BROWSER_SETUP_POLL_INTERVAL_MS = 2000;
|
|
16
|
+
const INSTALL_ID_PATH = node_path_1.default.join(paths_1.theclawbayConfigDir, "install-id");
|
|
13
17
|
function preferredBrowserCallbackPort() {
|
|
14
18
|
const parsed = Number(process.env.THECLAWBAY_CALLBACK_PORT?.trim() || "");
|
|
15
19
|
if (Number.isFinite(parsed) && parsed > 0 && parsed < 65536) {
|
|
@@ -46,6 +50,30 @@ function openUrlInBrowser(url) {
|
|
|
46
50
|
return false;
|
|
47
51
|
}
|
|
48
52
|
}
|
|
53
|
+
function normalizeInstallId(value) {
|
|
54
|
+
const normalized = value.trim();
|
|
55
|
+
if (normalized.length < 16 || normalized.length > 128)
|
|
56
|
+
return null;
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
|
59
|
+
async function getOrCreateInstallId() {
|
|
60
|
+
try {
|
|
61
|
+
const existing = normalizeInstallId(await promises_1.default.readFile(INSTALL_ID_PATH, "utf8"));
|
|
62
|
+
if (existing)
|
|
63
|
+
return existing;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const err = error;
|
|
67
|
+
if (err.code !== "ENOENT") {
|
|
68
|
+
throw new Error(`failed to read local install identity: ${err.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const installId = node_crypto_1.default.randomUUID();
|
|
72
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(INSTALL_ID_PATH), { recursive: true });
|
|
73
|
+
await promises_1.default.writeFile(INSTALL_ID_PATH, `${installId}\n`, "utf8");
|
|
74
|
+
await promises_1.default.chmod(INSTALL_ID_PATH, 0o600).catch(() => { });
|
|
75
|
+
return installId;
|
|
76
|
+
}
|
|
49
77
|
function htmlPage(title, message) {
|
|
50
78
|
return `<!doctype html>
|
|
51
79
|
<html lang="en">
|
|
@@ -237,7 +265,9 @@ async function createBrowserLinkedDeviceSession(params) {
|
|
|
237
265
|
const callbackServer = await createLocalCallbackServer(state);
|
|
238
266
|
const exchangeAbortController = new AbortController();
|
|
239
267
|
const exchangeSignal = exchangeAbortController.signal;
|
|
268
|
+
let exchangeInFlight = null;
|
|
240
269
|
try {
|
|
270
|
+
const installId = await getOrCreateInstallId();
|
|
241
271
|
const startResponse = await fetch(`${params.backendUrl}/api/setup/device-session/start`, {
|
|
242
272
|
method: "POST",
|
|
243
273
|
headers: { "Content-Type": "application/json" },
|
|
@@ -245,6 +275,7 @@ async function createBrowserLinkedDeviceSession(params) {
|
|
|
245
275
|
callbackUrl: callbackServer.callbackUrl,
|
|
246
276
|
state,
|
|
247
277
|
deviceLabel: label,
|
|
278
|
+
installId,
|
|
248
279
|
selectedClients: params.selectedClients,
|
|
249
280
|
}),
|
|
250
281
|
signal: AbortSignal.timeout(20000),
|
|
@@ -261,25 +292,31 @@ async function createBrowserLinkedDeviceSession(params) {
|
|
|
261
292
|
const deadlineMs = Date.now() + BROWSER_SETUP_TIMEOUT_MS;
|
|
262
293
|
let fallbackNoticeShown = false;
|
|
263
294
|
const timeoutError = new Error("browser setup timed out");
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
295
|
+
const performExchange = (code) => {
|
|
296
|
+
if (exchangeInFlight)
|
|
297
|
+
return exchangeInFlight;
|
|
298
|
+
const promise = exchangeBrowserSetupSession({
|
|
267
299
|
backendUrl: params.backendUrl,
|
|
268
|
-
sessionId:
|
|
269
|
-
state
|
|
270
|
-
code
|
|
300
|
+
sessionId: setupSessionId,
|
|
301
|
+
state,
|
|
302
|
+
code,
|
|
303
|
+
});
|
|
304
|
+
exchangeInFlight = promise.catch((error) => {
|
|
305
|
+
exchangeInFlight = null;
|
|
306
|
+
throw error;
|
|
271
307
|
});
|
|
308
|
+
return exchangeInFlight;
|
|
309
|
+
};
|
|
310
|
+
const waitForLocalCallbackExchange = async () => {
|
|
311
|
+
const callback = await withTimeout(callbackServer.waitForCallback(), BROWSER_SETUP_TIMEOUT_MS, exchangeSignal);
|
|
312
|
+
return performExchange(callback.code);
|
|
272
313
|
};
|
|
273
314
|
const waitForPolledExchange = async () => {
|
|
274
315
|
while (Date.now() < deadlineMs) {
|
|
275
316
|
if (exchangeSignal.aborted)
|
|
276
317
|
throw createSetupAbortError();
|
|
277
318
|
try {
|
|
278
|
-
return await
|
|
279
|
-
backendUrl: params.backendUrl,
|
|
280
|
-
sessionId: setupSessionId,
|
|
281
|
-
state,
|
|
282
|
-
});
|
|
319
|
+
return await performExchange();
|
|
283
320
|
}
|
|
284
321
|
catch (error) {
|
|
285
322
|
const message = error.message || "";
|
package/package.json
CHANGED