Warum entfernt PassportJS in Node die Sitzung beim Abmelden nicht?

70

Ich habe Probleme, mein System dazu zu bringen, sich mit PassportJS abzumelden. Es scheint, dass die Abmelderoute aufgerufen wird, die Sitzung jedoch nicht entfernt wird. Ich möchte, dass 401 zurückgegeben wird, wenn der Benutzer nicht auf einer bestimmten Route angemeldet ist. Ich rufe authenticateUser an, um zu überprüfen, ob der Benutzer angemeldet ist.

Vielen Dank!

/******* 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();
}
Jeffrey Chen
quelle
In meinem Code rufe ich req.logOut()in Großbuchstaben auf, aber in Bezug auf die Anleitung sollte Ihr Code auch funktionieren.
Balazs
Ich habe viele Lösungen ausprobiert, aber keine davon hat für mich funktioniert. Schließlich habe ich versucht, das Paket [email protected] auf [email protected] zu aktualisieren und es funktioniert!
Prisan

Antworten:

82

Brices Antwort ist großartig , aber ich bemerkte immer noch eine wichtige Unterscheidung; Der Passport-Leitfaden schlägt vor, .logout()Folgendes .logOut()als solches zu verwenden (auch als Alias ​​bezeichnet ):

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

Wie oben erwähnt, ist dies jedoch unzuverlässig. Ich fand es wie erwartet, als ich Brices Vorschlag wie folgt umsetzte:

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

Hoffe das hilft!

jlmakes
quelle
2
Ich bekomme nur 'Object # <Session> hat keine Methode' destroy '' als Fehlermeldung
schlenger
@schlenger Hmm, oben wird Express 3 verwendet. Verwenden Sie Version 4?
Ich mache den
4
Seien Sie vorsichtig, da in dem hier angegebenen Beispiel die Verwendung von session.destroy die Verwendung des Express-Sessions-Moduls voraussetzt. Andere Sitzungsmodule (z. B. Mozilla Node-Client-Sessions) fügen eine Zerstörungsmethode hinzu, die nur synchron zurückgibt und Rückrufpararmeter ignoriert. In diesem Fall wird Ihr Code hängen
frooble
Basieren Sie das client-sessions? Die Zerstörungsfunktion benötigt kein Argument: github.com/mozilla/node-client-sessions/blob/master/lib/…
oztune
ok ich benutze deine zweite Methode. Die Sitzungsdatei im Sitzungsordner wird gelöscht und das Sitzungscookie vom Client entfernt. Der Knotenserver zeigt jedoch die folgende Fehlermeldung in der Konsole an: [session-file-store] will retry, error on last attempt: Error: ENOENT: no such file or directory, open ...Wie kann ich das beheben?
Emonhossain
42

Bin auf das gleiche Problem gestoßen. Verwenden req.session.destroy();statt req.logout();Arbeiten, aber ich weiß nicht, ob dies die beste Vorgehensweise ist.

Brice
quelle
1
Hinzufügen von req.session.destroy (); arbeitete für mich, ich hatte das gleiche Problem
Michael
2
req.session.destroy (); hat auch für mich funktioniert und denken Sie daran, Ihre Cookies mit res.clearCookie ('cookiename') zu löschen. Weiß jedoch jemand, was genau req.logout () tut?
Hrushikesh
@WebHrushi finden Sie die Funktion in passport / lib / passport / http / request.js. Heute funktionieren beide Versionen nicht mehr für mich. Die Umleitung wird auf beide Arten aufgerufen, bevor die Sitzung endgültig zerstört wird. Auf der umgeleiteten Seite scheinen beide hier beschriebenen Möglichkeiten gut zu funktionieren.
Ztirom
3
Ich mag die Idee nicht zu verwenden req.logout(), aus Gründen der Vorwärtskompatibilität und so weiter, also habe ich nur req.session.destroy();nachher hinzugefügt req.logout()und das funktioniert gut.
Gavin
14

session.destroy Möglicherweise reicht dies nicht aus. Um sicherzustellen, dass der Benutzer vollständig abgemeldet ist, müssen Sie auch das Sitzungscookie löschen.

Das Problem hierbei ist, dass, wenn Ihre Anwendung auch als API für eine Einzelseiten-App verwendet wird (nicht empfohlen, aber recht häufig), einige Anforderungen von Express verarbeitet werden können, die vor dem Abmelden gestartet und nach dem Abmelden beendet wurden. Wenn dies der Fall wäre, würde diese länger laufende Anforderung die Sitzung in redis wiederherstellen, nachdem sie gelöscht wurde. Und da der Browser beim nächsten Öffnen der Seite immer noch dasselbe Cookie hat, werden Sie erfolgreich angemeldet.

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

Das ist es, was vielleicht sonst passiert:

  1. Req 1 (jede Anfrage) wird empfangen
  2. Req 1 lädt die Sitzung von Redis in den Speicher
  3. Abmeldeanforderung erhalten
  4. Abmeldeanforderung lädt Sitzung
  5. Abmeldeanforderung zerstört Sitzung
  6. Abmeldeanforderung sendet Weiterleitung an den Browser (Cookie wird nicht entfernt)
  7. Req 1 schließt die Verarbeitung ab
  8. Req 1 speichert die Sitzung aus dem Speicher in Redis
  9. Der Benutzer öffnet die Seite ohne Anmeldedialog, da sowohl das Cookie als auch die Sitzung vorhanden sind

Idealerweise müssen Sie die Token-Authentifizierung für API-Aufrufe verwenden und nur Sitzungen in einer Web-App verwenden, die nur Seiten lädt. Selbst wenn Ihre Web-App nur zum Abrufen von API-Token verwendet wird, ist diese Race-Bedingung weiterhin möglich.

insb
quelle
1
Dies löst das Problem auf dem Client und nicht auf dem Server, was unsicher erscheint. Wenn Sie vor dem connect.sidAbmelden entführt werden, sollte sich keine andere Partei wie Sie anmelden können, nachdem Sie sich abgemeldet haben.
Paul S
8

Ich hatte das gleiche Problem, und es stellte sich heraus, dass es überhaupt kein Problem mit Passport-Funktionen gab, sondern vielmehr mit der Art und Weise, wie ich meine /logoutRoute anrief . Ich habe fetch verwendet, um die Route aufzurufen:

(Schlecht)

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

Es stellt sich heraus, dass dadurch keine Cookies gesendet werden, sodass die Sitzung nicht fortgesetzt wird und ich denke, dass die Sitzung res.logout()auf eine andere Sitzung angewendet wird. Wenn Sie Folgendes tun, wird das Problem auf jeden Fall behoben:

(Gut)

fetch('/auth/logout', { credentials: 'same-origin' })
  .then([other stuff]);
nrflaw
quelle
War 4 Stunden auf der Suche, bevor ich auf diese Antwort kam, ein dummer Fehler, aber danke für den Hinweis.
Azhar Husain
7

Ich hatte die gleichen Probleme, Kapital O hat es behoben;

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

Bearbeiten: Dies ist kein Problem mehr.

abitofcode
quelle
1
Die aktuelle Version von Passport akzeptiert entweder logOut oder logout (wie bei login und logIn)
cyberwombat
4
logoutund logOutwurden im Jahr 2015 zueinander voreingenommen: github.com/jaredhanson/passport/blame/…
Paul S
4

Ich hatte kürzlich das gleiche Problem und keine der Antworten hat das Problem für mich behoben. Könnte falsch sein, aber es scheint mit einer Rennbedingung zu tun zu haben.

Das Ändern der Sitzungsdetails in die folgenden Optionen scheint das Problem für mich behoben zu haben. Ich habe es jetzt ungefähr 10 Mal getestet und alles scheint richtig zu funktionieren.

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

Grundsätzlich habe ich mich gerade verändert saveUninitializedund resavevon truebis false. Das scheint das Problem behoben zu haben.

Nur als Referenz verwende ich die Standardmethode req.logout();in meinem Abmeldepfad. Ich benutze die Sitzung nicht zerstören, wie andere Leute erwähnt haben.

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/');
});
Charlie Fish
quelle
Ich hatte Probleme mit dem Abmelden und Anmelden, die gelegentlich nicht klebten, und dies hat es für mich behoben.
Tom Hughes
@ TomHughes Ich bin froh, dass es geholfen hat !!
Charlie Fish
4

Ich habe beide verwendet req.logout()und req.session.destroy()und funktioniert gut.

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

Nur um zu erwähnen, ich benutze Redis als Session Store.

sstauross
quelle
Ihr Code kann zu einem unerwarteten Verhalten führen, wenn der Cache langsam ist oder wenn nach der Abmeldelogik eine Middleware ausgeführt wird. Verwenden Sie dazu req.session.destroy (() => res.redirect ("/")).
Fareed Alnamrouti
session.destroyist asynchron und erfordert einen Rückruf. Sie sollten res.redirect('/');diesen Rückruf anrufen .
Grün
3

Keine der Antworten hat für mich funktioniert, daher werde ich meine teilen

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

und

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

Die Sitzung selbst zu zerstören sieht komisch aus. Ich hatte dieses Problem mit der nächsten Konfiguration:

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

Ich sollte sagen, dass diese Konfiguration gut funktioniert . Der Grund für mein Problem war der Brauch sessionStore, den ich hier definiert habe:

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

Um sicherzugehen, dass auch hier Ihr Problem auftritt, kommentieren Sie einfach die Speicherzeile und führen Sie sie aus, ohne dass die Sitzung bestehen bleibt. Wenn es funktioniert, sollten Sie in Ihrem benutzerdefinierten Sitzungsspeicher stöbern. In meinem Fall wurde die setMethode falsch definiert. Wenn Sie die req.logout()Session Store- destroy()Methode verwenden, wird diese nicht wie zuvor beschrieben aufgerufen. Stattdessen aufgerufene setMethode mit aktualisierter Sitzung.

Viel Glück, ich hoffe diese Antwort wird Ihnen helfen.

DontRelaX
quelle
1

Ich habe die Erfahrung gemacht, dass es manchmal nicht funktioniert, weil Sie den Pass nicht richtig einrichten können. Zum Beispiel, vhostaber in der Haupt-App richte ich einen Pass wie diesen ein, was falsch ist.

app.js (warum falsch? siehe Blockqoute unten)

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

Eigentlich muss es nicht möglich sein, sich anzumelden, aber ich schaffe das, weil ich weiterhin mehr Fehler mache. indem Sie hier ein anderes Pass-Setup einfügen, damit das Sitzungsformular für app.jsverfügbar istapp.host.sub1.js

app.host.sub1.js

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

Also, wenn ich mich abmelden möchte ... funktioniert es nicht, weil app.jsetwas falsch gemacht wurde , indem ich passport.jsvorher express-session.jsmit der Initialisierung begonnen habe , was falsch ist !!

Dieser Code kann die Probleme jedoch trotzdem lösen, wie andere erwähnen.

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

Aber in meinem Fall ist der richtige Weg ... tauschen Sie die Datei express-session.js vor passport.js aus

Dokument auch erwähnen

Beachten Sie, dass das Aktivieren der Sitzungsunterstützung völlig optional ist, obwohl dies für die meisten Anwendungen empfohlen wird. Wenn diese Option aktiviert ist, müssen Sie vor passport.session () express.session () verwenden, um sicherzustellen, dass die Anmeldesitzung in der richtigen Reihenfolge wiederhergestellt wird.

Also, Problem mit der Abmeldung in meinem Fall behoben durch ..

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

und jetzt req.logout();ist jetzt Arbeit.

Jongz Puangput
quelle
1

einfach req.logOut () hinzufügen; löste dieses Problem; "O" sollte groß geschrieben werden

DAYALAN S.
quelle
1
Herzlich willkommen! In diesem Handbuch finden Sie Informationen
Dinesh Nadimpalli
0

Ich hatte das gleiche Problem. Es stellte sich heraus, dass meine Passversion nicht mit Express 4.0 kompatibel war. Sie müssen nur eine ältere Version installieren.

    npm install --save express@3.0.0
Ryan
quelle
0

Das hat bei mir funktioniert:

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

Es stellt sicher, dass Ihre Seite nicht im Cache gespeichert wird

Xavier Reyes Ochoa
quelle
0

Ich arbeite mit einem Programmierer, der vorschlägt, den Benutzer von req zu entfernen:

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

Grund: Wir müssen aus req entfernen (passportjs tun dies auch, aber asynchron), da nach dem Abmelden keine Benutzerdaten verwendet werden. Dies spart Speicherplatz und kann dazu führen, dass passportjs Benutzerdaten gefunden werden und möglicherweise eine neue Sitzung erstellen und umleiten (aber nicht) noch passieren) Übrigens liegt es in unserer Verantwortung, irrelevante Dinge zu entfernen. PassportJS weist req.user nach der Anmeldung Daten zu und entfernt sie auch, wenn wir req.logout () verwenden, aber es funktioniert möglicherweise manchmal nicht richtig als NodeJS Asynchronous

mrmccormack
quelle
0

Ich hatte ein ähnliches Problem mit Passport 0.3.2.

Wenn ich Custom Callback für die Passport-Anmeldung und -Anmeldung verwende, bleibt das Problem bestehen.

Das Problem wurde durch ein Upgrade auf Passport 0.4.0 und Hinzufügen der Zeilen gelöst

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

Anscheinend gibt es mehrere mögliche Ursachen für dieses Problem. In meinem Fall lag das Problem in der falschen Reihenfolge der Deklarationen, dh der Abmeldeendpunkt wurde vor der Passinitialisierung deklariert. Die richtige Reihenfolge ist:

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


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

Da Sie die Passauthentifizierung verwenden, bei der eine eigene Sitzung über das connect.sidCookie verwendet wird, können Sie die Sitzung am einfachsten mit dem Abmelden abwickeln.

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

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

Alle Beispiele hier führen eine Umleitung nach der req.session.destroy durch. Beachten Sie jedoch, dass Express sofort eine neue Sitzung für die Seite erstellt, zu der Sie umleiten. In Kombination mit Postman habe ich das seltsame Verhalten festgestellt, dass eine Passport-Anmeldung direkt nach dem Abmelden den Effekt hat, dass Passport erfolgreich ist, die Benutzer-ID jedoch nicht in der Sitzungsdatei gespeichert werden kann. Der Grund dafür ist, dass Postman das Cookie in allen Anforderungen für diese Gruppe aktualisieren muss. Dies dauert eine Weile. Auch die Weiterleitung im Rückruf der Zerstörung hilft nicht.

Ich habe es gelöst, indem ich keine Weiterleitung durchgeführt habe, sondern nur eine JSON-Nachricht zurückgegeben habe.

BertC
quelle
0

Dies ist immer noch ein Problem.

Was ich getan habe, war, req.session.destroy(function (err) {});auf der Serverseite und auf der Clientseite zu verwenden, wann immer sie sich abmelden:

const logout = () => {
    const url = '/users/logout'
    fetch(url)
    setTimeout(function () {
      location.reload();    }, 500);

Auf diese Weise ist der Benutzer beim Aktualisieren der Seite ohne Sitzung. Stellen Sie einfach sicher, dass Sie zur richtigen Seite umleiten, wenn niemand authentifiziert ist.

Vielleicht nicht der beste Ansatz, aber er funktioniert.

Daniel Dias
quelle
Vielen Dank für den Versuch zu unterstützen. Ich würde vorschlagen, die Code-Formatierung in Ihrer Antwort zu korrigieren. Das von Ihnen gepostete Haupt-Snippet ist kein gültiges JS.
Andrew Nolan
0

Sie können versuchen, die Sitzung manuell neu zu generieren:

app.get('/logout', (req, res) => {
    req.logOut();
    req.session.regenerate(err => {
        err && console.log(err);
    });
    res.redirect('/');
});

Dadurch werden keine anderen Daten (wie der Reisepass) aus der Sitzung entfernt.

Tim Den
quelle
-1

Sie sollten req.logout () verwenden, um die Sitzung im Browser zu zerstören.

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

Ich weiß nicht wie, habe aber ng-href="https://stackoverflow.com/signout"mein Problem gelöst. Früher habe ich den Dienst zum Abmelden verwendet, aber stattdessen habe ich ihn direkt verwendet.

AKASH
quelle
1
Ich benutze Angular nicht, daher ergibt dies keinen Sinn. : /
IIllIIll
-3

In meinem Fall hat die Verwendung eines Rückrufs, der an übergeben wurde, req.session.destroynur teilweise geholfen, und ich musste auf diesen Hack zurückgreifen:

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

Ich weiß nicht, warum dies die einzige Lösung ist, die ich zur Arbeit bringen konnte, aber leider hat die Antwort von @ JulianLloyd bei mir nicht konsequent funktioniert.

Dies hat möglicherweise etwas mit der Tatsache zu tun, dass meine Live-Anmeldeseite SSL verwendet (ich konnte das Problem nicht auf der Staging-Site oder auf meinem lokalen Host reproduzieren). Möglicherweise ist in meiner App noch etwas anderes los. Ich verwende das Derby-Passport-Modul, da meine App das Derby- Framework verwendet. Daher ist es schwierig, das Problem zu isolieren.

Es ist eindeutig ein Zeitproblem, da ich zuerst eine Zeitüberschreitung von 100 ms versucht habe, was nicht ausreichend war.

Leider habe ich noch keine bessere Lösung gefunden.

Matt Browne
quelle
Warum Verzögerung von 2 Sekunden, wenn Sie einen asynchronen Rückruf verwenden können
Godfather
Der Rückruf wurde für mich nicht konsequent ausgelöst.
Matt Browne