





















@@ -2,6 +2,8 @@ import fs from "node:fs/promises";
22import os from "node:os";
33import path from "node:path";
44import { describe, expect, it, vi } from "vitest";
5+import { resolveOAuthDir } from "../config/paths.js";
6+import { legacyOAuthSidecarTestUtils } from "./auth-profiles/legacy-oauth-sidecar.js";
57import { resolveAuthStatePath, resolveAuthStorePath } from "./auth-profiles/paths.js";
68import {
79clearRuntimeAuthProfileStoreSnapshots,
@@ -169,6 +171,174 @@ describe("saveAuthProfileStore", () => {
169171}
170172});
171173174+it("keeps rehydrated legacy oauthRef sidecar tokens runtime-only during ordinary saves", async () => {
175+const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-save-oauth-ref-"));
176+const authPath = resolveAuthStorePath(agentDir);
177+const previousOAuthDir = process.env.OPENCLAW_OAUTH_DIR;
178+const previousSecretKey = process.env.OPENCLAW_AUTH_PROFILE_SECRET_KEY;
179+process.env.OPENCLAW_OAUTH_DIR = path.join(agentDir, "credentials");
180+process.env.OPENCLAW_AUTH_PROFILE_SECRET_KEY = "legacy-seed";
181+const oauthRef = {
182+source: "openclaw-credentials" as const,
183+provider: "openai-codex" as const,
184+id: "0123456789abcdef0123456789abcdef",
185+};
186+try {
187+await fs.mkdir(agentDir, { recursive: true });
188+await fs.writeFile(
189+authPath,
190+`${JSON.stringify(
191+ {
192+ version: 1,
193+ profiles: {
194+ "openai-codex:default": {
195+ type: "oauth",
196+ provider: "openai-codex",
197+ expires: Date.now() + 60_000,
198+ oauthRef,
199+ },
200+ },
201+ },
202+ null,
203+ 2,
204+ )}\n`,
205+);
206+const sidecarPath = path.join(resolveOAuthDir(), "auth-profiles", `${oauthRef.id}.json`);
207+await fs.mkdir(path.dirname(sidecarPath), { recursive: true });
208+await fs.writeFile(
209+sidecarPath,
210+`${JSON.stringify(
211+ {
212+ version: 1,
213+ profileId: "openai-codex:default",
214+ provider: "openai-codex",
215+ encrypted: legacyOAuthSidecarTestUtils.encryptLegacyOAuthMaterial({
216+ ref: oauthRef,
217+ profileId: "openai-codex:default",
218+ provider: "openai-codex",
219+ seed: "legacy-seed",
220+ material: {
221+ access: "legacy-access-token",
222+ refresh: "legacy-refresh-token",
223+ },
224+ }),
225+ },
226+ null,
227+ 2,
228+ )}\n`,
229+);
230+231+const runtimeStore = ensureAuthProfileStore(agentDir);
232+expectProfileFields(runtimeStore.profiles["openai-codex:default"], {
233+access: "legacy-access-token",
234+refresh: "legacy-refresh-token",
235+});
236+237+delete process.env.OPENCLAW_AUTH_PROFILE_SECRET_KEY;
238+const clonedRuntimeStore = JSON.parse(JSON.stringify(runtimeStore)) as AuthProfileStore;
239+saveAuthProfileStore(clonedRuntimeStore, agentDir);
240+241+const parsed = JSON.parse(await fs.readFile(authPath, "utf8")) as {
242+profiles: Record<string, Record<string, unknown>>;
243+};
244+expect(parsed.profiles["openai-codex:default"]?.oauthRef).toEqual(oauthRef);
245+expect(parsed.profiles["openai-codex:default"]).not.toHaveProperty("access");
246+expect(parsed.profiles["openai-codex:default"]).not.toHaveProperty("refresh");
247+} finally {
248+if (previousOAuthDir === undefined) {
249+delete process.env.OPENCLAW_OAUTH_DIR;
250+} else {
251+process.env.OPENCLAW_OAUTH_DIR = previousOAuthDir;
252+}
253+if (previousSecretKey === undefined) {
254+delete process.env.OPENCLAW_AUTH_PROFILE_SECRET_KEY;
255+} else {
256+process.env.OPENCLAW_AUTH_PROFILE_SECRET_KEY = previousSecretKey;
257+}
258+clearRuntimeAuthProfileStoreSnapshots();
259+await fs.rm(agentDir, { recursive: true, force: true });
260+}
261+});
262+263+it("writes refreshed legacy sidecar tokens inline when they replace runtime sidecar material", async () => {
264+const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-save-oauth-ref-"));
265+const authPath = resolveAuthStorePath(agentDir);
266+const previousOAuthDir = process.env.OPENCLAW_OAUTH_DIR;
267+process.env.OPENCLAW_OAUTH_DIR = path.join(agentDir, "credentials");
268+const profileId = "openai-codex:default";
269+const oauthRef = {
270+source: "openclaw-credentials",
271+provider: "openai-codex",
272+id: "0123456789abcdef0123456789abcdef",
273+};
274+try {
275+await fs.mkdir(agentDir, { recursive: true });
276+await fs.writeFile(
277+authPath,
278+`${JSON.stringify(
279+ {
280+ version: 1,
281+ profiles: {
282+ [profileId]: {
283+ type: "oauth",
284+ provider: "openai-codex",
285+ expires: Date.now() + 60_000,
286+ oauthRef,
287+ },
288+ },
289+ },
290+ null,
291+ 2,
292+ )}\n`,
293+);
294+const sidecarPath = path.join(resolveOAuthDir(), "auth-profiles", `${oauthRef.id}.json`);
295+await fs.mkdir(path.dirname(sidecarPath), { recursive: true });
296+await fs.writeFile(
297+sidecarPath,
298+`${JSON.stringify(
299+ {
300+ version: 1,
301+ profileId,
302+ provider: "openai-codex",
303+ access: "legacy-access-token",
304+ refresh: "legacy-refresh-token",
305+ },
306+ null,
307+ 2,
308+ )}\n`,
309+);
310+311+const runtimeStore = ensureAuthProfileStore(agentDir);
312+const refreshedStore: AuthProfileStore = {
313+ ...runtimeStore,
314+profiles: {
315+ ...runtimeStore.profiles,
316+[profileId]: {
317+ ...runtimeStore.profiles[profileId],
318+access: "refreshed-access-token",
319+refresh: "refreshed-refresh-token",
320+} as AuthProfileStore["profiles"][string],
321+},
322+};
323+saveAuthProfileStore(refreshedStore, agentDir);
324+325+const parsed = JSON.parse(await fs.readFile(authPath, "utf8")) as {
326+profiles: Record<string, Record<string, unknown>>;
327+};
328+expect(parsed.profiles[profileId]).not.toHaveProperty("oauthRef");
329+expect(parsed.profiles[profileId]?.access).toBe("refreshed-access-token");
330+expect(parsed.profiles[profileId]?.refresh).toBe("refreshed-refresh-token");
331+} finally {
332+if (previousOAuthDir === undefined) {
333+delete process.env.OPENCLAW_OAUTH_DIR;
334+} else {
335+process.env.OPENCLAW_OAUTH_DIR = previousOAuthDir;
336+}
337+clearRuntimeAuthProfileStoreSnapshots();
338+await fs.rm(agentDir, { recursive: true, force: true });
339+}
340+});
341+172342it("refreshes the runtime snapshot when a saved store rotates oauth tokens", async () => {
173343const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-save-runtime-"));
174344try {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。