Passport deserializeUser and stale session data: is there a supported way to refresh the cached user object?
0 reputation · 15 Oct 2020, 09:27 UTC
0 reputation · 15 Oct 2020, 09:27 UTC
I want a Passport.js (0.5.x-era session API) application to serve up-to-date user data on every request, even when the underlying database record changes after login. Passport stores whatever serializeUser writes into the session and calls deserializeUser once per request, but as far as the documented API goes there is no built-in mechanism to invalidate or refresh that cached object when the record is updated.
The concern is practical: role or permission changes, account suspension, or profile edits should take effect without forcing the user to log out and back in. Re-querying the database inside deserializeUser on every request works conceptually, but adds a query per request and it is unclear whether this is the intended pattern or merely a workaround for a missing feature. With a shared session store such as Redis, stale serialized data could also persist indefinitely without a TTL.
Community discussions mention this gap, but I have not found a documented strategy option, middleware hook, or configuration flag in Passport itself that addresses automatic refresh.
deserializeUser re-query the only sanctioned approach?35275 reputation · 15 Oct 2020, 12:56 UTC
No. Passport does not expose a built-in option, middleware hook or configuration flag to invalidate or automatically refresh the deserialized user object when the database record changes. The supported extension point is deserializeUser itself. Whatever it returns becomes req.user for that request.
For the goal of reflecting role, permission, suspension or profile changes without logout, the sanctioned approach is to make deserializeUser look up the current record on each authenticated request. There is no roadmap or open proposal for built-in stale-session handling in Passport’s session API.
serializeUser writes an identifier into the session. Passport does not cache a user object.deserializeUser is invoked once per authenticated request to turn that identifier back into req.user.deserializeUser performs. Session TTL in the store, e.g. Redis, only bounds how long the session identifier persists.The “cached user” is not in Passport, it is the result you return from deserializeUser. Re-querying per request is the documented intended use and gives immediate freshness on the next request.
Common production trade-offs:
deserializeUser. An indexed primary-key lookup is usually cheap enough. This is mandatory for hard revocation such as account suspension.deserializeUser, e.g. Redis with a few seconds TTL, plus explicit invalidation of that cache key on user writes. This reduces DB load while bounding staleness.req.login(user) to re-run serializeUser and overwrite the session copy for that session only. This does not affect other devices.Performance and consistency guidance is not documented by Passport. Measure query cost in deserializeUser under load before adding caching. Caching re-introduces staleness; the TTL and invalidation on writes determine worst-case delay.
deserializeUser fetches the user by the stored id on every request and returns the fresh record or null to end the session.maxAge and touch on activity to bound how long stale session identifiers persist in the shared store.Verification: log calls inside deserializeUser during a test session, change a user role in the DB, make a new authenticated request without logging out, and confirm req.user reflects the change. Inspect session store TTL for the session key to confirm persistence bounds.
One diagnostic detail that changes the recommendation: do you require immediate revocation across all devices, or is per-session refresh acceptable? Cross-device instant revocation requires a DB lookup or cache invalidation in deserializeUser; per-session refresh can be achieved with req.login after a change.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.