diff --git a/packages/server/src/server/agent/providers/paseo-agent/oauth-store.posix.test.ts b/packages/server/src/server/agent/providers/paseo-agent/oauth-store.posix.test.ts new file mode 100644 index 0000000000..d4b9df8884 --- /dev/null +++ b/packages/server/src/server/agent/providers/paseo-agent/oauth-store.posix.test.ts @@ -0,0 +1,51 @@ +// POSIX-only: file mode bits are not represented the same way on Windows. +import { mkdtempSync, rmSync, statSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; + +import { isPlatform } from "../../../../test-utils/platform.js"; +import { loginAndStoreCodex, loginAndStoreCodexBrowser } from "./oauth-store.js"; + +describe.skipIf(isPlatform("win32"))("oauth-store POSIX-only", () => { + let home: string; + let env: NodeJS.ProcessEnv; + + beforeEach(() => { + home = mkdtempSync(join(tmpdir(), "paseo-oauth-store-")); + env = { PASEO_HOME: home }; + }); + + afterEach(() => { + rmSync(home, { recursive: true, force: true }); + }); + + it("stores device-code credentials in a private file", async () => { + const login = async () => ({ refresh: "rt-from-login", access: "ac", expires: 123 }); + + const { path } = await loginAndStoreCodex({ + providerInstance: "chatgpt", + env, + onDeviceCode: () => {}, + login, + }); + + expect(statSync(path).mode & 0o777).toBe(0o600); + }); + + it("stores browser-login credentials in a private file", async () => { + const login = async (opts: { onAuth: (info: { url: string }) => void }) => { + opts.onAuth({ url: "https://auth.openai.com/oauth/authorize?x=1" }); + return { refresh: "rt-browser", access: "ac", expires: 456 }; + }; + + const { path } = await loginAndStoreCodexBrowser({ + providerInstance: "chatgpt", + env, + onAuthUrl: () => {}, + login, + }); + + expect(statSync(path).mode & 0o777).toBe(0o600); + }); +}); diff --git a/packages/server/src/server/agent/providers/paseo-agent/oauth-store.test.ts b/packages/server/src/server/agent/providers/paseo-agent/oauth-store.test.ts index c3d0e6a270..18341b3ad1 100644 --- a/packages/server/src/server/agent/providers/paseo-agent/oauth-store.test.ts +++ b/packages/server/src/server/agent/providers/paseo-agent/oauth-store.test.ts @@ -1,4 +1,4 @@ -import { mkdtempSync, readFileSync, rmSync, statSync } from "node:fs"; +import { mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; @@ -59,9 +59,6 @@ describe("oauth-store", () => { expect(hasStoredOAuthCredential("chatgpt", env)).toBe(true); const stored = JSON.parse(readFileSync(path, "utf8")); expect(stored.chatgpt).toMatchObject({ type: "oauth", refresh: "rt-from-login" }); - - // Stored private (0600). - expect(statSync(path).mode & 0o777).toBe(0o600); }); it("keys the credential by provider instance name", async () => { @@ -99,7 +96,6 @@ describe("oauth-store", () => { expect(hasStoredOAuthCredential("chatgpt", env)).toBe(true); const stored = JSON.parse(readFileSync(path, "utf8")); expect(stored.chatgpt).toMatchObject({ type: "oauth", refresh: "rt-browser" }); - expect(statSync(path).mode & 0o777).toBe(0o600); }); it("browser login falls back to manual code entry only when the callback can't complete", async () => {