Why is PassportJS in Node not removing session on

2020-01-26 03:35发布

I am having trouble getting my system to log out with PassportJS. It seems the logout route is being called, but its not removing the session. I want it to return 401, if the user is not logged in in specific route. I call authenticateUser to check if user is logged in.

Thanks a lot!

/******* This in index.js *********/
// setup passport for username & passport authentication
adminToolsSetup.setup(passport);

// admin tool login/logout logic
app.post("/adminTool/login",
    passport.authenticate('local', {
        successRedirect: '/adminTool/index.html',
        failureRedirect: '/',
        failureFlash: false })
);
app.get('/adminTool/logout', adminToolsSetup.authenticateUser, function(req, res){
    console.log("logging out");
    console.log(res.user);
    req.logout();
    res.redirect('/');
});


// ******* This is in adminToolSetup ********
// Setting up user authentication to be using user name and passport as authentication method,
// this function will fetch the user information from the user name, and compare the password     for authentication
exports.setup = function(passport) {
    setupLocalStrategy(passport);
    setupSerialization(passport);
}

function setupLocalStrategy(passport) {
    passport.use(new LocalStrategy(
        function(username, password, done) {
            console.log('validating user login');
            dao.retrieveAdminbyName(username, function(err, user) {
                if (err) { return done(err); }
                if (!user) {
                    return done(null, false, { message: 'Incorrect username.' });
                }
                // has password then compare password
                var hashedPassword = crypto.createHash('md5').update(password).digest("hex");
                if (user.adminPassword != hashedPassword) {
                    console.log('incorrect password');
                    return done(null, false, { message: 'Incorrect password.' });
                }
                console.log('user validated');
                return done(null, user);
            });
        }
    ));
}

function setupSerialization(passport) {
    // serialization
    passport.serializeUser(function(user, done) {
        console.log("serialize user");
        done(null, user.adminId);
    });

    // de-serialization
    passport.deserializeUser(function(id, done) {
        dao.retrieveUserById(id, function(err, user) {
            console.log("de-serialize user");
            done(err, user);
        });
    });
}

// authenticating the user as needed
exports.authenticateUser = function(req, res, next) {
    console.log(req.user);
    if (!req.user) {
        return res.send("401 unauthorized", 401);
    }
    next();
}

20条回答
虎瘦雄心在
2楼-- · 2020-01-26 04:10

Ran into the same issue. Using req.session.destroy(); instead of req.logout(); works, but I don't know if this is the best practice.

查看更多
beautiful°
3楼-- · 2020-01-26 04:10

Apparently there are multiple possible causes of this issue. In my case the problem was wrong order of declarations i.e. the logout endpoint was declared before passport initialization. The right order is:

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


app.get('/logout', function(req, res) {
  req.logout();
  res.redirect('/');
});
查看更多
干净又极端
4楼-- · 2020-01-26 04:11

I used both req.logout() and req.session.destroy() and works fine.

server.get('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

Just to mention, i use Redis as session store.

查看更多
▲ chillily
5楼-- · 2020-01-26 04:13

In my case, using a callback passed to req.session.destroy helped only some of the time and I had to resort to this hack:

req.session.destroy();
setTimeout(function() {
    res.redirect "/";
}, 2000);

I don't know why that's the only solution that I've been able to get to work, but unfortunately @JulianLloyd's answer did not work for me consistently.

It may have something to do with the fact that my live login page uses SSL (I haven't been able to reproduce the issue on the staging site or my localhost). There may be something else going on in my app too; I'm using the derby-passport module since my app is using the Derby framework, so it's difficult to isolate the problem.

It's clearly a timing issue because I first tried a timeout of 100 ms, which wasn't sufficient.

Unfortunately I haven't yet found a better solution.

查看更多
Emotional °昔
6楼-- · 2020-01-26 04:15

I don't know how but ng-href="/signout" solved my problem. Previously I have used service to logout, but instead I've used it directly.

查看更多
做个烂人
7楼-- · 2020-01-26 04:19

session.destroy may be insufficient, to make sure the user is fully logged out you have to clear session cookie as well.

The issue here is that if your application is also used as an API for a single page app (not recommended but quite common) then there can be some request(s) being processed by express that started before logout and end after logout. If this were the case then this longer running request will restore the session in redis after it was deleted. And because the browser still has the same cookie the next time you open the page you will be successfully logged in.

req.session.destroy(function() {
    res.clearCookie('connect.sid');
    res.redirect('/');
});

That's the what maybe happening otherwise:

  1. Req 1 (any request) is received
  2. Req 1 loads session from redis to memory
  3. Logout req received
  4. Logout req loads session
  5. Logout req destroys session
  6. Logout req sends redirect to the browser (cookie is not removed)
  7. Req 1 completes processing
  8. Req 1 saves the session from memory to redis
  9. User opens the page without login dialog because both the cookie and the session are in place

Ideally you need to use token authentication for api calls and only use sessions in web app that only loads pages, but even if your web app is only used to obtain api tokens this race condition is still possible.

查看更多
登录 后发表回答