Serialize and Deserialize User — passport.js - passport.js

What do the passport.js functions passport.serializeUser and passport.serializeUser do? Is this an example of serialization as it is described on wikipedia: http://en.wikipedia.org/wiki/Serialization

Yep.
These allow the user's data to be saved and retrieved from a session store. This could be memory, redis, or any other database.
If you like reading the source, you will see that passport.session is involved as it will restore login state from a session and the deserializeUser gets the user information from that.
The code that follows after Authenticator.prototype.session defines the serializeUser and deserializeUser methods that let us add our own serializers/deserializers.

Related

Why use cookie-session in addition to passport.js?

My understanding of passport.js so far is that passport.js serializes the user object and sends an ID every time to the client. I am just starting with it so sorry if it's a silly question:
Instead of express-session, I am using cookie-session as I am a beginner. My understanding of cookie-session is that it sends a session ID every time, and this ID can be used to look up the database when needed.
Now, I don't understand why we can't just use the passport.js ID? Why do we need to use cookie-session in addition? Also, (this may be a little unrelated, but) is the difference between session-based authentication and token-based authentication that this ID that's shared is dynamic, or changing every time? Is this still the standard and modern way of doing it in 2020?
"Instead of express-session, I am using cookie-session as I am a beginner."
using cookie session does not make anyone beginner. If you are going to store large data, use express-session, cause it stores the data in the database or redis, keeps the database id of that data, so when it gets a request, fetch the database with that id and compare the request credentials. On the other hand, cookie-session stores the data upto 4kb in the cookie on the user browser and since only user-id is stored in the cookie with passport.js, generally cookie session is used.
passport.serializeUser(
(user, done ) => {
done(null, user.id); // stores the id<4kb
}
);
When client authorizes your app, google send the responds to your callback url.
app.get("/auth/google/callback", passport.authenticate("google"))
passport.authenticate() will call req.login() this is where passport.user gets generated. req.login() will initiate the serializeUser() which determines which data of the user should be stored in the session.
passport:{user:userId}
Now this object will be assigned to req.session. so we will have req.session.passport.user
Everytime when you make a request to a server, browser automatically checks if there is cookie set related to that server and if there is it automatically attaches the cookie to the request. If you were using token based authentication, you had to manually attach the cookie to request everytime you make a request. Cookie is just transportation medium, you store data and move the data, you can even store the token in cookie. Cookie is not just related to authentication. If you have server-side project, you have to set cookie.(this is just a side node).
"My understanding of cookie-session is that it sends a session ID every time, and this ID can be used to look up the database when needed."
so far I explained how session is created. Now what happens when user makes a request?. In app.js file you should have two middleares.
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.initialize()) this function checks if req.session.passport.user exists, if it does it will call passport.session(). if it finds a serialized user object in the session, it will consider this req is authenticated. And then deserializeUser() will be invoked. it will retrieve the user and attach it to req.user
You don't need to use session. It is totally upto you. Just put {session: false} in route. You don't need to write passport.serializeUser and passport.deserializeUser.
cookie-session puts cookie on client system, and it is sent each time with request. passportjs search that cookie and run deserializeUser to convert it into object and attach it with request object.
express-session stores session data on the server; it only saves the session identifier in the cookie, not session data.
where as cookie-session is basically used for lightweight session applications. it allows you to store the session data in a cookie but within the client [browser]. Only use it when session data is relatively small and easily encoded as primitive values See this question for more understanding
const express = require('express');
const { Router } = express;
const router = new Router();
router
.get('/', passport.authenticate('google', { session: false }))

How does passport js stores user object in session?

I am using node/express with passport in my development. I came across an article which says:
Express loads the session data and attaches it to the req. As passport stores the serialised user in the session, the serialised user object can be found at req.session.passport.user.
But to my surprise, the value for sessionID stores in the browser cookies remain the same before and after login. So where does the serialised user object is stored?
I thought that it was stored in the user sessionid cookie initially but it seems that this is not the case as I still can access my user object with req.session.passport.user
So where does the serialised user object is stored?
In Short
The serialized user object is stored in req.user by PassportJS taken from req.session.passport.user (which is is populated by Express) with the help of Passport's deserializeUser method.
Express adds the id of the session object into a cookie on user's browser, which is sent back to express in a header on every request. Express then takes the id from the header and search the session store (i.e. Mongo or whatever) and find the entry and load it to req.session.
PassportJS uses the content of req.session to keep track of the authenticated user with the help of serializeUser and deserializeUser methods (for more information on workflow of serializeUser and deserializeUser see my answer in this SO question).
Express is responsible for creating the session. when does the sessions gets created? That is when Express do not detect a session cookie. So the order in which you organize your session and passport configs in your app or server.js file is very important. If you declare your session and passport configs above static directory configs then all requests for static content will also get a session, which is not good.
See my answer to this SO question, where I have mentioned about static content access as well as how to selectively apply passport to certain routes, rather than default (you might not need to authenticate all the routes - hence you could avoid unnecessary session store lookup and de-serialization by attaching session only to requests that map to secure URLS see below).
//selectively applying passport to only secure urls
app.use(function(req, res, next){
if(req.url.match('/xxxx/secure'))
passport.session()(req, res, next)
else
next(); // do not invoke passport
});
There is one amazing tutorial that I highly recommend you to read up if you want to understand the workflow of PassportJS.
You can look at the sessionID in the cookie as a key to a database where the session data is stored. Depending on what session handler you use with express, and what storage policy you use the data will be stored in different ways. This means that the sessionID can be the same value both before login, after a successful login, and even after a user logs out.
If you use express-session with MemoryStore the data will be saved in the memory of the node process, indexed on the sessionID. Look here for initialization of the store and here for storing of the data.
You could create a store where the data is serialized to the cookie, but none such are listed in the compatible session stores.

