Why is PassportJS in Node not removing session on logout

天大地大妈咪最大 提交于 2019-11-26 10:24:41

问题


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();
}

回答1:


Brice’s answer is great, but I still noticed an important distinction to make; the Passport guide suggests using .logout() (also aliased as .logOut()) as such:

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/'); //Can fire before session is destroyed?
});

But as mentioned above, this is unreliable. I found it behaved as expected when implementing Brice’s suggestion like this:

app.get('/logout', function (req, res){
  req.session.destroy(function (err) {
    res.redirect('/'); //Inside a callback… bulletproof!
  });
});

Hope this helps!




回答2:


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.




回答3:


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.




回答4:


I was having the same issue, and it turned out to not be a problem with Passport functions at all, but rather in the way I was calling my /logout route. I used fetch to call the route:

(Bad)

fetch('/auth/logout')
  .then([other stuff]);

Turns out doing that doesn't send cookies so the session isn't continued and I guess the res.logout() gets applied to a different session? At any rate, doing the following fixes it right up:

(Good)

fetch('/auth/logout', { credentials: 'same-origin' })
  .then([other stuff]);



回答5:


I was having the same issues, capital O fixed it;

app.get('/logout', function (req, res){
  req.logOut()  // <-- not req.logout();
  res.redirect('/')
});

Edit: this is no longer an issue.




回答6:


I was recently having this same issue and none of the answers fixed the issue for me. Could be wrong but it does seem to have to do with a race condition.

Changing the session details to the options below seems to have fixed the issue for me. I have tested it about 10 times or so now and everything seems to be working correctly.

app.use(session({
    secret: 'secret',
    saveUninitialized: false,
    resave: false
}));

Basically I just changed saveUninitialized and resave from true to false. That seems to have fixed the issue.

Just for reference I'm using the standard req.logout(); method in my logout path. I'm not using the session destroy like other people have mentioned.

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/');
});



回答7:


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.




回答8:


Destroying session by yourself looks weird. I faced with this issue having next configuration:

"express": "^4.12.3",
"passport": "^0.2.1",
"passport-local": "^1.0.0",

I should say that this configuration works well. The reason of my issue was in custom sessionStore that I defined here:

app.use(expressSession({
    ...
    store: dbSessionStore,
    ...
}));

To be sure that your issue here too just comment store line and run without session persisting. If it will work you should dig into your custom session store. In my case set method was defined wrong. When you use req.logout() session store destroy() method not invoked as I thought before. Instead invoked set method with updated session.

Good luck, I hope this answer will help you.




回答9:


I got an experience that, sometime it's doesn't work because you fail to to setup passport properly. For example, I do vhost, but on main app I setup passport like this which is wrong.

app.js (why wrong ? please see blockqoute below)

require('./modules/middleware.bodyparser')(app);
require('./modules/middleware.passport')(app);
require('./modules/middleware.session')(app);
require('./modules/app.config.default.js')(app, express);

// default router across domain
app.use('/login', require('./controllers/loginController'));
app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
});

// vhost setup
app.use(vhost('sub1.somehost.dev', require('./app.host.sub1.js')));
app.use(vhost('somehost.dev', require('./app.host.main.js')));

actually, it must not be able to login, but I manage to do that because, I continue to do more mistake. by putting another passport setup here, so session form app.js available to app.host.sub1.js

app.host.sub1.js

// default app configuration
require('./modules/middleware.passport')(app);
require('./modules/app.config.default.js')(app, express);

So, when I want to logout... it's not work because app.js was do something wrong by start initialize passport.js before express-session.js, which is wrong !!.

However, this code can solved the issues anyway as others mention.

app.js

app.get('/logout', function (req, res) {
    req.logout();
    req.session.destroy(function (err) {
        if (err) {
            return next(err);
        }

        // destroy session data
        req.session = null;

        // redirect to homepage
        res.redirect('/');
    });
});

But in my case the correct way is... swap the express-session.js before passport.js

document also mention

Note that enabling session support is entirely optional, though it is recommended for most applications. If enabled, be sure to use express.session() before passport.session() to ensure that the login session is restored in the correct order.

So, resolved logout issue on my case by..

app.js

require('./modules/middleware.bodyparser')(app);
require('./modules/middleware.session')(app);
require('./modules/middleware.passport')(app);
require('./modules/app.config.default.js')(app, express);


// default router across domain
app.use('/login', require('./controllers/loginController'));
app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
});

app.host.sub1.js

// default app configuration
require('./modules/app.config.default.js')(app, express);

and now req.logout(); is work now.




回答10:


None of the answers worked for me so I will share mine

app.use(session({
    secret: 'some_secret',
    resave: false,
    saveUninitialized: false,
   cookie: {maxAge: 1000} // this is the key
}))

and

router.get('/logout', (req, res, next) => {
    req.logOut()
    req.redirect('/')
})



回答11:


I was having the same issue. Turned out that my version of passport wasn't compatible with Express 4.0. Just need to install an older version.

    npm install --save express@3.0.0



回答12:


This worked for me:

app.get('/user', restrictRoute, function (req, res) {
  res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate,
              max-stale=0, post-check=0, pre-check=0');
});

It makes sure that your page won't get stored in cache




回答13:


I'm working with a programmer, that suggests to remove user of req:

app.get('/logout', function (req, res){
  req.session.destroy(function (err) {
    req.user = null;
    res.redirect('/'); //Inside a callback… bulletproof!
  });
});

Reason: we need to remove from req(passportjs also doing this but async way) because there is no use of user data after logout even this will save memory and also might be passportjs found user data and may create new session and redirect(but not yet happen) By the ways, this is our responsibility to remove irrelevant thing. PassportJS assign data into req.user after login and also remove if we use req.logout() but it may not works properly some times as NodeJS Asynchronous in nature




回答14:


I faced the similar problem with Passport 0.3.2.

When I use Custom Callback for the passport login and signup the problem persisted.

The problem was solved by upgrading to Passport 0.4.0 and adding the lines

app.get('/logout', function(req, res) {
    req.logOut();
    res.redirect('/');
});



回答15:


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('/');
});



回答16:


Since you are using passport authentication which uses it's own session via the connect.sid cookie this simplest way of dealing with logging out is letting passport handle the session.

app.get('/logout', function(req, res){
  if (req.isAuthenticated()) {
    req.logOut()
    return res.redirect('/') // Handle valid logout
  }

  return res.status(401) // Handle unauthenticated response
})



回答17:


All examples here do a redirect after the req.session.destroy. But do realise that Express will create a new session instantly for the page you are redirecting to. In combination with Postman I found the strange behaviour that doing a Passport-Login right after the logout gives the effect that Passport is successful but cannot store the user id to the session file. The reason is that Postman needs to update the cookie in all requests for this group, and this takes a while. Also the redirect in the callback of the destroy does not help.

I solved it by not doing a redirect but just returning a json message.




回答18:


You shoulde be using req.logout() to destroy the session in the browser.

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/'); // whatever the route to your default page is
});



回答19:


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.




回答20:


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.



来源:https://stackoverflow.com/questions/13758207/why-is-passportjs-in-node-not-removing-session-on-logout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!