Skip to content

fix(codex-auth): replace lexicographic timestamp compare with refresh-token identity check - #29

Closed
dean0x wants to merge 2 commits into
mainfrom
fix/codex-auth-concurrent-refresh-guard
Closed

fix(codex-auth): replace lexicographic timestamp compare with refresh-token identity check#29
dean0x wants to merge 2 commits into
mainfrom
fix/codex-auth-concurrent-refresh-guard

Conversation

@dean0x

@dean0x dean0x commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replaces the last_refresh string > comparison in persistTokens with a refresh-token identity check (fileNow.tokens.refresh_token !== refreshToken), making the concurrent-refresh guard format-independent and collision-safe.
  • Adds an early return when fileIsNewer is true but materialFrom fails on the newer file, preventing the guard from being silently bypassed on a parse error.
  • Adds three new tests covering the previously untested cases: format-diverged timestamps, same-second collision, and the guard-bypass-on-parse-failure path.

Changes

src/codex-auth.ts

  • persistTokens gains a refreshToken: string parameter (the token we sent to the OAuth endpoint).
  • Call site in doRefresh updated to pass refreshToken.
  • fileIsNewer now checks fileNow.tokens.refresh_token !== refreshToken as primary signal; Date.parse numeric comparison retained as secondary || signal.
  • Early return added after materialFrom fails on the newer file so control cannot fall through into the merge/write path.

test/unit/codex-auth.test.ts

  • Test: guard fires when last_refresh formats diverge but refresh_token differs (format-divergence case).
  • Test: guard fires in the same-second collision case via identity check.
  • Test: no write occurs when fileIsNewer is true but materialFrom fails (Fix 2 path).

CHANGELOG.md — Unreleased section added.

Breaking Changes

None. persistTokens is private.

Reviewer Focus Areas

  • The fileIsNewer logic in persistTokens: verify the primary (identity) and secondary (timestamp) signals are ordered correctly and that fallback behavior under edge cases (both tokens equal, timestamps unparseable via Date.parse → NaN) is safe. NaN > NaN is false, so an unparseable timestamp does not produce a false-positive newer signal — control reaches the merge and writes, which is the safe fallback when the tokens are equal (no rotation detected).
  • Fix 2's early return: verify it only fires inside if (fileIsNewer) and cannot affect the normal (non-newer) code path.

Fixes #26

dean0x and others added 2 commits August 19, 2026 00:59
…en identity check

The concurrent-refresh guard in `persistTokens` used lexicographic `>` on
`last_refresh` strings, which is wrong: ISO-8601 format divergence (e.g.,
fractional-second precision) or a same-second write makes the comparison
unreliable. If the guard missed, subswitch would clobber the Codex CLI's
rotated refresh_token with its own already-consumed token, causing an
`invalid_grant` failure on the next cycle with no downstream rescue.

Fix 1: replace the timestamp comparison with a refresh-token identity check
(`fileNow.tokens.refresh_token !== refreshToken`). This is format-independent
and is the established idiom in the `invalid_grant` retry path at line 291.
A secondary `Date.parse` numeric comparison is kept for the same-second case.

Fix 2: add an early return when `fileIsNewer` is true but `materialFrom` fails
on the newer file. Previously, control fell through into the merge and wrote
anyway, defeating the guard even when it had correctly fired.

Fixes #26

Co-Authored-By: Claude <noreply@anthropic.com>
…terialFrom fallback

- CHANGELOG: version header from [Unreleased] to [0.2.1] - 2026-08-19 so the
  release-notes extractor can locate the entry (RELEASE-FLOW.md:137)
- Comments: reword 'same-second case' to 'cross-format ordering' in both the
  CHANGELOG entry and the codex-auth.ts inline comment; the Date.parse secondary
  signal fires on format mismatches (e.g. '.500Z' vs 'Z'), not same-second writes
  where identical strings produce equality. Add NaN note (Change 5).
- Regression test: fix the format-diverged fixture so lexicographic order is
  genuinely wrong while chronological order is right. Baseline '2027-01-15T08:00:00.000Z',
  file writes epoch-millis '1800000005000' ('1' < '2' lexicographically → string guard
  misses; identity check fires). Empirically confirmed: FAILS on main, PASSES on branch.
  Add Falsifier: comment per convention.
- materialFrom fallback: when fileIsNewer is true but materialFrom fails on the newer
  file, serve the just-refreshed tokens from memory rather than returning an error.
  No write occurs (clobber protection intact); the request now succeeds. Update the
  corresponding test to assert success + no write.

Co-Authored-By: Claude <noreply@anthropic.com>
@dean0x

dean0x commented Aug 19, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #33. All commits from this branch are contained in wave/passthrough-hardening, which carries this work plus the transparency-hardening follow-up. Closing unmerged.

@dean0x dean0x closed this Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

persistTokens: last_refresh compared lexicographically — clobber guard depends on the Codex CLI's timestamp format

1 participant