Express Passport.js: req.user VERSUS req.session.passport.user

As per this article
http://toon.io/understanding-passportjs-authentication-flow/
it looks as though PassportJS/Express store the logged in user in two places
req.user
and
req.session.passport.user
why both? which one should I use? When I logout with passport, does it destroy both req.user and req.session.passport.user?
You should always, always use req.user in your own code -- this is important because if you use req.session.passport.user, you're essentially pulling user information out of a session cookie (which may be outdated).
It's always best to rely on req.user as opposed to cookie data directly, as depending on your implementation, that information might be out of date.
And to answer your question: if you log a user out, both req.session and req.user will no longer be available.

Locals/ Helpers Provided By Passport

I finally got done replacing an everyauth/ mongoose-auth login system with a passport implementation. I'm using this purely for local username/ password logins, so I'm also utilizing the passport-local module as well.
I know from looking through a few examples, I found Passport auto-assigns a couple req helpers. I've not been able to find a full list of variables it puts there though, a couple I've found are req.isAuthenticated() and the req.user variables.
Is there a full list provided anywhere online? Just interested in my options available in routes/ views. I can keep trolling through examples, but it would be nice if there was a reference somwhere.
For a Connect/Express application,
1.var passport = require('passport');
The following four helper functions are added to http.IncomingMessage.prototype(i.e., the request object's prototype):
login/logIn(user, [options,] done)
logout/logOut()
isAuthenticated() - i.e. whether req.user exists.
isUnauthenticated()
If a user is authenticated successfully, usually a callback function done(null, user) is called. This callback function then calls req.logIn() which in turn calls serializeUser() to store the user id as req._passport.session.user.
The req.logOut() function deletes req._passport.session.user.
2.app.use(passport.initialize());
Get the passport info from current session and store it as req._passport.session(i.e., req.session['passport']).
3.app.use(passport.session());
Check whether req._passport.session.user exists, that is, whether the user id is stored in current session. If yes, call deserializeUser() to get the user object which will be stored as req.user.

Re-validating expired AccessToken (or get new one) when logging into app via Facebook Strategy using Passport

Under a variety of circumstances, Facebook's internal AccessToken (used for various features in the Facebook API and other cases) may become invalid. For example, if a user changes her password between sessions of using an application that relies on Facebook Login. There are a few other cases as well.
What happens, however, is that your application essentially crashes, with an error like this: data={"error":{"message":"Error validating access token: Session does not match current stored session. This may be because the user changed the password since the time the session was created or Facebook has changed the session for security reasons.", "type":"OAuthException", "code":190, "error_subcode":460}}.
Passport's Facebook Strategy uses the AccessToken, and it's available to an app as soon as a user is logged in / authenticated via Passport. The problem, however, is what to do when the above error is encountered. Facebook gives a convoluted re-auth flow as an example in PHP, but the general sense is that you need to re-authorize your app with Facebook.
The question is, even when removing the Facebook app from your Facebook page, and forcing the application relying on Facebook Login to re-authorize itself, it seems that Passport + Facebook Strategy is still picking up the last existing AccessToken from the browser's session storage. (At least that's what I'm seeing with Mozilla/Fx 26). I have re-authorized my app several times, but when debugging and looking at what Passport returns, I always get back the same invalid AccessToken. Seems like if an AccessToken exists in session-storage, Passport picks that up instead of getting a new one from Facebook.
So is there a way - within Passport + Facebook Strategy - to essentially ignore or override any stored AccessToken and always request a new one from Facebook, in the event of this kind of error? Not at all clear how to make that happen. Thanks for the help.
Update: The sample code for invoking the strategy has refreshToken as a parameter; what does this do? Is there a possible solution with this parameter?
passport.use(new FacebookStrategy(
{
...
},
function(accessToken, refreshToken, profile, done)
{
I found the answer to my question, and will leave it up in case anyone else encounters the same issue.
Passport, in its VerifyCallback function, passes the AccessToken to the application. The recommendation (tacit or otherwise) is to of course save this AccessToken as part of the user's persisted record (say, a MongoDB document). It'll be needed when making subsequent calls to the Facebook API, and though it can probably be safely retrieved from the request.user object passed around through the Express middleware, having your own local copy probably makes sense.
Now, when you're done authenticating the user via Passport, you still will pull up your user's persisted record to get other user data. Passport documentation features a generic function called Users.findOrCreate() to serve as a model/prototype of this function for you to implement. And here's where the issue lies: If you find a user (one already exists) via this function, you'll be using that record throughout your app. And if that record also holds an AccessToken, and that AccessToken has changed, then of course you'll be in trouble: Passport/Facebook is passing you a new AccessToken, but you're using an outdated one.
So the simple fix is this: If you've implemented a Users.findOrCreate() function to persist a user and rely on this to retrieve a user's set of data - and that record also stores the AccessToken - then make sure you check the current (old) AccessToken to the one Passport/Facebook is passing you in the VerifyCallback when a user has just been authenticated; if the two are different, update the user's persisted record and use this new Token.

Resources