fix: prevent claude-cli oauth downgrade (#654) (thanks @radek-paclt)
This commit is contained in:
@@ -68,7 +68,7 @@
|
|||||||
- Dependencies: Pi 0.40.0 bump (#543) — thanks @mcinteerj.
|
- Dependencies: Pi 0.40.0 bump (#543) — thanks @mcinteerj.
|
||||||
- Build: Docker build cache layer (#605) — thanks @zknicker.
|
- Build: Docker build cache layer (#605) — thanks @zknicker.
|
||||||
|
|
||||||
- Auth: enable OAuth token refresh for Claude CLI credentials (`anthropic:claude-cli`) with bidirectional sync back to Claude Code storage (file on Linux/Windows, Keychain on macOS). This allows long-running agents to operate autonomously without manual re-authentication.
|
- Auth: enable OAuth token refresh for Claude CLI credentials (`anthropic:claude-cli`) with bidirectional sync back to Claude Code storage (file on Linux/Windows, Keychain on macOS). This allows long-running agents to operate autonomously without manual re-authentication (#654 — thanks @radek-paclt).
|
||||||
|
|
||||||
## 2026.1.8
|
## 2026.1.8
|
||||||
|
|
||||||
|
|||||||
@@ -946,6 +946,59 @@ describe("external CLI credential sync", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not downgrade store oauth to token when CLI lacks refresh token", async () => {
|
||||||
|
const agentDir = fs.mkdtempSync(
|
||||||
|
path.join(os.tmpdir(), "clawdbot-cli-no-downgrade-oauth-"),
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await withTempHome(
|
||||||
|
async (tempHome) => {
|
||||||
|
const claudeDir = path.join(tempHome, ".claude");
|
||||||
|
fs.mkdirSync(claudeDir, { recursive: true });
|
||||||
|
// CLI has token-only credentials (no refresh token)
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(claudeDir, ".credentials.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
claudeAiOauth: {
|
||||||
|
accessToken: "cli-token-access",
|
||||||
|
expiresAt: Date.now() + 30 * 60 * 1000,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const authPath = path.join(agentDir, "auth-profiles.json");
|
||||||
|
// Store already has OAuth credentials with refresh token
|
||||||
|
fs.writeFileSync(
|
||||||
|
authPath,
|
||||||
|
JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
profiles: {
|
||||||
|
[CLAUDE_CLI_PROFILE_ID]: {
|
||||||
|
type: "oauth",
|
||||||
|
provider: "anthropic",
|
||||||
|
access: "store-oauth-access",
|
||||||
|
refresh: "store-refresh",
|
||||||
|
expires: Date.now() + 60 * 60 * 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const store = ensureAuthProfileStore(agentDir);
|
||||||
|
// Keep oauth to preserve auto-refresh capability
|
||||||
|
const cliProfile = store.profiles[CLAUDE_CLI_PROFILE_ID];
|
||||||
|
expect(cliProfile.type).toBe("oauth");
|
||||||
|
expect((cliProfile as { access: string }).access).toBe(
|
||||||
|
"store-oauth-access",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ prefix: "clawdbot-home-" },
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(agentDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("updates codex-cli profile when Codex CLI refresh token changes", async () => {
|
it("updates codex-cli profile when Codex CLI refresh token changes", async () => {
|
||||||
const agentDir = fs.mkdtempSync(
|
const agentDir = fs.mkdtempSync(
|
||||||
path.join(os.tmpdir(), "clawdbot-codex-refresh-sync-"),
|
path.join(os.tmpdir(), "clawdbot-codex-refresh-sync-"),
|
||||||
|
|||||||
@@ -716,6 +716,11 @@ function syncExternalCliCredentials(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Avoid downgrading from oauth to token-only credentials.
|
||||||
|
if (existing?.type === "oauth" && claudeCreds.type === "token") {
|
||||||
|
shouldUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldUpdate && !isEqual) {
|
if (shouldUpdate && !isEqual) {
|
||||||
store.profiles[CLAUDE_CLI_PROFILE_ID] = claudeCreds;
|
store.profiles[CLAUDE_CLI_PROFILE_ID] = claudeCreds;
|
||||||
mutated = true;
|
mutated = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user