How to update req.user session object set by passp

2020-02-10 13:18发布

问题:

I'm trying to do this since a lot of days but all my tests fails...

Users on my platform connect them by using passportjs strategies (paypal, facebook, google...).

When the user is connected I write his nickname on the right in the header. The html code of the header is generated from a handlebars template, and when this partial is served by expressjs, I send the req.user session object to the template in order to write nickname and other informations...

By the way, this works great but I have a problem when the user update his nickname from his profile, I can't update session object on the server side and if the user reload the page, the old nickname appears again.

And I don't want to request user informations from DB every time a user load a page, so i want to keep this config :

// -- Passport session setup
passport.serializeUser(function(user, done) { done(null, user); });
passport.deserializeUser(function(obj, done) { done(null, obj); });

My middleware to set locals :

// -- Set accessible datas from the template
res.locals = _.extend(res.locals, {
    user: req.user,
    query: req.url,
    title: app.config.title,
    url: app.config.url
});

My fails :

// Trying to update req.user directly : not persistent
req.user.nickname = User.get('nickname');

// Trying to update passport session : exception error
req.session.passport.user = User.toJSON();

// Trying to replace full session object : not persistent
var session = req.session;
session.passport.user = User.toJSON();
req.session = session;

Any suggestion ?

For moment, only a logout then a login works... It's not really efficient :)

EDIT :

// Application router
var Router = require('./helpers/router.js');

// Create Express Server
var app = express().http().io();

// -- Init app router
var router = new Router(app);

// -- Framework Middleware
app.use(router.middleware); 

#########################
/***
 * APP ROUTER
 **/

// Export router
module.exports = function(app) {

    // Set instance
    var router = this;

    // Returns routes register & middleware methods
    return {

        // -- Register routes
        register: function() {
            requirejs(['routes'], function(routes) {
                _.each(routes, function(name, route) {
                    app.get(route, function(req, res, next) {
                        requirejs(['views/'+name], function(view) {
                            if ( view ) {
                                var _view = new view(_.extend(req.params, {server: {req: req, res: res, next: next}})); 
                                _view.render(name, req, res, next); 
                            }
                            else {
                                next();
                            }
                        }, function (err) {
                            console.log('error' + err)

                        });
                    }); 
                });
            });
        },

        // -- Bind middleware
        middleware: function(req, res, next) {

            // Get the current path
            console.log("Middleware :: "+req.url);

            // Add user informations
            res.locals = _.extend(res.locals, {
                user: req.user,
                query: req.url,
                title: app.config.title,
                url: app.config.url
            });

            // Go next 
            next(); 

        }
    }
}

回答1:

This works and persists for me:

req.session.passport.user.nickname = 'mynewnickname'

Are you employing caching on your routes that might need busting before refresh?

(passport serialize is the same)

passport.serializeUser(function(user, done) { done(null, user); });
passport.deserializeUser(function(obj, done) { done(null, obj); });


回答2:

I got the same problem and finally I found the solution:

var user = newDataObj;
req.login(user, function(error) {
    if (!error) {
       console.log('succcessfully updated user');
    }
});
res.end(); // important to update session

The req.login will call serialize function and save a new session to db. res.end() is important. without it the session will not be saved.



回答3:

For 'serializeUser' you are returning the entire user... which will serialize the entire object and put it inside the cookie used to track sessions. That serialization only happens one time (when the session is established). When the next request comes in, it always deserializes that exact same user object you stored originally, which doesn't have any updates you made to it. That's why logging out and logging back in works, because it retrieves the edited user from your database (I assume), and recreates the session tracking cookie.

To confirm this answer, put a breakpoint on 'serializeUser', and notice that it is only hit when you login.



回答4:

This is still one of the top results for 'update session passport js', so I thought I'd add what worked for me (the provided answers didn't work for me):

req.session.passport.user.updatedfield= 'updatedvalue'
req.session.save(function(err) {console.log(err);}

Without req.session.save() the session data would not update for me. Hope this helps someone.



回答5:

Quan Duong's answer was the most useful to me, but here is a bit more realistic scenario:

async function foo(req, res) {
  // get some data from somewhere
  const newNickname = req.body.nickname;

  // update the user database somehow - e.g. mongodb 
  const users = db.getCollection('users');
  await users.updateOne({
    _id: req.user._id
  }, {
    $set: {
      nickname: newNickname
    }
  });

  // create a temporary copy of the user w/ the updated property
  const updatedUser = req.user;
  updatedUser.nickname = newNickname

  // persist the change to the session
  req.login(updatedUser, async(error) => {
    if (error) {
      res.json({
        err: 'Sorry, there was an error updating your account.'
      });
      return;
    }

    /* 
       maybe you need to call another function from here that uses the updated info before 
       responding to the original request
    */
    try {
      await bar(req.user);
    } catch (error) {
      res.json({
        err: 'Sorry, there was an error updating your account.'
      });
      return;
    }

    // done
    res.send(200);
  });
}