Fix: persist refreshed OAuth tokens to session store#266
Conversation
After token refresh in isAuthenticated middleware, re-serialize the updated tokens into req.session.passport.user and call session.save() so they persist to PostgreSQL. Prevents redundant refreshTokenGrant calls on every request after token expiry. Fixes #265 https://claude.ai/code/session_01JUweaU4PunmvR2R3oD9SKD
📝 WalkthroughWalkthroughThe PR adds explicit persistence of refreshed OAuth tokens to the Express session store in the Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/replit_integrations/auth/replitAuth.ts`:
- Around line 221-234: The code assigns refreshed tokens to (req.session as
any).passport.user then calls req.session.save(...) but calls next()
immediately, so move the next() invocation into the req.session.save callback
and handle save failures by forwarding the error to Express (call next(err) when
err is truthy) and only call next() with no args when save succeeds; update the
block around req.session.save(...) / next() accordingly so the request only
proceeds after the session persist completes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 294071d9-d6a3-431a-91ac-71231146b956
📒 Files selected for processing (1)
server/replit_integrations/auth/replitAuth.ts
- Move next() inside session.save() callback to prevent race condition where response dispatches before session persistence completes - Return 500 on session save failure instead of proceeding - Log err.message instead of full error object to prevent token leakage - Extract serializeUserPayload() helper to eliminate shape duplication between serializeUser and the refresh path - Add 7 tests for isAuthenticated middleware covering all code paths https://claude.ai/code/session_01JUweaU4PunmvR2R3oD9SKD
…ging - Add null check for req.session.passport before assignment to prevent TypeError on corrupted session rows - Use safe error message extraction (err instanceof Error) to handle non-Error objects in session.save callback https://claude.ai/code/session_01JUweaU4PunmvR2R3oD9SKD
Summary
When the OIDC access token expires, the
isAuthenticatedmiddleware refreshes it viarefreshTokenGrantbut previously only updated tokens in memory. Withresave: false, express-session never persisted the refreshed tokens to the PostgreSQL session store, causing redundant refresh calls on every subsequent request (~100-500ms extra latency each) and potential unexpected logouts if the provider issues one-time-use refresh tokens.This PR persists refreshed tokens to the session store and hardens the refresh path against several edge cases discovered during review.
Changes
Core fix (
server/replit_integrations/auth/replitAuth.ts):req.session.passport.userand callreq.session.save()next()inside thesession.save()callback to prevent race condition where the response dispatches before persistence completesHardening:
serializeUserPayload()helper to eliminate shape duplication betweenserializeUser(login) and the refresh pathreq.session.passportbefore assignment to handle corrupted session rows gracefullyerr instanceof Error ? err.message : String(err)) to prevent logging undefined and avoid leaking full error objectsTests (
server/replit_integrations/auth/replitAuth.test.ts):isAuthenticatedmiddleware covering: unauthenticated, missing expiry, valid token, missing refresh token, successful refresh with session persistence verification, failed refresh grant, and session save failure (500 response)How to test
npm run check— TypeScript passesnpm run test— All 1764 tests pass (7 new)npm run build— Production build succeedsrefreshTokenGrantcallsRelated issues
https://claude.ai/code/session_01JUweaU4PunmvR2R3oD9SKD