Skip to content

fix(api): guard rotating providers in manual token-refresh route (Codex family-revocation) - #3030

Merged
diegosouzapw merged 1 commit into
release/v3.8.8from
fix/codex-manual-refresh-rotating-guard
Jun 1, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.8from
fix/codex-manual-refresh-rotating-guard

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Problem

Even after the quota-sync (refreshAndUpdateCredentials) and connection-test guards (#2941), opening the Quota / Providers dashboard still cascaded Codex multi-account setups — all accounts of the same domain died with [403] <!DOCTYPE.

Root cause (traced live on the VM via call_logs + proxy_logs + authenticated reproduction)

The deployed quota-page paths are clean:

But POST /api/providers/[id]/refresh (the manual/auto "refresh token" endpoint) had no rotating-provider guard — verified empirically: calling it on a Codex connection triggered refresh_token_reused. The dashboard auto-refreshes every expiring connection on a page load (and an old cached frontend still bulk-calls it), so each Codex account's single-use refresh_token got rotated. Under the shared Auth0 client_id, Auth0 revoked the whole token family (openai/codex#9648) → every account but the last died.

This was the last unguarded proactive-refresh entry point for rotating providers.

Fix

POST /api/providers/[id]/refresh now skips proactive rotation when rotationGroupFor(provider) !== null, returning { success: true, skipped: true, message } and deferring genuine expiry to the reactive, serialized 401 path on the next real request. Non-rotating providers keep refreshing on demand. Same guard pattern as refreshAndUpdateCredentials and the connection-test route.

Test (TDD, red→green)

tests/unit/codex-manual-refresh-rotating-guard.test.ts — source-assertion style (matches token-refresh-race-comprehensive.test.ts #2941): asserts the route imports rotationGroupFor and guards before getAccessToken, returning early.

Regression: token-refresh-race-comprehensive, refresh-serializer, codex-quota-sync-no-proactive-refresh, providers-test-route-cli-map all pass. lint + typecheck clean.

Operator note

Existing dashboard tabs should be hard-refreshed (Ctrl+Shift+R) to drop the old cached frontend; this backend guard makes the cascade impossible regardless.

…ex family-revocation)

POST /api/providers/[id]/refresh was the last unguarded proactive-refresh
entry point for rotating-refresh providers. The dashboard auto-refreshes every
expiring connection on a page load (and an old cached frontend bulk-calls this
endpoint), so each Codex account's single-use refresh_token got rotated; Auth0
then revoked the whole token family (openai/codex#9648) — every account but the
last died with [403] <!DOCTYPE. refreshAndUpdateCredentials (quota-sync) and the
connection-test route were already guarded; this closes the gap.

The route now skips proactive rotation when rotationGroupFor(provider) !== null
and returns {success,skipped,message}, deferring genuine expiry to the reactive,
serialized 401 path. Non-rotating providers keep refreshing on demand.

Test: codex-manual-refresh-rotating-guard (source-assertion, matches the
token-refresh-race-comprehensive #2941 style).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a guard in the manual/auto token refresh endpoint (POST /api/providers/[id]/refresh) to skip proactive token rotation for rotating-refresh providers (such as Codex/OpenAI sharing an Auth0 client ID). This prevents Auth0 token-family revocation when multiple sibling accounts are refreshed concurrently or in bulk. A unit test has been added to verify that the route imports rotationGroupFor and applies the guard before calling getAccessToken, and the CHANGELOG.md has been updated. I have no feedback to provide.

@diegosouzapw
diegosouzapw merged commit 46c482c into release/v3.8.8 Jun 1, 2026
3 checks passed
@diegosouzapw
diegosouzapw deleted the fix/codex-manual-refresh-rotating-guard branch June 1, 2026 06:03
@kilo-code-bot

kilo-code-bot Bot commented Jun 1, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

This PR addresses a critical issue with Codex multi-account family-revocation cascade by adding a guard in the manual refresh endpoint to skip proactive token rotation for rotating-refresh providers (Codex/OpenAI share an Auth0 client_id).

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Files Reviewed (3 files)
  • src/app/api/providers/[id]/refresh/route.ts - Guard logic for rotating providers, uses sanitized error response
  • tests/unit/codex-manual-refresh-rotating-guard.test.ts - Source-assertion tests verifying guard placement
  • CHANGELOG.md - Documentation of fix

Observations

Security: The error handling at line 142 uses (error as Error).message which is already covered by internal logging patterns. This is acceptable since:

  • The endpoint already exists and follows existing patterns in the codebase
  • Error messages here are operational (not exposing upstream provider errors that would need upstream sanitization)

Consistency: The import path @omniroute/open-sse/services/refreshSerializer.ts uses .ts extension which is the established pattern in this codebase (111 existing imports across src/app/api/ and test files).

Guard Logic Verification: The guard at line 61 correctly:

  • Checks rotationGroupFor(provider) !== null before any token refresh attempt
  • Returns early with skipped: true flag for rotating providers
  • Defers to the reactive, serialized 401 path for genuine expiry handling
  • Matches the existing pattern in test/route.ts (line 430) for the same protection

Reviewed by laguna-m.1-20260312:free · 2,185,764 tokens

diegosouzapw added a commit that referenced this pull request Jun 2, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: #3052 (heap-pressure auto-calibration),
  #3051/#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  #3049/#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (#2958 scope fix, #2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (#2966–#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration),
  cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration),
  cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…add contributor hall

Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG:

- Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration),
  cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch),
  cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77).
- Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959
  Notion context source) and @JxnLexn (per-API-key stream default mode).
- Added an Added entry for the per-API-key stream default mode feature.
- Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format.

Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original
issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
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.

1 participant