fix(desktop): isolate Electron auth cookies per app

Make the legacy Electron desktop client generate and pass a per-launch auth cookie name too, so parallel desktop instances stop clobbering each other's localhost session cookie just like the Tauri client.
This commit is contained in:
Pascal André
2026-03-15 09:38:00 +01:00
parent 800133361d
commit 2abda0e6b4
2 changed files with 11 additions and 4 deletions

View File

@@ -327,7 +327,6 @@ function finalizeCliSwap(url: string) {
mainWindow.loadURL(url).catch((error) => console.error("[cli] failed to load CLI view:", error))
}
const SESSION_COOKIE_NAME = "codenomad_session"
let bootstrapExchangeInFlight = false
function extractCookieValue(setCookieHeader: string | string[] | undefined, name: string): string | null {
@@ -350,6 +349,7 @@ function extractCookieValue(setCookieHeader: string | string[] | undefined, name
}
async function exchangeBootstrapToken(baseUrl: string, token: string): Promise<boolean> {
const sessionCookieName = cliManager.getAuthCookieName()
const target = new URL("/api/auth/token", baseUrl)
const body = JSON.stringify({ token })
@@ -380,14 +380,14 @@ async function exchangeBootstrapToken(baseUrl: string, token: string): Promise<b
return false
}
const sessionId = extractCookieValue(result.setCookie, SESSION_COOKIE_NAME)
const sessionId = extractCookieValue(result.setCookie, sessionCookieName)
if (!sessionId) {
return false
}
await session.defaultSession.cookies.set({
url: baseUrl,
name: SESSION_COOKIE_NAME,
name: sessionCookieName,
value: sessionId,
httpOnly: true,
path: "/",

View File

@@ -11,6 +11,7 @@ import { buildUserShellCommand, getUserShellEnv, supportsUserShell } from "./use
const nodeRequire = createRequire(import.meta.url)
const BOOTSTRAP_TOKEN_PREFIX = "CODENOMAD_BOOTSTRAP_TOKEN:"
const SESSION_COOKIE_NAME_PREFIX = "codenomad_session"
type CliState = "starting" | "ready" | "error" | "stopped"
type ListeningMode = "local" | "all"
@@ -122,6 +123,7 @@ export class CliProcessManager extends EventEmitter {
private stdoutBuffer = ""
private stderrBuffer = ""
private bootstrapToken: string | null = null
private authCookieName = `${SESSION_COOKIE_NAME_PREFIX}_${process.pid}_${Date.now()}`
private requestedStop = false
async start(options: StartOptions): Promise<CliStatus> {
@@ -132,6 +134,7 @@ export class CliProcessManager extends EventEmitter {
this.stdoutBuffer = ""
this.stderrBuffer = ""
this.bootstrapToken = null
this.authCookieName = `${SESSION_COOKIE_NAME_PREFIX}_${process.pid}_${Date.now()}`
this.requestedStop = false
this.updateStatus({ state: "starting", port: undefined, pid: undefined, url: undefined, error: undefined })
@@ -328,6 +331,10 @@ export class CliProcessManager extends EventEmitter {
return { ...this.status }
}
getAuthCookieName(): string {
return this.authCookieName
}
private resolveListeningMode(): ListeningMode {
return readListeningModeFromConfig()
}
@@ -416,7 +423,7 @@ export class CliProcessManager extends EventEmitter {
}
private buildCliArgs(options: StartOptions, host: string): string[] {
const args = ["serve", "--host", host, "--generate-token"]
const args = ["serve", "--host", host, "--generate-token", "--auth-cookie-name", this.authCookieName]
if (options.dev) {
// Dev: run plain HTTP + Vite dev server proxy.