Skip to content

fix(cli): identity-only auth-epoch hashing for token credentials (#74312)#74493

Merged
steipete merged 4 commits into
openclaw:mainfrom
stainlu:fix/issue-74312-auth-epoch-identity-only-token
May 31, 2026
Merged

fix(cli): identity-only auth-epoch hashing for token credentials (#74312)#74493
steipete merged 4 commits into
openclaw:mainfrom
stainlu:fix/issue-74312-auth-epoch-identity-only-token

Conversation

@stainlu

@stainlu stainlu commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: Claude CLI sessions are hard-reset every few minutes during active conversations on macOS. Logs show cli session reset: provider=claude-cli reason=auth-epoch clusters that correlate with claude-cli's OAuth token rotation. Reporter (claude-cli auth-epoch flips on token rotation, forcing session resets mid-conversation #74312) observed ~16 resets in 24h on one host, accelerating to 2 resets in 3 minutes near token expiry. The user's live conversation context is dropped on every rotation.
  • Why it matters: macOS Claude keychain rewrites are non-atomic. A read during rotation can briefly see accessToken without refreshToken, causing parseClaudeCliOauthCredential to return type: "token" instead of type: "oauth". The previous encodeClaudeCredential token branch hashed credential.token directly, so that transient race flipped the auth-epoch and reset every reusable claude-cli session. On macOS keychain-only setups (no ~/.claude/.credentials.json fallback), getLocalCliCredentialFingerprint returns undefined and the auth-profile branch is the dominant trigger; encodeAuthProfileCredential token case had the same hash-rotates-with-token defect.
  • What changed: Extends the identity-only hashing pattern Ayaan started in 1ff461fe7b (fix(cli): stabilize oauth session auth epochs) and 3eb6edc67c (fix(cli): key oauth session epochs on identity), and which Chunyue extended to Gemini in fix(cli): key gemini cli auth epoch on google account identity #71076, to the static-token credential branch. encodeClaudeCredential routes both OAuth and token claude-cli credentials through encodeOAuthIdentity, collapsing partial keychain reads onto the same provider-keyed identity hash. encodeAuthProfileCredential token case drops credential.token from the hash; identity fields (provider, tokenRef, email, displayName) are the discriminator. CLI_AUTH_EPOCH_VERSION bumped 4 → 5.
  • What did NOT change: parseClaudeCliOauthCredential (the parser, not hardened here per scope discipline; identity-only hashing makes the partial-read race harmless without parser changes), the OAuth branches (already identity-only since 3eb6edc67c), Gemini and Codex encoders (already identity-only), resolveCliAuthEpoch orchestration, or any consumer code (prepareExecution reads the same string).

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause

  • Root cause: encodeClaudeCredential and encodeAuthProfileCredential retained credential.token in the auth-epoch hash for the static-token branch, even though Ayaan's recent commits had moved OAuth credentials and Chunyue's fix(cli): key gemini cli auth epoch on google account identity #71076 had moved Gemini OAuth onto identity-only hashing. The remaining token branches hashed rotating material, so any transient type: "token" parse (which the macOS keychain rewrite race triggers reliably) flipped the epoch.
  • Missing detection / guardrail: The existing test asserted token rotation expect(second).not.toBe(first) — i.e., locked in the buggy behavior. There was no regression case proving OAuth and token claude-cli epochs match (which the partial-read race requires).
  • Contributing context: parseClaudeCliOauthCredential falls through to type: "token" when refreshToken is empty/missing. This is correct for credentials that were never OAuth, but produces false type: "token" parses during keychain rotation. The hash semantics needed to be stable across that fallthrough.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/agents/cli-auth-epoch.test.ts
  • Scenarios the tests lock in:
    • keeps claude cli token epochs stable across token rotation (replaces the previous changes ... when the static token changes assertion)
    • matches claude cli token and oauth epochs so partial keychain reads do not flip (new regression case for the partial-read race)
    • keeps token auth-profile epochs stable across credential.token rotation (auth-profile branch — the dominant trigger on keychain-only macOS setups)
    • changes token auth-profile epochs when the email identity changes (preserves real account-switch invalidation)

User-visible / Behavior Changes

  • Active claude-cli conversations no longer drop context every few minutes during token rotation.
  • cli session reset: reason=auth-epoch log lines no longer correlate with token rotation; they now only fire on real identity changes (provider, email, tokenRef, displayName).
  • CLI_AUTH_EPOCH_VERSION bumped 4 → 5; existing stored auth-epochs invalidate once on first read after upgrade (one expected reset per host on the upgrade boundary).
  • No config changes, no new directives.

Security Impact

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No (the hash is over identity fields; tokens are still read and used for auth requests, just no longer included in the epoch fingerprint)
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: macOS (reporter on Ubuntu Kylin 20.04 — keychain race is macOS-specific but auth-profile branch hits other platforms via auth-profile sync)
  • Backend: claude-cli with OAuth credentials in macOS keychain

Steps (per reporter)

  1. Run a long-lived claude-cli agent on Telegram or another active channel.
  2. Monitor gateway.out for cli session reset: provider=claude-cli reason=auth-epoch lines.
  3. Observe that resets correlate with claude-cli OAuth token rotation activity (~every 30-60 minutes, accelerating near token expiry).

Expected (after this PR)

No auth-epoch resets during normal token rotation. Conversation context preserved across rotations.

Actual (before this PR)

Resets on every rotation. Each reset drops mid-conversation context. Reporter measured ~16 resets/24h, peaking at 2 resets in 3 minutes near token expiry.

Actual (after this PR)

Local test verification: pnpm test src/agents/cli-auth-epoch.test.ts → 16 passed. The new and rebased cases prove (a) token rotation no longer flips, (b) OAuth and token epochs match, (c) auth-profile token rotation no longer flips, (d) OAuth profile aliases for the same account stay stable, and (e) real email identity changes still flip the epoch.

Real behavior proof

  • Behavior or issue addressed: Claude CLI and auth-profile token rotation should not flip OpenClaw's auth epoch or reset reusable sessions, while a real account identity change must still invalidate the epoch.
  • Real environment tested: local OpenClaw checkout at 57502b29fb0c8354124ae65d5861af75f855e25e, macOS host, Node 24.15.0, pnpm 10.33.2. The proof exercised the production resolveCliAuthEpoch path with controlled Claude CLI and auth-profile credential providers.
  • Exact steps or command run after this patch: ran a pnpm exec tsx -e proof script that resolved an epoch for a Claude token credential, rotated the token, resolved again, then repeated the same rotation check for an auth-profile token credential and finally changed the auth-profile email identity.
  • Evidence after fix:
v24.15.0
claude_token_rotation_stable=true
profile_token_rotation_stable=true
profile_email_identity_change_flips=true
epoch_version=5
  • Observed result after fix: rotating token material leaves the Claude CLI and auth-profile epochs stable, while changing the email identity flips the profile epoch as expected.
  • What was not tested: live macOS keychain rotation timing. The proof targets the encoding/epoch layer that made the parser's transient token-shaped read harmful.

Evidence

RUN  v4.1.5
Test Files  1 passed (1)
     Tests  16 passed (16)

pnpm test src/agents/cli-auth-epoch.test.ts → 16 passed.
pnpm test src/agents/cli-credentials.test.ts → 16 passed (no regressions).
pnpm test src/agents/cli-session.test.ts → 12 passed (auth-epoch consumer).
pnpm tsgo:core and pnpm tsgo:core:test clean. oxlint 0 warnings, 0 errors. Format check clean.

Followed clawsweeper acceptance criteria from the issue:

  • pnpm test src/agents/cli-auth-epoch.test.ts src/agents/cli-credentials.test.ts
  • pnpm exec oxfmt --check --threads=1 src/agents/cli-auth-epoch.ts src/agents/cli-auth-epoch.test.ts

Human Verification

  • Verified the new regression cases catch the bug: temporarily reverting either guard makes the relevant new test fail with the exact expect(second).toBe(first) mismatch.
  • Verified that real account changes still invalidate: the new "changes token auth-profile epochs when the email identity changes" test asserts the inverse and passes.
  • Verified that the OAuth side is unaffected: the existing OAuth tests (keeps identity-less claude cli oauth epochs stable across token changes, keeps oauth auth-profile epochs stable across token refreshes, changes oauth auth-profile epochs when the account identity changes) all still pass with their original assertions.
  • Confirmed no other consumers depend on the old token-flipping behavior — cli-session.test.ts (12 tests) and cli-credentials.test.ts (16 tests) both pass unchanged. prepareExecution at src/agents/cli-runner/prepare.ts:216 only reads the resolved string, not the encoding internals.
  • What I did not verify: live macOS keychain race reproduction. The fix is at the encoding layer, upstream of any keychain or parser behavior; with the encoding stable, the parser's fallthrough is harmless.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes for runtime behavior. The CLI_AUTH_EPOCH_VERSION bump (4 → 5) means cached epochs from prior versions invalidate once on first read; users see one expected auth-epoch reset on the upgrade boundary, then stable epochs afterward.
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: A user actually using a static type: "token" credential (e.g. PAT) and switching to a different token for the SAME identity will no longer get a session reset on the swap. Mitigation: This is the intended behavior per Ayaan's OAuth pattern — token replacement is an authorized refresh, not an identity change. Real identity changes (different email/provider/tokenRef) still invalidate.
  • Risk: The parseClaudeCliOauthCredential partial-read fallthrough still produces a "token" credential type that gets persisted to auth-profile, even though the hash is now stable. The semantic is misleading (claude-cli OAuth users may see a "token" auth-profile entry). Mitigation: Out of scope for this PR per the reporter's preferred fix shape ("identity-only hash for both types"). If parser hardening is desired as defense-in-depth, that's a clean follow-up — the current fix is sufficient for the reported symptom.
  • Risk: CLI_AUTH_EPOCH_VERSION bump invalidates cached epochs once. Mitigation: Documented behavior; one-time upgrade-boundary reset is acceptable cost vs ongoing rotation-induced resets.

@stainlu
stainlu requested a review from a team as a code owner April 29, 2026 17:15
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Apr 29, 2026
@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

Summary
The PR changes CLI auth-epoch hashing for Claude token-shaped credentials and token auth profiles, bumps the auth-epoch version, adds regression tests, and updates the changelog.

Reproducibility: yes. source-level. Current main can parse a partial Claude OAuth credential as token-shaped, hash rotating token material into the auth epoch, and reset same-version reusable sessions when that hash changes.

Real behavior proof
Sufficient (terminal): The PR body supplies terminal proof from a macOS checkout exercising the production auth-epoch resolver after the patch for token/profile rotation stability and identity-change invalidation.

Next step before merge
A focused repair can keep the useful Claude token-rotation fix while restoring the documented generic token-profile invalidation boundary.

Security
Needs attention: The diff weakens the static-token auth boundary for reusable CLI sessions.

Review findings

  • [P2] Preserve static-token auth-profile invalidation — src/agents/cli-auth-epoch.ts:117-123
Review details

Best possible solution:

Land a narrowed auth-epoch fix that keeps Claude token-shaped local reads stable across OAuth rotation, bumps the epoch version, and preserves generic static-token auth-profile invalidation unless a stable identity boundary is explicit.

Do we have a high-confidence way to reproduce the issue?

Yes, source-level. Current main can parse a partial Claude OAuth credential as token-shaped, hash rotating token material into the auth epoch, and reset same-version reusable sessions when that hash changes.

Is this the best way to solve the issue?

No, not as currently proposed. Identity-only hashing for Claude token-shaped local credentials is the right direction, but applying it to every token auth profile is broader than the documented static-token contract.

Full review comments:

  • [P2] Preserve static-token auth-profile invalidation — src/agents/cli-auth-epoch.ts:117-123
    Dropping credential.token from every token auth-profile epoch makes same-profile token swaps invisible when provider, tokenRef, email, and displayName do not change. The CLI backend docs include changed static tokens as auth identity changes that drop stored session reuse, so keep token material in the generic static-token hash or narrow identity-only behavior to Claude-derived profiles with an explicit stable identity boundary.
    Confidence: 0.84

Overall correctness: patch is incorrect
Overall confidence: 0.84

Security concerns:

  • [medium] Static-token swaps can keep reused CLI sessions — src/agents/cli-auth-epoch.ts:120
    At PR head, token auth-profile epochs no longer include credential.token, so a same-profile token-only credential change can leave the old CLI session reusable even though docs say changed static tokens invalidate reuse.
    Confidence: 0.8

Acceptance criteria:

  • git diff --check
  • pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/agents/cli-auth-epoch.ts src/agents/cli-auth-epoch.test.ts
  • node scripts/run-vitest.mjs src/agents/cli-auth-epoch.test.ts src/agents/cli-credentials.test.ts
  • pnpm run tsgo:core
  • pnpm run tsgo:core:test

What I checked:

  • Current main hashes Claude token material: On current main, encodeClaudeCredential still includes credential.token for token-shaped Claude credentials, so a transient token-shaped read can change the auth epoch. (src/agents/cli-auth-epoch.ts:68, ba8a6499f02f)
  • Current main hashes auth-profile token material: On current main, encodeAuthProfileCredential includes credential.token for generic token auth profiles, which preserves invalidation on static-token swaps. (src/agents/cli-auth-epoch.ts:102, ba8a6499f02f)
  • Parser can produce token-shaped Claude credentials: parseClaudeCliOauthCredential returns a token credential when accessToken and expiresAt are present but refreshToken is absent, matching the linked report's partial-read race shape. (src/agents/cli-credentials.ts:115, ba8a6499f02f)
  • Auth-epoch drift invalidates sessions: resolveCliSessionReuse returns invalidatedReason: "auth-epoch" when same-version stored and current auth epochs differ. (src/agents/cli-session.ts:164, ba8a6499f02f)
  • Documented static-token boundary: The CLI backend docs say stored CLI session reuse is dropped when the selected auth identity changes, including changed static tokens, while OAuth access and refresh rotation should not cut reuse. Public docs: docs/gateway/cli-backends.md. (docs/gateway/cli-backends.md:240, ba8a6499f02f)
  • PR head drops token-profile token material: At PR head, the token branch hashes provider, tokenRef, email, and displayName but no longer hashes credential.token, making same-profile token-only swaps invisible to the auth epoch. (src/agents/cli-auth-epoch.ts:117, 38e0aeaa2d0b)

Likely related people:

  • obviyus: Auth-epoch OAuth stabilization and identity-keying commits define the stable-identity pattern this PR extends. (role: introduced adjacent behavior; confidence: high; commits: 1ff461fe7b9d, 3eb6edc67c2f; files: src/agents/cli-auth-epoch.ts, src/agents/cli-auth-epoch.test.ts, src/agents/cli-session.ts)
  • openperf: Authored the merged Gemini identity-based auth-epoch change using the same stable-identity and version-bump pattern. (role: adjacent area contributor; confidence: high; commits: bc73141e8284; files: src/agents/cli-auth-epoch.ts, src/agents/cli-credentials.ts, src/agents/cli-auth-epoch.test.ts)

Remaining risk / open question:

  • Same-profile static-token auth-profile swaps would no longer invalidate reusable CLI sessions, contrary to the current CLI backend docs.
  • I did not rerun the live macOS keychain timing race; the reproduction and proof are source-level plus contributor terminal proof.

Codex review notes: model gpt-5.5, reasoning high; reviewed against ba8a6499f02f.

@aderius

aderius commented Apr 29, 2026

Copy link
Copy Markdown

Thanks for picking this up. The CLI_AUTH_EPOCH_VERSION bump to 5 is exactly the migration path I missed when applying an identity-only hashing patch locally — without it, sessions with version-4 bindings get one stale-hash reset on first resume after the algorithm change. Your bump avoids that entirely. LGTM.

One defense-in-depth observation while you're in this code, scoped separately from parser hardening:

getLocalCliCredentialFingerprint returns undefined when readClaudeCliCredentialsCached returns null entirely (not just falls through to type: "token"). That removes the local: part from the parts array → shape change → hash flips. With your fix, the type:"token" fallthrough is harmless because both branches now hash to the same identity, but a true null parse still shape-flips.

A null-safe fallback closes that path:

case "claude-cli": {
    const credential = cliAuthEpochDeps.readClaudeCliCredentialsCached({
        ttlMs: 5e3,
        allowKeychainPrompt: false
    });
    return hashCliAuthEpochPart(encodeClaudeCredential(credential ?? { type: "oauth", provider: "anthropic" }));
}

I observed this empirically on macOS — once-per-rotation pattern post-v1-patch (3 sessions resets at 08:01 EDT today, before I added the null-safe fallback). After both patches, zero further race-induced resets across 5h runtime; only the one expected migration reset.

Happy to file as a separate follow-up PR if you'd prefer it scoped that way — not blocking this fix.

Re-review progress:

@stainlu

stainlu commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

thanks for the empirical confirmation and the null-safe fallback catch — that shape-flip was a real gap, the encoder-only fix wasn't sufficient.

just rebased onto current main (resolves the dirty state clawsweeper flagged) and rolled the null-safe fallback into the same commit since it's the same root cause and your validation makes it ready-to-land:

  • getLocalCliCredentialFingerprint now passes a synthetic { type: "oauth", provider: "anthropic" } identity to encodeClaudeCredential when the keychain read returns null, keeping the parts-array shape stable across both transient type: "token" fallthroughs and full parse failures
  • new test case keeps claude cli epochs stable when the keychain read fails entirely locks in the null-credential path
  • existing "returns undefined" test split so claude-cli's stable behavior and other providers' undefined behavior are asserted separately
  • changelog entry expanded to cover the absent-credential fallback alongside the encoder fix

scoped to claude-cli per your repro; codex-cli and google-gemini-cli have the same structural shape but no reported empirical evidence yet, so leaving those for a separate follow-up if anyone hits the same pattern there.

13 cli-auth-epoch tests pass, 16 cli-credentials tests pass, tsgo:core + core:test clean. CI rerun pending after force-push.

@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch 2 times, most recently from c484f66 to 9f05be3 Compare May 1, 2026 07:52
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch 2 times, most recently from c2fe876 to cf125cc Compare May 7, 2026 10:28
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 7, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from cf125cc to 57502b2 Compare May 7, 2026 10:41
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from 57502b2 to c9e64f2 Compare May 7, 2026 14:47
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from c9e64f2 to 96d3857 Compare May 8, 2026 16:25
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 8, 2026
@stainlu

stainlu commented May 8, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and resolved the cli-auth-epoch.test.ts conflict by keeping current main's assertion helper plus this PR's identity-only Claude token/null-read behavior.

Local proof:

  • pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/agents/cli-auth-epoch.ts src/agents/cli-auth-epoch.test.ts
  • git diff --check
  • OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test src/agents/cli-auth-epoch.test.ts src/agents/cli-credentials.test.ts
  • pnpm tsgo:core
  • pnpm tsgo:core:test

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 8, 2026
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from 96d3857 to a795060 Compare May 10, 2026 04:16
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from a795060 to 78a9d6b Compare May 10, 2026 04:56
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from 78a9d6b to 047feda Compare May 10, 2026 05:01
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@stainlu

stainlu commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

Fixed the ClawSweeper auth-state blocker.

I removed the synthetic null Claude credential fallback, so identity-only hashing now applies only when a Claude credential is actually read. A true absent credential returns undefined again, preserving reusable-session invalidation on logout/removal, while token-shaped Claude reads and token auth-profile rotation remain identity-stable.

Validation:

  • pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/agents/cli-auth-epoch.ts src/agents/cli-auth-epoch.test.ts
  • git diff --check
  • env OPENCLAW_TEST_HEAVY_CHECK_LOCK_HELD=1 OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test src/agents/cli-auth-epoch.test.ts src/agents/cli-credentials.test.ts
  • pnpm run tsgo:core
  • pnpm run tsgo:core:test

@clawsweeper re-review

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot added size: S and removed size: M proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 12, 2026
@clawsweeper

clawsweeper Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 12, 2026
@stainlu
stainlu force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from 9e059ce to 38e0aea Compare May 12, 2026 05:55
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 12, 2026
@clawsweeper

clawsweeper Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 12, 2026
@RomneyDa

Copy link
Copy Markdown
Member

Heads up: this PR needs to be updated against current main before the new required Dependency Guard check can pass.

@steipete steipete self-assigned this May 31, 2026
stainlu and others added 4 commits May 31, 2026 11:06
…nclaw#74312)

Extend Ayaan's OAuth identity-only hashing pattern (1ff461f,
3eb6edc) and Chunyue's Gemini extension (openclaw#71076) to the static-token
credential branch. Token rotation and transient partial keychain reads
no longer flip the claude-cli auth-epoch.

Before: encodeClaudeCredential token branch hashed credential.token,
and encodeAuthProfileCredential token case did the same. Either
rotation or a partial keychain read (where parseClaudeCliOauthCredential
sees accessToken without refreshToken and falls into the token branch)
flipped the auth-epoch and forced cli session reset: reason=auth-epoch
on every active claude-cli conversation. macOS keychain-only setups
hit the auth-profile path as the dominant trigger.

After: encodeClaudeCredential routes both branches through
encodeOAuthIdentity, collapsing partial reads onto the same
provider-keyed identity hash. encodeAuthProfileCredential token case
drops credential.token from the hash; identity fields (provider,
tokenRef, email, displayName) are the discriminator.

CLI_AUTH_EPOCH_VERSION bumped 4 to 5.

Test updates flip the existing token-flips assertion to stability,
add a regression case proving Claude CLI OAuth and token epochs match
(closing the partial-read race), and add auth-profile token tests for
both rotation stability and identity-change invalidation.
@steipete
steipete force-pushed the fix/issue-74312-auth-epoch-identity-only-token branch from 38e0aea to c2b786f Compare May 31, 2026 10:15
@steipete

Copy link
Copy Markdown
Contributor

Verification before merge for c2b786fd3d1b7c7757994a9d0ca779e292b8371d:

Known proof gap: no live macOS Claude keychain rotation timing test was run; the proof exercises the auth-epoch layer that made transient token-shaped Claude CLI reads reset reusable sessions.

@steipete
steipete merged commit 4b1e5b7 into openclaw:main May 31, 2026
153 of 156 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude-cli auth-epoch flips on token rotation, forcing session resets mid-conversation

4 participants