Passportjs extract user from isAuthenticated function - node.js

Alrigh, so I have set up an entire passport local authorization, every thing works pretty perfectly, except I can't seem to wrap my head around one issue with passport.js.
I am authorizing users through sessions (they work fine), however I can't seem to extract the user info from that session, although if I manually check my session db, for every session there is a userid (which links to the user).
Also the deserialize functions seem to return the user and the log is showing the correct user.
Now the problem is that I can't seem to pass this user<->session info anywhere in my routes.
The issue that I seem to have with this is that people that are logged in, are authorized, but they can fiddle with the request body as much as they like, doing things as posting messages in their name, logging them out, ...
Code example:
router.post('/games/:id/verify', isAuthenticated, function(req, res){
log('#POST verify for game with id: ' + req.params.id);
gameController.postVerify(req.params.id, req, res);
});
In this code, it just checks if a user is logged in, if possible, I want to check which user is logged in to that session, so that he cannot verify this game if he was not part of it.
Let's say user A, B were part of the game and need to verify it. This function allows user C who is logged in to verify for them, because this function only checks if the user is logged in.
If I can pass the logged in user (user C in the test case) to the gameController, I can write my own checks in there and throw them out.

Passport will populate req.user when authentication has succeeded. It will be populated by the data provided by deserializeUser, which generally is a database record of the user.
You can use that information to limit other database queries/checks/etc to make sure that the logged-in user is allowed to access particular information.

Related

Vuejs/Node/Express Session ID workflow with OpenID

