Skip to content

Fix refresh token bugs: preserve on OIDC omission, deduplicate concurrent refreshes#271

Merged
bd73-com merged 4 commits intomainfrom
claude/fix-bugs-dw5NV
Mar 26, 2026
Merged

Fix refresh token bugs: preserve on OIDC omission, deduplicate concurrent refreshes#271
bd73-com merged 4 commits intomainfrom
claude/fix-bugs-dw5NV

Conversation

@bd73-com
Copy link
Owner

Summary

Fixes two bugs in the OIDC token refresh middleware (isAuthenticated) that caused intermittent session invalidation. The refresh token was silently overwritten with undefined when the OIDC provider omitted it from the response (violating RFC 6749 §6), and concurrent requests with expired tokens raced to call refreshTokenGrant, invalidating rotated tokens.

Closes #268, closes #269.

Changes

Refresh token preservation (bug #268)

  • Use nullish coalescing (tokens.refresh_token ?? user.refresh_token) in updateUserSession to preserve the existing token when the OIDC provider omits it from the refresh response

Concurrent refresh deduplication (bug #269)

  • Add per-session in-flight promise cache (inflightRefreshes Map) keyed by req.sessionID
  • First request creates the refresh promise; concurrent requests for the same session await it
  • Promise returns TokenResult so each waiter updates its own req.user object (Express deserializes separate user references per request)
  • 15-second timeout via Promise.race prevents indefinite hangs from stalling OIDC providers
  • Defensive cap at 10,000 entries with console.warn when reached — degrades gracefully by skipping dedup
  • Cleanup via .catch(() => {}).finally(() => delete) prevents unhandled rejections and map leaks

Tests

  • Test: refresh token preserved when OIDC omits it
  • Test: concurrent refresh calls deduplicated (single refreshTokenGrant call)
  • Test: both concurrent waiters receive fresh tokens
  • Test: rejection propagates 401 to all concurrent waiters
  • Added sessionID to mock request objects

How to test

  1. npm run check && npm run test — all 1767 tests pass
  2. npm run build — production build succeeds
  3. Manual: Log in, wait for token expiry, trigger concurrent API calls — both should succeed without 401
  4. Manual: Test with an OIDC provider that omits refresh_token in refresh responses — session should persist

claude added 4 commits March 25, 2026 18:00
…concurrent refreshes

- Fix #268: Use nullish coalescing to keep existing refresh_token when
  OIDC provider omits it from the refresh response (per RFC 6749 §6)
- Fix #269: Add per-session in-flight promise cache so concurrent
  requests share a single refreshTokenGrant call instead of racing
- Update test mocks with sessionID to support deduplication logic

https://claude.ai/code/session_012tfRZzAZk31qLhhHq33bZE
- Add MAX_INFLIGHT_REFRESHES (10,000) to prevent unbounded map growth
  under extreme concurrent session expiry
- Add test: refresh token preserved when OIDC omits it (#268)
- Add test: concurrent refresh calls deduplicated (#269)

https://claude.ai/code/session_012tfRZzAZk31qLhhHq33bZE
…ogging

- Refactor inflight promise to return TokenResult so each concurrent
  waiter updates its own user object (fixes stale-token write-back)
- Add 15s timeout via Promise.race to prevent indefinite OIDC hangs
- Add console.warn when inflight map reaches capacity
- Add test: concurrent rejection propagates 401 to all waiters
- Add assertions verifying second waiter receives fresh tokens

https://claude.ai/code/session_012tfRZzAZk31qLhhHq33bZE
@github-actions github-actions bot added the fix label Mar 25, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

Warning

Rate limit exceeded

@bd73-com has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 40 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 944c2e91-c2a5-449c-ab99-c77c65bdc435

📥 Commits

Reviewing files that changed from the base of the PR and between bb72750 and 9fa0f50.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !package-lock.json
📒 Files selected for processing (2)
  • server/replit_integrations/auth/replitAuth.test.ts
  • server/replit_integrations/auth/replitAuth.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-bugs-dw5NV

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@bd73-com bd73-com merged commit 871b90d into main Mar 26, 2026
1 check passed
@github-actions github-actions bot deleted the claude/fix-bugs-dw5NV branch March 26, 2026 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants