From 5a4915543ad18620a1f283d3ec8ee551bc138152 Mon Sep 17 00:00:00 2001 From: Omkar Chebale Date: Sun, 27 Sep 2026 18:09:29 +0530 Subject: [PATCH] Keep login waiting when the browser cannot be opened If xdg-open/open fails (e.g. WSL without a browser handler), the auth flow aborted even though it had just printed the URL to visit manually. Log the failure and keep the callback server listening until the existing timeout instead. Fixes #72 --- src/services/auth.test.ts | 21 +++++++++++++++++++++ src/services/auth.ts | 9 +++------ 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/services/auth.test.ts diff --git a/src/services/auth.test.ts b/src/services/auth.test.ts new file mode 100644 index 0000000..99e0b4d --- /dev/null +++ b/src/services/auth.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test } from "bun:test"; + +import { startAuthFlow } from "./auth.js"; + +describe("startAuthFlow", () => { + test("keeps waiting for the callback when the browser cannot be opened", async () => { + let authUrl = ""; + const flow = startAuthFlow(5_000, async (url) => { + authUrl = url.toString(); + throw new Error("spawn xdg-open ENOENT"); + }); + + await new Promise((resolve) => setTimeout(resolve, 50)); + const callback = new URL(new URL(authUrl).searchParams.get("callback")!); + callback.searchParams.set("state", "wrong"); + const response = await fetch(callback); + + expect(response.status).toBe(403); + expect(await flow).toEqual({ success: false, error: "Invalid auth state" }); + }); +}); diff --git a/src/services/auth.ts b/src/services/auth.ts index bacfac8..636bf30 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -65,7 +65,7 @@ export interface AuthResult { error?: string; } -export function startAuthFlow(timeoutMs = AUTH_TIMEOUT): Promise { +export function startAuthFlow(timeoutMs = AUTH_TIMEOUT, open = openUrl): Promise { return new Promise((resolve) => { let resolved = false; const stateToken = randomBytes(16).toString("hex"); @@ -168,12 +168,9 @@ export function startAuthFlow(timeoutMs = AUTH_TIMEOUT): Promise { console.log("Opening browser for authentication..."); console.log(`If it doesn't open, visit: ${authUrl}`); - openUrl(authUrl).catch((error) => { + open(authUrl).catch((error) => { if (!resolved) { - resolved = true; - clearTimeout(timer); - server.close(); - resolve({ success: false, error: `Failed to open browser: ${error.message}` }); + console.log(`Could not open a browser (${error.message}). Open the URL above to continue.`); } }); });