I am having a tough time understanding how I'm supposed to implement sessions.
Currently I'm writing a Vuejs app and, somehow, have managed to evade implementing any kind of Oath2 "sign in with x" process, or at least have copy and pasted my way to success without understanding, I'm afraid.
Here's what I'm trying to do, and it actually works 90% of the way there.
I've implemented passport-steam and I can click through a Sign in to Steam button, login with my account, and get kicked back to my homepage. A sessionID cookie is set. This is how I have it all configured:
app.use(session({
secret: 'topSecretKey!',
name: 'sessionID',
resave: true,
saveUninitialized: true
}))
passport.use(new SteamStrategy({
returnURL: `${host}/auth/steam/return`,
realm: host,
profile: true, // enable profile exchange
apiKey: ApiSettings.apiKey
}, (identifier, profile, done) => {
process.nextTick(() => {
// ...
// fetch the user profile in the background, matching
// the steamID to the account
profile.identifier = identifier
return done(null, profile)
})
}
))
app.get('/auth/steam/return', passport.authenticate('steam', {
failureRedirect: '/'
}), (req, res) => {
console.log('sessionID=' + req.sessionID)
console.log(`User logged in w/ Steam ID #${steamID}`)
res.redirect('/')
})
I run all of this, I sign in, and check my Node console and all is well!
Found existing account.
sessionID=2YL_YdMrauNyZ-F0gnIv3XV_5sNFo4C9
User logged in w/ Steam ID #xxxxxx
But here begins my questions. sessionID is stored in the request object, this is clear.
Is the sessionID supposed to be used as a token when querying an OpenID-enabled API? (Steam API doesn't require anything other than the Steam ID which is returned plain as day.
Is the sessionID supposed to ever make it to the frontend? Currently I static-serve a /dist directory (again, this is Vue) so Node doesn't actually do much except handle API calls out to the SteamAPI. Of course, this doesn't actually require a sessionID... just an APIkey and the users SteamID which I store in their user profile on the Mongo side.
What is the pattern here? Am I supposed to put the users' sessionID in their user record in Mongo and ... pass it around? Seems strange to me to do it that way.
What's the procedure to check if a user is logged in? Check the cookie sessionID and make a request to return that user object to the frontend (using Vuex layer for quick retrieval).
An extension of #2, but in theory, is the sessionID ever supposed to make it to the frontend? The only data manipulation is happening in my Vuex stores - for example, adding a game for a user. How is the sessionID supposed to be used?
Thanks in advance - I know these questions are all over the place but I've been pulling my hair out!
UPDATE August 2nd:
Thanks to anyone who read - I had some developments last night that were definitely very helpful in understand how this workflow is supposed to go. Couple things I learned:
The sessionID is not supposed to be referenced on the frontend - it is passed back and fourth automatically between frontend and back to Node via the request
Rather than relying on a cookie (which keeps resetting the sessionID every time Node restarts) I set up a Mongo store using connect-mongo. This way my sessionID never actually changes until it naturally expires.
As for #4, checking if a user is logged in, I am just setting a cookie called loggedIn. I honestly can't think of a better way to do this, as the sessionID cookie is HTTP only, and thus, unavailable to any frontend Javascript/Vue in this case.
I've come to the understanding that the sessionID is to be used in the backend only, but I still am wondering if I'm supposed to connect it to the user some way. This link was super helpful but I still feel as if my workflow is not perfect. When I do passport.serializeUser, what am I ideally supposed to pass to done() ? I am passing done(null, user.steamID) as the SteamAPI only requires the user SteamID to request their game list. Is this OK to do, or should I be passing the sessionID? Still confused!
UPDATE AGAIN
Great source of information here, regarding the security and the why behind all of the little details.

How to handle user loggin identifier change with Node/Express/Passport?

In my application, users sign in use their email as the unique identifier. But I am asked to implement a function which lets user change their email in the profile page after they log in. The issue is even if I can make the database successfully update, the client session stores the old information. Then if I try to do (e.g. GET) something, the req.user.email will not be in the database. How to handle such issue? Or in another word, how to update the express session correctly?
Once you sure the user data can be put in the session, you can just call req.logIn again (this function has been called by passport in the login process), and it will call the process correctly (e.g. calling the serializeUser function, etc)
req.logIn(newUser, function (err) {
if (err) {
// Ops! Something went wrong!!
}
});
Hope it can help!

How to create sessions in a strong way and store them in cookies in node js?

In my login module once I log in I pass my request to a header to store a session here is the code
var series = rand.generate(16);
var token = rand.generate(16);
var loginToken = new LoginTokens({
userId: req.user._id,
series: series,
token: token
});
loginToken.save(function(err, l) {
if (err) {
console.log(err);
} else {
console.log(l);
res.cookie('loginToken', JSON.stringify({
series: series,
token: passwordHash.generate(token)
}));
res.status(200);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': req.params.res
}));
}
});
Though this code was pre-written and I don't understand it much(I don't get the point of randomly generating 16 digit number and saving it if somebody does please explain)
I have been tasked with implementing log out and As I don't understand the rationale behind above code I want to implement my own session module such that even if the same user logs in from a different device, both sessions will be maintained and logging out from one device will not log out someone from all device.
So now the point comes that the session id will not be based on user id. Also there is a module called express-session which I don't know if I should use or not, personally I feel that after reading it up on GitHub that what it will do is create a session id for me, which again I will have to save in my database, so why to import a module, instead I will base64 encode a random number like above and use it.
So what I want to ask is the real question
How do you implement sessions in node js?
My understanding of life cycle
A user signs up you see if he/she exists in the database and if he does you raise an error message and if he/she doesn't you create a username for him, probably by email id.
You then via code log him in. In that process, you first check if he is there in the database. If he is you go ahead take the _id field and try and create a session object. Ques what does your session table should look like? What all parameters should be present in sessions table? Then how do save them into res.cookies? Then what do you do with all those cookies, like on client side?
While implementing logout. My thinking is using passport.logout will remove the user object from request header but, I will have to first remove the entry from sessions table somehow empty the cookies and then do req.logout?
Can somebody please answer the above doubts?
I asked a very specific question but realised after the comment that what I need is logical clarity and I did not find one on youtube videos nor on SO posts, so if someone can share their views mostly in code, with some explanation it would be great.
Cookies get deleted by sending a new cookie with the same name (some invalid data would be good) and an expiry in the past.
passport.logout() removes the session cookie that passport uses to maintain the current user, the next request will no longer have the cookie and will appear as an invalid request.
I'm not sure why there's a loginToken as well as passport authentication, apart from allowing you to invalidate sessions server side (by deleting the appropriate token from the database) Check if your passport strategy configuration has any checks against LoginToken during it's deserialize user step (this is where passport takes the session cookie and turns it into a user against the request)

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.

mediator.user is lost after route is changed manually

I am building a single-page-application with a passport local authentication.
The user is authenticated and returned within app.post "/login"
After the userobject is returned I save it in Chaplin.mediator.user (like seen in the facebook-example). As long as I don't change the URL manually everything works fine, but when I change the URL and hit enter, the application gets loaded again however with a different route --> no user in the front end.
What I am doing right now is everytime I change the route manually I send a request to the server and ask for the user in req.user. After receiving the user the application continues its workflow. It works but I don't think this is how it's meant to be. If you don't wait for the response you end up having no user object although you are logged in.
How can I solve this problem?
EDIT: I ended up saving the user in a seperate cookie.
This is how I save the user:
$.cookie.json = true;
$.cookie 'user', user.toJSON()
And this is how I extract the user after the page was loaded:
userCookie = $.cookie 'user'
if userCookie?
mediator.user = new Model JSON.parse userCookie
You need the jquery-cookie plugin to make it work. Don't forget to delete the cookie if the user logs out. And of course I am still open to other ideas.
You should store the user (aka the session) locally. When the Backbone application loads, you should then route the user to the correct place if they in fact are already logged in.

Resources