Passport update session isn't persisting - node.js

I'm trying to update my user session with a new name, an easy task I thought.
I'm a logged in user and in hitting a 'update' route and I've defined my own middleware to update the session:
module.exports = function(req, res, next) {
console.log(req.user);
req.login(req.body.user, function(err) {
if (err) return next(new Error('Error updating user profile'));
console.log('USER UPDATED *******', req.user);
next();
});
};
It took a bit of time to dig out the above code which should simply update the Passport session object. It correctly logs the previous session, and then the updated session but when I navigate to a new page after the inital response the user object is entirely lost and just returns {}.
Any ideas?
source

To log in a user and persist it into session passport uses a serialize function which typically stores user.id as a cookie and a deserialize function which retrieves that cookie and does a User.findById database call to find the associated user, and if found one, that's the user object that gets stored in req.user.
req.login passes whatever you pass it as the first argument directly to passport.serialize, which otherwise would've typically come from a strategy, which itself would've retrieved the user object from a database call, or created one.
So when you use req.login you need to pass it the user object that passport.serialize actually would've received, so that it could store the id in a cookie.
In your case, you were doing req.login(req.body.user, ... and since req.body.user comes from a POST Form variable it must not have the id that passport.serialize would've stored in the cookie.
You should instead use the new values from req.body.user and update req.user itself, and then do req.login(req.user, ...
var _ = require('lodash');
module.exports = function(req, res, next) {
//using lodash merge the updated user into the cached user
_.merge(req.user, req.body.user);
req.login(req.user, function(err) {
if (err) return next(new Error('Error updating user profile'));
console.log('USER UPDATED *******', req.user);
next();
});
};

Related

check if user logged in by passport Strategy (different user types)

I have two user collections in my db and I want to make different login types for every one, so I have made two strategy on passport for my site ('local-user' & 'local-manager').
My question is how to check logged in user type (by used strategy) in my app?
In this code, passport just checks user auth but I want to check by strategy. (eg: if user logged in by 'local-manager', then show the page)
function isLoggedIn(req, res, next){
if (req.isAuthenticated()) {
next();
return;
}
res.redirect('/login');
}
It's better you use role mapping for this.
Anyway for now you can use this concept :
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
{passReqToCallback: true},
function(req, username, password, done) {
req.usedStrategy = 'local-user';
//do auth stuff
});
}
));
And use like this :
function isLoggedIn(req, res, next){
if (req.isAuthenticated() && req.usedStrategy === 'local-user') {
next();
return;
}
res.redirect('/login');
}
Also you can use session if you enable it in passport.
It must be said (and has been in other answers/comments) that you should really look again at your modelling of the domain. User objects can be really simple (just login information) and the rest can be broken out into other models/schemas.
Anyway on to the answer to your original question:
You can switch on the user type. Passport doesn't reach too far into the rest of your application. The log in strategies are not known about outside of the actual log in section.
You can handle that as middleware and add some extra information to the request object by checking for a unique property in one of the models:
function(request, response, next){
request.isManager = !!(request.user && request.user['unique_prop']);
next();
}
Place this after the auth middleware. Then in your route you can switch based on request.isManager. Also encapsulating this in middleware will abstract it from the user model and allow you to refactor it in the background.
Another alternative would be to add the function as a static/method/virtual (depending on the implementation) to the schema if you're using mongoose.
Hope this helps 👍 If you have further questions feel free to add comments and I can amend the answer. 🤔
I know this question is old, but I just had the same problem, and I managed to find a way to at least know what kind of account the user is. As someone else said, I don't think there is a way to see what strategy a user used to log in, but if you have a field in your user database to show what kind of account they are (e.g.: admin, client, etc.), then you can pass this information along in the req.user variable when you serialize the user:
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, {
id: user.user_id,
username: user.user_email,
type: user.user_type
});
});
});
passport.deserializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user);
});
});
In the example above, I'm using an SQL database, and one of the columns is called user_type. Once the user is deserialized, passport passes on the fields you ask for in passport.serialize() to req.user. So, if you wanted to know what type of account is currently logged in, you could say something like:
console.log("This user is a(n) " + req.user.type);
Or, something more realistic, if you're using Express:
app.get("/adminsOnly", (req, res) {
if (req.isAuthenticated() { // returns true if a user successfully logged in
if (req.user.type === "admin") { //check if user is an admin
res.render("adminsOnly.ejs"); //render admin section of website if true
} else {
res.redirect("/adminsLogin"); //redirected somewhere else because user is not an admin
}
} else {
res.redirect("/login"); //req.isAuthenticated() returns false, so user is redirected to the login page
}
});
I'm pretty new to coding in general, so I'm sure there are better ways to tackle this question, but this is a way to work around the problem of not being able to pinpoint which strategy the user logged in with.

Refresh passport.js session data using req.login()

I'm using passport.js. The problem is when I change user information such as user name, It's not changed until user re-login. If user logout and login again, the information changed. but I want to change in the session without re-logging in.
My deserializeUser function like below, It looks different original passport's example because it is pickup data from session instead of querying database. I want to up loading speed so I didn't use database query on deserialize function.
passport.deserializeUser(function(userSession, done){
var member = { id : userSession[0], name : userSession[1] };
done(null, member);
});
So I think I have to use req.login() like below, but the session wouldn't changed.
Members.findOne({_id : someId }, function(err, member){
member[username] = req.body.username;
member.save(function (err){
if(err) return next();
console.log('The original session :'+req.sessionId);
req.login(req.user, {}, function(err){
if (err) return next();
console.log('The new session :'+req.sessionId);
res.send(200);
});
})
})
The problem is original session and new session is same, How can I force to refresh req.user object?
I red this document but didn't get solution.
https://github.com/jaredhanson/passport/issues/208

Passport session not updating

I'm trying to update the passport session after user info change using:
user.save(function(err) {
if (err) return next(err)
// What's happening in passport's session? Check a specific field...
console.log("Before relogin: "+req.session.passport.user.changedField)
req.login(user, function(err) {
if (err) return next(err)
console.log("After relogin: "+req.session.passport.user.changedField)
res.send(200)
})
})
But even after re-login, with no errors my passport session remains the same. I have to manually logout and log back in for the session info to update. Can anyone explain what's happening?
Passport sets req.user using the function you have provided with passport.deserializeUser. You are using req.session.passport.user which is incorrect. There is no need to re-login after change of user information - deserializeUser is called on every request, and this is where you fetch the user information from your database. Then you access it via req.user.

req.session has no method 'touch'?

I'm trying to use express-session with connect-redis to store user sessions. Currently, after a user logs in, I return back req.sessionID and then whenever the user wants to make a request to a secure part of the api, he/she has to provide the session ID. The authentication middleware then goes back into redis and checks to see if the session exists and if so, replace the current session with the one stored in Redis.
function isLoggedIn(req, res, next){
var session_id = req.body.session_id;
if (!session_id){
res.send(401, {status: 0, message: "Not authorized"});
return;
}
console.log(req);
sessionStore.get(session_id, function(err, session){
if(err){
res.send(401, {status: 0, message: "Not authorized"});
return;
}
req.session = session;
req.sessionID = req.body.session_id;
return next();
});
}
But for some reason, this error shows up:
/node_modules/express-session/index.js:269
req.session.touch();
^
TypeError: Object #<Object> has no method 'touch'
I can't seem to find anyone else online that has this error because touch() is a built in functionality to express-session. Please help? My express-session version is 1.9.3.
I was having the same error. It seems that if you're coming from express cookie sessions, it was possible to set req.session = {/* some arbitrary session object */}. Obviously, req.session has some methods on the instance that express needs.
So, just make sure you're not explicitly overriding req.session anywhere in your code.
Try this:
req.session.user = { 'id': 123 };
req.session.pageviews = 1; // This too
Font: https://davidburgos.blog/expressjs-session-error-req-session-touch-not-function/

express.js not saving session data on nodeunit tests

Update at bottom!
My node.js server uses express.js to manage sessions. Upon login, I store some user information in req.session. I have a logout endpoint that simply deletes the user data from req.session before sending its response.
With every request the user makes, I use authentication middleware to make sure there is still user data in the session, so deleting user data in the session object should fail any subsequent authentication.
To test my server, I have a nodeunit test that logs in, calls a few endpoints, logs out, and then attempts to call another endpoint. I would expect the last endpoint to fail in authentication because I previously blew away user data. Instead, when I make the last call, my user data is still there. It's as if the logout call that deleted it was not written back into the session store.
Here's my app.js:
app.use(express.cookieParser('secretPassword'));
app.use(express.cookieSession({key: 'someKey'}));
...
app.get('/logout', accounts.logout);
app.get('/account', auth.authenticateSession, accounts.show);
auth.js:
exports.authenticateSession = function(req, res, next) {
if (!req.session.user) {
return res.json(401, {
error: 'Access denied. You must be logged in to make this request.'
});
}
...
}
accounts.js:logout:
exports.logout = function(req, res) {
req.session.user = null;
res.send('Logged out');
};
Unit tests:
step1_logIn : function(test) {
var postData = qs.stringify({
accountname: 'testAcct',
accountpassword: 'hello'
});
ct.postAndCall('/login', null, postData, function(resData, res) {
myCookie = res.headers['set-cookie'];
test.ok(res.statusCode === 200);
test.done();
});
},
step2_logout : function(test) {
ct.getAndCall('/logout', myCookie, function(data, res) {
test.ok(data === 'Logged out.');
test.ok(res.statusCode === 200);
test.done();
});
},
step3_ensureLoggedOut: function(test) {
ct.getAndCall('/account', myCookie, function(data, res) {
test.ok(res.statusCode === 401);
test.done();
});
}
When the tests run, execution goes through logout successfully, then into authenticateSession on the call to /account and at this point, req.session.user still exists! Why!?
Is my cookie store giving me stale data?
Is there a way to force express to save my session data manually, or do I just modify the req.session object and trust that it's going to save it?
Update:
It looks like this problem is directly related to the app middleware surrounding cookies. When I use app.use(express.session()) instead of app.use(express.cookieSession(...)), the session data is properly blown away and my tests pass.
I figured it out. Apparently express.cookieSession(...) is meant to be a set-once type of storage. Subsequent requests will have access to the session data that was initially set, but changing the req.session object won't save back new session data.
To fix the problem, I switched over to use express.session(...) which uses a server-side store for session vars.

Resources