Fix Claude CLI session continuity after token refresh#70132
Conversation
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟠 Auth epoch mismatch ignored for unversioned bindings enables stale session reuse after credential changes
Description
Impact:
Vulnerable code: const storedAuthEpoch = normalizeOptionalString(binding?.authEpoch);
if (
binding?.authEpochVersion === params.authEpochVersion &&
storedAuthEpoch !== currentAuthEpoch
) {
return { invalidatedReason: "auth-epoch" };
}RecommendationFail closed for missing/unknown const storedAuthEpoch = normalizeOptionalString(binding?.authEpoch);
const storedVersion = typeof binding?.authEpochVersion === "number" ? binding.authEpochVersion : undefined;
// Invalidate when versions differ OR are missing, unless you can safely recompute/translate the stored epoch.
if (storedVersion !== params.authEpochVersion) {
return { invalidatedReason: "auth-epoch" };
}
if (storedAuthEpoch !== currentAuthEpoch) {
return { invalidatedReason: "auth-epoch" };
}If you need a migration path, consider:
Avoid accepting unversioned epochs when Analyzed PR: #70132 at commit Last updated on: 2026-04-22T11:38:49Z |
Greptile SummaryThis PR fixes OAuth token refresh causing CLI session loss by hashing only stable identity material (refresh token, provider) instead of volatile fields (access token, expiry) into the auth epoch, and introducing a versioned epoch scheme ( The implementation is well-structured and thoroughly tested. One mild concern: Confidence Score: 4/5Safe to merge; the one concern is a latent footgun in the optional parameter signature, not a current breakage. All production callers correctly pass authEpochVersion and the epoch stability fix is sound. The P2 finding about the optional authEpochVersion creating a symmetrical bypass for future callers is a code-quality concern rather than a present defect, but it's subtle enough to warrant a look before merging. src/agents/cli-session.ts — the authEpochVersion optional parameter type in resolveCliSessionReuse Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/agents/cli-session.ts
Line: 152-157
Comment:
**Version-mismatch bypass applies symmetrically in both directions**
The guard `binding?.authEpochVersion === params.authEpochVersion` skips epoch validation whenever the versions differ — including the case where `params.authEpochVersion` is omitted (`undefined`) but the stored binding already carries `authEpochVersion: 2`. In that scenario, a versioned binding would silently pass the epoch check even if the refresh token had changed. The production call-site in `prepare.ts` always supplies `authEpochVersion: CLI_AUTH_EPOCH_VERSION`, so this isn't broken today, but `authEpochVersion` being optional in the parameter type makes it easy for a future caller to omit it and unknowingly get epoch validation bypassed for already-versioned bindings.
Consider making `authEpochVersion` a required parameter (or defaulting it to `CLI_AUTH_EPOCH_VERSION` inside the function) so omissions are caught at compile time rather than silently degrading security.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "test(cli): cover oauth auth epoch contin..." | Re-trigger Greptile |
|
Local smoke passed on this branch. Covered:
Only unrelated noise seen: existing channel network warnings. |
333b30d to
1943340
Compare
|
Landed via rebase. Commits: Verified locally:
|
getLocalCliCredentialFingerprint only switched on claude-cli and codex-cli, so google-gemini-cli fell through to undefined and the stored session's authEpoch never bound to the user's local Gemini OAuth identity. Read ~/.gemini/oauth_creds.json through the same cached-reader pattern as Codex/MiniMax, and encode a stable identity fingerprint from the refresh token (stable across Google's access-token rotation, flips on re-authentication), matching openclaw#70132's stability contract. Adds: readGeminiCliCredentials(Cached) in cli-credentials.ts, a 'google-gemini-cli' case in getLocalCliCredentialFingerprint, and parallel test coverage alongside the existing claude/codex cases.
Fixes Claude CLI sessions being discarded after a normal OAuth token refresh and gateway restart.
What changed:
Verification: