PassportJS is awesome. It provides an abstraction layer over logging in with various providers such as Facebook, Google, Github, Twitter and more. When first getting started though it can be a little challenging to understand what’s going on and why. Their documentation is pretty good but leaves out some specifics that I initially found difficult to understand. Hopefully this helps answer some of your questions and clears up some confusion. I assume you’ve read the docs a little and have maybe tried implementing it. Even if you haven’t though, you should still be able to follow along. Even if you have, this should still prove useful in understanding PassportJS a little better. Here’s what’s covered The Callback Function in Strategy Setup Why is needed in the callback passport.authenticate() Checking if the User is Logged in Understanding and serializeUser() deserializeUser() Alright, lets go! Issue 1: Understanding the Callback in Strategy Setup Take a look at the code below. We require our modules, set up our google strategy and create two routes, one for handling logging in and another for the . callbackURL When a user visits , we authenticate with google and in this case, request their and information. /login/google email profile One key part though is, as part of , the function on line 9 is called before the is on line 15. passport.authenticate() console.log() This function is the first time you have access to the profile information. Typically this is where you would add the user to your DB if it doesn’t already contain it (See code example below). But how do you exit this function to get back to your code, specifically to the route handler for the specified on line 19. callbackURL To do this you call . done() What is ? An internal PassportJS function. You pass it two arguments, null and the profile info like this: done() This takes the profile info and attaches it on the request object so its available on your callback url as . This will be available for the route as defined on line 41 and this is where you would set the session for the user and then send them on their way to another part of your site. req.user /login/google/return Issue 2: Why the passport.authenticate() in the callbackURL? If a user makes it back to , why do we need another ? This is partly to make sure the user didn’t just go to the route directly and verifies that they are in fact logged in. This is handled internally by PassportJS. /login/google/return passport.authenticate() As a side note, you should not use as a method to check if a user is logged in. This should only be used to actually log a user in which is needed in this case since they might not yet be logged in if they just typed in the url and route and hit enter. passport.authenticate() Issue 3: Check if a User is Logged In On line 36 in the code block above we set to equal the profile of the person who just logged in. To check if someone is logged in we can just see if this value has been set and then use this function as middleware on any route we want where the user has to be logged in. Here is an example of the middleware function. req.session.user Then if you want to ensure a user is logged in for a route, just do this This will call . If is set then the request continues on due to the call. Otherwise the user is redirected to . isLoggedIn req.session.user next() /login Issue 4: What is this Serialization and Deserialization Business? From above we said that passport attaches the profile information to . This occurs as a direct result of the and functions. req.user serializeUser() deserializeUser() When you write in the callback function specified when setting up the strategy (look at the second code block all the way at the top where we do or ), we pass in the entire user profile object (in this example as either or as ). done() done(null, dbUserRecord done(null, newUser) dbUserRecord newUser The reason for this is because it is received by . This function then calls . Passport takes that user id and stores it internally on which is passport’s internal mechanism to keep track of things. serializeUser() done(null, user.id) req.session.passport Then in , the first argument is an which is the same that was passed in of . then makes a request to our DB to find the full profile information for the user and then calls . This is where the user profile is attached to the request handler at . Then finally after all this occurs, the user is routed back to the route handler where we can finally access the user profile information on . deserializeUser() id id done(null, user.id) serializeUser() deserializeUser() done(null, user) req.user /login/google/return req.user To understand this user flow a little better take a look at the diagram on this StackOverflow answer: . https://stackoverflow.com/questions/27637609/understanding-passport-serialize-deserialize?answertab=votes#tab-top Wrapping Up You might ask yourself now, why is all this necessary. Handling user info in this manner means PassportJS only has to store the user id and not the entire user profile. This decreases the likelihood of confusing which is something we set and which is PassportJS’s way of keeping track of what’s going on. req.session.user req.session.passport.user This can be seen by logging which will return something like this req.session Notice how is the same as . They have to be so we can perform the database lookup with the right information. Further only stores the id and nothing else like or which we see in the object. passport.user user._id passport.user name googleID user Conclusion PassportJS makes it really easy to get setup but understanding its data flow can be confusing. I hope this helps explain some of the underlying principles it uses. If you have any questions please leave them in the comments. Thanks for reading!