web-dev-qa-db-fra.com

Pourquoi PassportJS dans Node ne supprime-t-il pas la session lors de la déconnexion

Je ne parviens pas à déconnecter mon système avec PassportJS. Il semble que la route de déconnexion est appelée mais ne supprime pas la session. Je veux qu'il retourne 401, si l'utilisateur n'est pas connecté dans un itinéraire spécifique. J'appelle authenticateUser pour vérifier si l'utilisateur est connecté.

Merci beaucoup! 

/******* 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();
}
59
Jeffrey Chen

La réponse de Brice est excellente , mais j’ai quand même remarqué une distinction importante à faire; Le guide de passeport suggère d'utiliser .logout() (alias également comme .logOut()) en tant que tel:

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

Mais comme mentionné ci-dessus, ce n'est pas fiable. Je me suis rendu compte que tout se passait comme prévu lors de la mise en œuvre de la suggestion de Brice:

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

J'espère que cela t'aides!

72
jlmakes

Couru dans le même problème. Utiliser req.session.destroy(); au lieu de req.logout(); fonctionne, mais je ne sais pas si c'est la meilleure pratique.

41
Brice

session.destroy peut être insuffisant. Pour vous assurer que l'utilisateur est complètement déconnecté, vous devez également effacer le cookie de session.

Le problème ici est que si votre application est également utilisée en tant qu'API pour une application à page unique (non recommandée mais assez commune), il peut y avoir des demandes en cours de traitement par express qui ont démarré avant la déconnexion et se terminent après la déconnexion. Si tel était le cas, cette requête plus longue restaurera la session en redis après sa suppression. Et parce que le navigateur a toujours le même cookie la prochaine fois que vous ouvrez la page, vous serez connecté avec succès.

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

C'est ce qui se passe peut-être autrement:

  1. La demande 1 (toute demande) est reçue
  2. Req 1 charge la session de redis en mémoire
  3. Déconnexion req reçu
  4. Déconnexion req load session
  5. Déconnexion req détruit la session
  6. Déconnexion req envoie une redirection vers le navigateur (le cookie n'est pas supprimé)
  7. Req 1 termine le traitement
  8. Req 1 enregistre la session de la mémoire à redis
  9. L'utilisateur ouvre la page sans boîte de dialogue de connexion car le cookie et la session sont en place

Idéalement, vous devez utiliser l'authentification par jeton pour les appels d'API et utiliser uniquement des sessions dans une application Web ne chargeant que des pages. Toutefois, même si votre application Web n'est utilisée que pour obtenir des jetons API, cette condition de concurrence critique est toujours possible.

9
esp

J'avais le même problème et il s'est avéré que ce n'était pas du tout un problème avec les fonctions de Passport, mais plutôt de la façon dont j'appelais mon itinéraire /logout. J'ai utilisé fetch pour appeler la route:

(Mal)

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

Il s’avère que cela n’envoie pas de cookies, donc la session n’est pas poursuivie et je suppose que la fonction res.logout() est appliquée à une session différente? Quoi qu’il en soit, ce qui suit corrige tout cela:

(Bien)

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

J'avais les mêmes problèmes, O majuscule le réglait;

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

J'avais récemment le même problème et aucune des réponses ne l'a résolu. Peut-être que vous avez tort, mais cela semble être lié à une situation de concurrence.

Changer les détails de la session aux options ci-dessous semble avoir résolu le problème pour moi. Je l'ai testé environ 10 fois ou plus maintenant et tout semble fonctionner correctement.

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

En gros, je viens de changer saveUninitialized et resave de true à false. Cela semble avoir résolu le problème.

Juste pour référence, j'utilise la méthode req.logout(); standard dans mon chemin de déconnexion. Je n'utilise pas la session détruire comme d'autres personnes l'ont mentionné.

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

J'ai utilisé à la fois req.logout() et req.session.destroy() et fonctionne bien.

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

Juste pour mentionner, j'utilise Redis comme magasin de session.

4
sstauross

Détruire une session par vous-même a l’air bizarre ... J'ai rencontré ce problème avec la configuration suivante:

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

Je devrais dire que cette configuration fonctionne bien . La raison de mon problème était dans la coutume sessionStore que j'ai définie ici:

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

Pour être sûr que votre problème ici aussi, commentez ligne de magasin et exécutez-le sans persister. Si cela fonctionne, vous devriez creuser dans votre magasin de session personnalisé. Dans mon cas, la méthode set était mal définie. Lorsque vous utilisez la méthode req.logout() session store destroy() non invoquée comme je le pensais auparavant. À la place, invoqué la méthode set avec la session mise à jour.

Bonne chance, j'espère que cette réponse vous aidera.

1
DontRelaX

J'ai eu une expérience qui, parfois, ne fonctionne pas parce que vous ne parvenez pas à configurer le passeport correctement ... par exemple, je fais vhost, mais sur l'application principale, je configure un passeport comme celui-ci qui est faux.

app.js (pourquoi mal? Merci de voir blockqoute ci-dessous)

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')));

en fait, il ne doit pas pouvoir se connecter, mais j'arrive à le faire car je continue à faire plus d'erreur. en mettant une autre configuration de passeport ici, afin que le formulaire de session app.js soit disponible pour app.Host.sub1.js

app.Host.sub1.js

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

Donc, quand je veux me déconnecter ... ça ne marche pas parce que app.js a fait quelque chose de mal en démarrant initialize passport.js avant express-session.js, ce qui est faux !!.

Cependant, ce code peut résoudre les problèmes de toute façon comme d'autres le mentionnent.

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

Mais dans mon cas, la bonne façon est de ... échanger le express-session.js avant le passport.js

document mentionne aussi

Notez que l'activation du support de session est entièrement facultative, bien que ce soit le cas recommandé pour la plupart des applications. Si activé, assurez-vous d'utiliser express.session () avant passport.session () pour s'assurer que le login la session est restaurée dans le bon ordre.

Donc, résolu problème de déconnexion sur mon cas par ..

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

et maintenant req.logout(); est un travail maintenant.

1
Jongz Puangput

J'avais le même problème. Il s'est avéré que ma version de passeport n'était pas compatible avec Express 4.0. Juste besoin d'installer une version plus ancienne.

    npm install --save [email protected]
0
Ryan

Cela a fonctionné pour moi:

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

Il s'assure que votre page ne sera pas stockée dans le cache

0
Xavier Reyes Ochoa

Tous les exemples ici font une redirection après le req.session.destroy. Mais sachez que Express créera instantanément une nouvelle session pour la page vers laquelle vous redirigez. En combinaison avec Postman, j’ai trouvé le comportement étrange selon lequel le fait de procéder à une connexion Passport-Login immédiatement après la déconnexion donne l’effet que Passport aboutit mais ne peut pas stocker l’ID utilisateur dans le fichier de session. La raison en est que Postman doit mettre à jour le cookie dans toutes les demandes de ce groupe, ce qui prend un certain temps. Aussi la redirection dans le rappel de la destruction ne aide pas. 

Je l'ai résolu en ne faisant pas de redirection, mais en renvoyant un message json.

0
BertC

Apparemment, il existe plusieurs causes possibles à ce problème. Dans mon cas, le problème était un mauvais ordre de déclaration, c’est-à-dire que le noeud final de déconnexion avait été déclaré avant l’initialisation du passeport. Le bon ordre est:

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


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

Vous devriez utiliser req.logout () pour détruire la session dans le navigateur.

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

Je travaille avec un programmeur, qui suggère de supprimer l'utilisateur de req:

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

Raison: Nous devons supprimer de req (passportjs le fait également mais de manière asynchrone) car il n’est pas fait usage des données de l’utilisateur après la déconnexion Même cela permettra d’économiser de la mémoire et pourrait également contenir les données d’utilisateur trouvées et peut créer une nouvelle session et rediriger (mais ne se produit pas encore) Soit dit en passant, il est de notre responsabilité de supprimer tout élément non pertinent. PassportJS attribue des données à req.user après la connexion et les supprime également si nous utilisons req.logout (), mais cela risque de ne pas fonctionner correctement à certains moments, car NodeJS est de nature asynchrone.

0
mrmccormack

J'ai rencontré le même problème avec Passport 0.3.2.

Lorsque j'utilise le rappel personnalisé pour la connexion et l'inscription au passeport, le problème persiste.

Le problème a été résolu en passant à Passport 0.4.0 et en ajoutant les lignes.

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

Aucune des réponses n’a fonctionné pour moi, alors je vais partager les miennes.

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

et

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

Puisque vous utilisez une authentification de passeport qui utilise sa propre session via le cookie connect.sid, cette façon la plus simple de gérer la déconnexion consiste à laisser Passport gérer la 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
})
0
acharlop