Skip to content

Fix Grok browser cookie refresh - #2458

Merged
steipete merged 5 commits into
steipete:mainfrom
olddonkey:agent/fix-grok-cookie-refresh
Jul 28, 2026
Merged

Fix Grok browser cookie refresh#2458
steipete merged 5 commits into
steipete:mainfrom
olddonkey:agent/fix-grok-cookie-refresh

Conversation

@olddonkey

@olddonkey olddonkey commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Allow explicitly user-initiated Grok CLI cookie refreshes to import browser cookies while keeping ordinary CLI and background imports gated.
  • Cache validated grok.com sessions and reuse them for background/app and later CLI fetches.
  • Clear cached Grok cookies only on authentication failures, with conditional mutations so concurrent replacements are preserved.
  • Keep the teamUsageUnsupported classification for cached sessions whose trailing cookie-only attempt fails with a non-authentication error, so team principals continue degrading to identity-only data instead of pinning an ineradicable failing cache entry.
  • Read the cached entry from the conditional-mutation observation (single Keychain read, matching the Claude web fetcher).
  • Document the explicit refresh behavior, the cache/eviction policy, and the browser-cookie import contract; add regression coverage for availability, refresh staging, eviction policy, and both team-cookie fallbacks.

Root cause

The generic cookie refresh command marks provider work as user initiated, but the Grok strategy still rejected every CLI browser import unless CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT was set manually. After a browser session did validate, Grok also did not store it in CookieHeaderCache, so the refresh command had no staged cookie to commit and background fetches could not reuse the session behind the Chromium Keychain gate.

Two follow-up fixes hardened the cached path:

  1. (Codex review finding) A stale cached team cookie could hide its trailing 401 behind the teamUsageUnsupported classification and never evict. The cached path now prefers a trailing authentication failure so stale sessions evict and re-import.
  2. (Inverse case) A still-valid cached team cookie whose trailing cookie-only attempt fails with gRPC 9 "No personal team" (a non-authentication error) must not surface that raw error — it would bypass the identity-only degradation and could never be evicted. The trailing error now wins only when it is an authentication failure; otherwise teamUsageUnsupported is preserved.

Security note: userInitiated import boundary

Producers of ProviderInteractionContext.userInitiated were audited for this change: in the CLI, only the explicit cookie refresh command sets it (CLICookieCommand.performCookieRefreshes); usage and serve explicitly bind .background. In the app, every setter is a direct UI gesture, and the app runtime was already permitted via runtime == .app, so the new clause only affects explicit CLI refreshes. Scheduled/background work inherits the .background task-local default. The contract is now documented on canImportBrowserCookies.

Live behavior proof

Validated on macOS with the final branch binary (87cffdea) and a real signed-in Chrome profile. Cookie values, usage values, reset timestamps, and account data are redacted.

1. Ordinary CLI reuses the validated cached session without browser-import opt-in

$ env -u CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT CodexBarCLI usage --provider grok --source web --log-level debug --format json
... debug com.steipete.codexbar.cookie-cache: provider=grok [CodexBarCore] Cookie cache hit
[{"source":"Chrome Default","usage":{"identity":{"providerID":"grok"},"primary":{"usedPercent":<redacted>,"resetsAt":"<redacted>"},...},"provider":"grok"}]

No browser-cookie import or Keychain-attempt log was emitted.

2. A stale cached session is evicted on a real authentication failure

For this check, the cached entry's cookieHeader was replaced with an invalid value (entry shape untouched); the Chrome source session was left signed in.

$ env -u CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT CodexBarCLI usage --provider grok --source web --log-level debug --format json
... Cookie cache hit
[{"source":"web","error":{"kind":"provider","code":1,"message":"Grok auth.json not found. Run `grok login` to authenticate."},"provider":"grok"}]

grok.com rejected the invalid session with an authentication failure, the entry was evicted, and the run fell through to the auth-file probe (absent on this machine). The next run proves the eviction and that ordinary CLI work stays gated — it exited 1 with no browser-cookie import or Keychain-attempt log:

$ env -u CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT CodexBarCLI usage --provider grok --source web --log-level debug --format json
... Cookie cache miss
[{"error":{"message":"No available fetch strategy for grok.","code":1,"kind":"provider"},"source":"web","provider":"grok"}]

3. Explicit refresh imports, validates, and commits a fresh browser session

$ env -u CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT CodexBarCLI cookie refresh --provider grok --allow-keychain-prompt --format json
[{"provider":"grok","status":"refreshed","message":"Browser cookie refreshed."}]

4. The committed session is reused by the next ordinary CLI fetch

$ env -u CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT CodexBarCLI usage --provider grok --source web --log-level debug --format json
... Cookie cache hit
[{"source":"Chrome Default","usage":{...,"primary":{"usedPercent":<redacted>,"resetsAt":"<redacted>"},...},"provider":"grok"}]

