PassportJS deserializeUser never called

2020-02-02 14:53发布

问题:

I've got Passport setup to authenticate users stored in mongodb. Seems to work fine: authentication succeeds/fails appropriately and session variables get set. However, getting Passport to check for a session is failing. Something seems to be quite wrong in that the console.log statements I've added to the deserializeUser callback never see the light of day. I assume my problem is related to deserializeUser never being called. Anyone able to diagnose my misstep?

// Passport configuration
passport.serializeUser(function(user, cb){ cb(null, user.id) });
passport.deserializeUser(function(uid, cb){
  console.log("Trying to deserialize user: "+uid);
  User.findById(uid, function(err, user){
    cb(err, user);
  });
});
// auth strategy function
passport.use(new LocalStrategy({usernameField: 'email'},
  function(email, pass, done){
    User.findOne({email: email}, function (err, user) {
      if (err)
        return done(err);
      if (!user)
        return done(null, false, {message: "Couldn't find user"});
      var crypted = bcrypt.hashSync(pass, user.salt);
      if(user.hashpass != crypted)
        return done(null, false, {message: "Bad password"});
      return done(null, user);
    });
  }
));

passport.CreateSession =  function (req, res, next) {
  passport.authenticate('local', function(err, user, info){
    if(err || !user)
      return res.json({status: "Failure: "+err});
    req.logIn(user, function (err){
      if(err)
        return res.json({status: "Failure: "+err});
      return res.json({status: "Authenticated"});
    });
  })(req, res, next);
};

with the following in app.js:

app.post('/session', passport.CreateSession); // restify better
app.del('/session', passport.DestroySession);
app.get('/images', passport.CheckSession, routes.images);

回答1:

For anyone else who is having this issue, take a look at this:

app.use(session({ 
    secret: 'something', 
    cookie: { 
        secure: true
    }}));

If you have cookie.secure set to true and you're NOT using SSL (i.e. https protocol) then the cookie with the session id is not returned to the browser and everything fails silently. Removing this flag resolved the problem for me - it took hours to realise this!



回答2:

If you are using the authenticate callback when you authenticate with passport you need to log the user in manually. It will not be called for you.

passport.authenticate('local', function (err, user) {
    req.logIn(user, function (err) { // <-- Log user in
       return res.redirect('/'); 
    });
})(req, res);


回答3:

Have you use()'d passport.session() middleware? Like in this example:

https://github.com/jaredhanson/passport-local/blob/v1.0.0/examples/login/app.js#L91

That's what restores the session and calls deserializeUser, so it sounds like that may be missing.



回答4:

Okay, this took me almost 3 days to solve, which seems to be a simple fix, at least in my case. I had to place the app.use(require('cookie-parser')); and app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false })); BEFORE calling the:

app.use(passport.initialize()); app.use(passport.session());

Note: These snippets are not my actual code, I have taken it from : https://github.com/passport/express-4.x-local-example > server.js file; but that is the gist of it.



回答5:

I fought the same problem the entire day. I had a custom callback set up to do authorizations (like at the bottom of http://passportjs.org/guide/authenticate/). It looks like Passport preprocesses all request objects before they even hit the other middleware (based on some logging statements I wrote). As part of that preprocessing, it places the deserialized user information into the request.user object. Unfortunately, the default behavior of passport.authenticate ('local', callback) seems to be to ignore any session or cookie data, and instead assume that the username and PW are being sent in the request object. What I did to fix this was to first check the request.user object, and if it existed, it meant that Passport had done its preprocessing and then I could call login directly. Here is my authorization function, which I use in place of passport.authenticate('local'):

function doAuth(request, response, next)
{ 

if (request.user) {
    request.logIn(request.user, function (err) {
        if (err) {
            console.log(err);
            response.status(500).json({message:
                    "Login failed. Auth worked. Nonsense"});
            return;
        }
        console.log("doAuth: Everything worked.");
        next();
    });
    return;
}

passport.authenticate('local', function(err, user, info) {
    if (err) {
        response.status(500).json({message: "Boo Passport!"});
        return; //go no further
        //until I figure out what errors can get thrown
    }
    if (user === false) {
        console.log("Authentication failed. Here is my error:");
        console.log(err);
        console.log("Here is my info:");
        console.log(info);
        response.status(500).json({message: "Authentication failed."});
        return;
    }
    request.logIn(user, function(err) {
        if (err) {
        console.log("Login failed. This is the error message:");
        console.log(err);
        response.status(500).json({message:"Login failed."});
            return;
        }
        console.log("doAuth: Everything worked. I don't believe it.");

        next();
    });
})(request, response, next);

 }


回答6:

In my case it was because I had this code:

app.get("/auth/google/callback", 
    passport.authenticate("google", { 
        failureRedirect: "/admin/login/?error",
    }),
    (req, res) => {
        return res.redirect("/admin/");
    },
);

Apparently in this case req.login() will not be called (which calls deserialize). I changed it to this and it worked:

app.get("/auth/google/callback", 
    passport.authenticate("google", { 
        successRedirect: "/admin/", 
        failureRedirect: "/admin/login/?error",
    })
);


回答7:

Today I faced the same issue, and in my case the cause was how the client side makes the API calls. And the following approach helped me to overcome the problem:

const doFetch(url, method = 'GET', body) {
  const headers = new Headers();
  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');
  headers.append('Cache', 'no-cache');
  const params = { 
    method, headers, 
    credentials: 'include' // The cookie must be sent!
  }
  if(body) {
    params.body = body;
  }
  return fetch(url, params);
}

After this, deserializeUser began to be called properly.



回答8:

in my case, I've placed

app.use(cookieSession({
  maxAge : 60*60*1000,
  keys : [key.cookieSession.key1]
}));

above

app.use(passport.initialize());
app.use(passport.session());

and it worked.



回答9:

make sure the line of code app.use(express.static(path.join(__dirname, 'public'))); stays below the app.use(require('express-session')



回答10:

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

passport.deserializeUser(
    function(id, done){
        User.findById(id, function(err, user){
            if(err){
                done(err);
            }
            done(null, user);
        });
});

**> try this one**