Passport SerializeUser & DeserializeUser

What is serializeUser in Passport.js?

In Passport.js, serializeUser is the function that decides what part of the authenticated user object should be stored in the session after a successful login. When a user logs in, Passport receives the complete user object returned by your authentication strategy, but instead of storing everything, it reduces that object to a minimal piece of data, typically a unique identifier like user.id. This step is important because sessions should remain lightweight and secure, and storing excessive user data can lead to performance issues and unnecessary exposure of sensitive information.

To understand how this fits into the full authentication cycle, you should also look at how the Passport session authentication flow works.

What is deserializeUser?

While serializeUser handles storing data, deserializeUser performs the reverse operation. It takes the small piece of data stored in the session, usually the user ID, and uses it to retrieve the full user object, typically from a database. Once retrieved, Passport attaches this object to req.user, making it accessible throughout your application. This process ensures that even though only minimal data is stored in the session, your application still has access to complete user details when needed.

This becomes clearer when you understand how req.user works in Passport, especially in protected routes.

How serializeUser and deserializeUser work together

The interaction between serializeUser and deserializeUser forms the backbone of session-based authentication in Passport. During login, serializeUser runs once and stores the identifier in the session. On every subsequent request, Passport reads that identifier from the session and calls deserializeUser to reconstruct the full user object. This creates a seamless experience where the user remains authenticated across multiple requests without repeatedly logging in, while the server maintains efficiency by not storing large objects in the session.

To see the full lifecycle visually, refer to the Passport authentication flow diagram.

When is serializeUser called?

A common misunderstanding is assuming that serializeUser runs on every request, but in reality, it is only executed immediately after successful authentication. Once the user is logged in and the session is established, serializeUser is no longer involved. Instead, deserializeUser is triggered on each request that contains a valid session, making it the more frequently executed function and therefore more important to optimize for performance.

This behavior depends heavily on middleware configuration. Most of these issues can be avoided by properly understanding the Passport middleware setup order, especially the difference between initialize and session middleware.

Using serializeUser with TypeScript

When working with TypeScript, the behavior of serializeUser and deserializeUser does not change, but developers often introduce interfaces to define the shape of the user object. This improves type safety and ensures that properties like id and email are consistently available. In many cases, developers also extend Express’s User interface so that req.user is properly typed after deserialization, which helps avoid runtime errors and improves developer experience.

If you're building a full typed auth system, it's useful to understand the Express session setup for Passport as well.

serializeUser with JWT authentication

If you are using JWT-based authentication instead of sessions, serializeUser and deserializeUser are not used at all. This is because JWT operates in a stateless manner, where user information is encoded directly into the token and sent with each request. Since there is no session storage involved, Passport does not need to serialize or deserialize anything, which is why these functions are completely bypassed in JWT setups.

For a clearer comparison, check Passport JWT vs Session authentication.

Common issues with serializeUser

Many beginners encounter issues such as serializeUser not being called or errors indicating that it is not a function. These problems usually arise when session middleware is not properly configured or when passport.session() is missing from the middleware chain. Another frequent source of confusion is mixing JWT and session-based authentication concepts, which leads developers to expect serialization behavior in a stateless setup where it does not apply.

Most of these issues can be avoided by properly understanding the Passport middleware setup order, especially the difference between initialize and session middleware.