The team-cookie matrix (trailing 401 evicts; trailing gRPC 9 keeps the team classification) is covered by unit tests; no team account was available for a live team run.

Validation

  • swift test --filter "GrokWebBillingFetcherTests|CLICookieRefreshTests" (52 tests in 2 suites passed on 87cffde)
  • make check (clean: 0 violations in 1581 files)
  • Full make test was previously blocked at group 48/61 by four unrelated singularization expectations in SessionEquivalentForecastTests (1 windows versus 1 window); this PR does not touch the forecast implementation or tests.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. labels Jul 26, 2026
@clawsweeper

clawsweeper Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed July 28, 2026, 2:04 AM ET / 06:04 UTC.

ClawSweeper review

What this changes

This PR allows an explicit Grok CLI cookie refresh to import and cache a validated browser session, then reuses it for later app and CLI usage fetches while evicting it only after authentication failures.

Merge readiness

⚠️ Ready for maintainer review - 2 items remain

Keep this PR open for normal maintainer review. It has focused implementation, regression coverage, and convincing redacted macOS proof; the remaining merge-critical point is confirming that the expanded userInitiated browser-cookie-import gate remains limited to direct user actions.

Priority: P2
Reviewed head: c7d85f45fc6a259b5038892e2875dbb3c4a857c0
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) This is a focused, well-proven provider fix with one maintainer-visible authentication-boundary decision remaining.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The PR includes redacted final-branch terminal evidence from a real signed-in Chrome profile covering the changed import, cache, eviction, and reuse behavior.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The PR includes redacted final-branch terminal evidence from a real signed-in Chrome profile covering the changed import, cache, eviction, and reuse behavior.
Evidence reviewed 5 items PR implementation: The Grok strategy adds the explicit user-initiated import condition, checks the Keychain-backed cookie cache before browser discovery, and conditionally removes only cached sessions that fail authentication.
Regression coverage: The PR adds coverage for explicit CLI import, cache-based availability, refresh staging, authentication-only eviction, and team-session fallback behavior.
Maintainer-facing documentation: Provider documentation now distinguishes ordinary CLI runs from the explicit cookie-refresh action and explains cache reuse and authentication-only eviction.
Findings None None.
Security None None.

How this fits together

CodexBar’s Grok usage provider combines local Grok credentials with an optional browser-backed session to fetch billing data. The changed path decides whether a request may read browser cookies, validates and caches that session, and returns full usage or an identity-only fallback.

flowchart LR
  A[CLI or app request] --> B[Interaction context]
  B --> C{Cached Grok session}
  C -->|Valid| D[Billing usage fetch]
  C -->|Missing or auth failed| E{Browser import allowed}
  E -->|Explicit refresh or app| F[Validate browser session]
  E -->|Background or ordinary CLI| G[Credential fallback]
  F --> H[Store validated session]
  H --> D
  D --> I[Usage or identity-only result]
Loading

Decision needed

Question Recommendation
Should the repository accept ProviderInteractionContext.userInitiated as an authorization condition for CLI browser-cookie import, alongside the app runtime and explicit environment override? Accept the explicit refresh boundary: Merge with the documented contract that only direct UI gestures and cookie refresh may set the user-initiated context.

Why: The patch is narrow and proven, but this condition becomes a shared authorization boundary whose safety depends on all present and future producers preserving its direct-user-action meaning.

Before merge

  • Resolve merge risk (P1) - Merging expands a local authentication boundary: any future non-interactive caller that incorrectly sets the task-local interaction context to userInitiated could access Chromium Keychain-backed browser cookies without the environment override.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Patch surface 4 files affected; 246 added, 24 removed The change keeps the implementation, focused regression tests, and provider documentation aligned.
Focused validation 52 tests across 2 suites reported passing The reported tests cover the cache and cookie-refresh behavior that the patch changes.

Merge-risk options

Maintainer options:

  1. Confirm interaction-context callers (recommended)
    Before merge, confirm that every current producer of userInitiated is a direct user action and that background paths explicitly remain .background.
  2. Use a refresh-only capability
    If the shared interaction-context contract is not acceptable, replace it with a narrower capability set only by the explicit cookie refresh command.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Audit all ProviderInteractionContext userInitiated producers and preserve explicit background binding for non-interactive CLI and scheduled work.

Technical review

Best possible solution:

Keep browser-cookie import restricted to the app and explicit refresh actions, retain cache reuse for background requests, and preserve the documented authentication-only eviction behavior with the added regression coverage.

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

Yes, with medium confidence: the supplied final-branch macOS transcripts exercise the reported cache miss, explicit refresh, cache commit, stale-session eviction, and later cache reuse; the base-path behavior is also directly reflected by the changed provider gate.

Is this the best way to solve the issue?

Yes, conditionally: caching only validated sessions and granting browser import for an explicit refresh is a narrow solution, provided maintainers accept the shared userInitiated context as the durable authorization boundary.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5e233b2efcb2.

