fix(auth): protect fresher codex reauth state

- invalidate cached Codex CLI credentials when auth.json changes within the TTL window
- skip external CLI sync when the stored Codex OAuth credential is newer
- cover both behaviors with focused regression tests

Refs #53466

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
giulio-leone
2026-03-24 11:13:27 +01:00
committed by Peter Steinberger
parent 559b3a5fd4
commit bbe6f7fdd9
4 changed files with 145 additions and 3 deletions

View File

@@ -21,6 +21,7 @@ type CachedValue<T> = {
value: T | null;
readAt: number;
cacheKey: string;
sourceMtimeMs?: number | null;
};
let claudeCliCache: CachedValue<ClaudeCliCredential> | null = null;
@@ -148,6 +149,14 @@ function resolveMiniMaxCliCredentialsPath(homeDir?: string) {
return path.join(baseDir, MINIMAX_CLI_CREDENTIALS_RELATIVE_PATH);
}
function readFileMtimeMs(filePath: string): number | null {
try {
return fs.statSync(filePath).mtimeMs;
} catch {
return null;
}
}
function computeCodexKeychainAccount(codexHome: string) {
const hash = createHash("sha256").update(codexHome).digest("hex");
return `cli|${hash.slice(0, 16)}`;
@@ -526,11 +535,14 @@ export function readCodexCliCredentialsCached(options?: {
}): CodexCliCredential | null {
const ttlMs = options?.ttlMs ?? 0;
const now = Date.now();
const cacheKey = `${options?.platform ?? process.platform}|${resolveCodexCliAuthPath()}`;
const authPath = resolveCodexCliAuthPath();
const cacheKey = `${options?.platform ?? process.platform}|${authPath}`;
const sourceMtimeMs = readFileMtimeMs(authPath);
if (
ttlMs > 0 &&
codexCliCache &&
codexCliCache.cacheKey === cacheKey &&
codexCliCache.sourceMtimeMs === sourceMtimeMs &&
now - codexCliCache.readAt < ttlMs
) {
return codexCliCache.value;
@@ -539,8 +551,16 @@ export function readCodexCliCredentialsCached(options?: {
platform: options?.platform,
execSync: options?.execSync,
});
if (ttlMs > 0) {
codexCliCache = { value, readAt: now, cacheKey };
const cachedSourceMtimeMs = readFileMtimeMs(authPath);
if (ttlMs > 0 && cachedSourceMtimeMs === sourceMtimeMs) {
codexCliCache = {
value,
readAt: now,
cacheKey,
sourceMtimeMs: cachedSourceMtimeMs,
};
} else if (ttlMs > 0) {
codexCliCache = null;
}
return value;
}