Labels

Label justifications:

  • P2: This is a bounded provider authentication and usage-refresh fix with meaningful but limited user impact.
  • merge-risk: 🚨 auth-provider: The patch changes when CLI work may import browser-backed authentication cookies and how validated provider sessions are retained or evicted.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR includes redacted final-branch terminal evidence from a real signed-in Chrome profile covering the changed import, cache, eviction, and reuse behavior.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR includes redacted final-branch terminal evidence from a real signed-in Chrome profile covering the changed import, cache, eviction, and reuse behavior.

Evidence

What I checked:

Likely related people:

  • steipete: The PR head incorporates current main through a merge commit authored by steipete; no stronger current-main ownership trail was available in the read-only review environment. (role: recent area contributor; confidence: low; commits: c7d85f45fc6a; files: Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift, Tests/CodexBarTests/GrokWebBillingFetcherTests.swift)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Confirm the complete userInitiated producer audit before merge.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (4 earlier review cycles)
  • reviewed 2026-07-26T09:30:00.401Z sha 0d7d313 :: needs real behavior proof before merge. :: [P3] Remove the release-owned changelog entry
  • reviewed 2026-07-26T10:10:55.426Z sha 9e3ada7 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-26T11:41:45.742Z sha 372ae3d :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-26T22:16:42.571Z sha 87cffde :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 26, 2026
@olddonkey
olddonkey marked this pull request as ready for review July 26, 2026 10:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e3ada765d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

credentials: browserCredentials)
return (snapshot, cached.sourceLabel, false)
} catch {
guard Self.isCookieAuthenticationFailure(error) else { throw error }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Let stale team cookies reach browser fallback

When auth.json contains a non-expired team principal and the cached cookie has expired, the cookie-plus-bearer attempt can return teamUsageUnsupported while the subsequent cookie-only attempt returns 401. fetchValidCookieHeader prioritizes the saved team error, so this guard treats the cached attempt as non-authentication-related and exits before importing the newly signed-in browser session. Regular app refreshes then remain stuck on the stale cache and publish identity-only data until the user runs an explicit cookie refresh; preserve the team error only after fresh browser sessions have also been attempted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 26, 2026
A cached team-limited session whose trailing cookie-only attempt fails
with a non-authentication error (gRPC 9 "No personal team") must keep
degrading to identity-only data instead of failing outright and pinning
a cache entry that authentication-failure eviction can never remove.
Trailing errors now win over the team classification only when they are
authentication failures, so stale cached sessions still evict and
re-import.

Also reads the cached entry from the conditional-mutation observation
(single Keychain read, matching the Claude web fetcher), documents the
browser-cookie import contract and cache behavior, and resets the
display cache in the cookie tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@olddonkey

Copy link
Copy Markdown
Contributor Author

Addressed the remaining review items in 87cffde and refreshed the PR body with final-head proof:

Security: userInitiated cookie-import boundary. All producers of ProviderInteractionContext.userInitiated were audited: in the CLI only the explicit cookie refresh command sets it (CLICookieCommand.performCookieRefreshes), while usage (CLIUsageCommand.swift) and serve (CLIServeCommand.swift) explicitly bind .background. App-side setters are all direct UI gestures, and the app runtime was already permitted through runtime == .app, so the new clause only affects explicit CLI refreshes. Scheduled/background work inherits the .background task-local default and can never reach the Chromium Keychain gate. The contract is now documented on canImportBrowserCookies.

Cached team-cookie fallback (both directions). On top of the earlier fix that lets a stale team cookie surface its trailing 401 for eviction, 87cffde keeps teamUsageUnsupported when the trailing error is not an authentication failure (gRPC 9 "No personal team"), so a still-valid team session keeps degrading to identity-only data instead of failing outright with an entry that authentication-failure eviction can never remove. Both directions have regression tests.

Final-head live proof. The PR body now shows, on binary 87cffdea: cached reuse without opt-in, a stale cached session being rejected by grok.com with a real authentication failure and evicted (next run: cache miss, still gated, no import/Keychain logs), an explicit refresh recommitting a fresh session, and the restored cache-hit fetch.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 26, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 26, 2026
@steipete
steipete merged commit 9ed0dcc into steipete:main Jul 28, 2026
9 checks passed
@steipete

Copy link
Copy Markdown
Owner

Merged after full verification. Per the owner-approved semantics: an explicit CLI cookie-refresh counts as consent to Chromium import, validated Grok sessions are cached in Keychain and reused for background/CLI fetches, and only authentication failures evict (team-unsupported responses keep the cache). Review confirmed the shared CookieHeaderCache conditional-mutation pattern and that fresh imports stay consent-gated. Exact-head CI: all checks green (https://github.com/steipete/CodexBar/actions/runs/30333376489, shard 1 completed after runner backlog). Autoreview clean. Thanks @olddonkey — nice first contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants