Umleitung zur vorherigen Seite nach der Authentifizierung in node.js mit passport.js

98

Ich versuche, einen Anmeldemechanismus mit node.js, express und passport.js einzurichten. Das Login selbst funktioniert ganz gut, auch Sitzungen werden gut mit redis gespeichert, aber ich habe einige Probleme damit, den Benutzer dorthin umzuleiten, wo er angefangen hat, bevor er zur Authentifizierung aufgefordert wird.

zB Benutzer folgt Link http://localhost:3000/hiddenwird dann umgeleitet, http://localhost:3000/loginaber dann möchte ich, dass er wieder zurückgeleitet wird zu http://localhost:3000/hidden.

Wenn der Benutzer zufällig auf eine Seite zugreift, auf der er zuerst angemeldet sein muss, wird er mit seinen Anmeldeinformationen auf die / login-Site umgeleitet und dann zurück auf die Site umgeleitet, auf die er zuvor versucht hat, zuzugreifen.

Hier ist mein Login-Beitrag

app.post('/login', function (req, res, next) {
    passport.authenticate('local', function (err, user, info) {
        if (err) {
            return next(err)
        } else if (!user) { 
            console.log('message: ' + info.message);
            return res.redirect('/login') 
        } else {
            req.logIn(user, function (err) {
                if (err) {
                    return next(err);
                }
                return next(); // <-? Is this line right?
            });
        }
    })(req, res, next);
});

und hier meine sureAuthenticated Methode

function ensureAuthenticated (req, res, next) {
  if (req.isAuthenticated()) { 
      return next();
  }
  res.redirect('/login');
}

welches in die /hiddenSeite einhakt

app.get('/hidden', ensureAuthenticated, function(req, res){
    res.render('hidden', { title: 'hidden page' });
});

Die HTML-Ausgabe für die Anmeldeseite ist recht einfach

<form method="post" action="/login">

  <div id="username">
    <label>Username:</label>
    <input type="text" value="bob" name="username">
  </div>

  <div id="password">
    <label>Password:</label>
    <input type="password" value="secret" name="password">
  </div>

  <div id="info"></div>
    <div id="submit">
    <input type="submit" value="submit">
  </div>

</form>
Alx
quelle
Hallo, ich mache das auch. Der einzige Unterschied besteht darin, dass der Benutzer sich erfolgreich anmeldet und dann zur Seite welcome.html mit der Meldung "Willkommener Benutzername" weiterleitet. Hören Sie, dass Benutzername sein / ihr Anmeldename ist. Können Sie mir zeigen, wie der Textfeldwert an die nächste Seite übergeben wird?
Dhrumil Shah
Ich weiß nicht viel und würde es einfach durchgehen. Nach meinem Beispiel könnte dies sein: res.render ('hidden', {title: 'hidden page', Benutzername: 'Tyrion Lannister'});
Alx

Antworten:

84

Ich weiß nichts über Reisepass, aber so mache ich das:

Ich habe eine Middleware, die ich mit app.get('/account', auth.restrict, routes.account)diesen Sets redirectToin der Sitzung verwende ... dann leite ich zu / login um

auth.restrict = function(req, res, next){
    if (!req.session.userid) {
        req.session.redirectTo = '/account';
        res.redirect('/login');
    } else {
        next();
    }
};

Dann routes.login.postmache ich in:

var redirectTo = req.session.redirectTo || '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);
chovy
quelle
Danke für deine Antwort. Stellen Sie die Weiterleitung / das Konto nicht direkt in Ihrem Code ein? Was passiert, wenn Sie einen anderen Link haben? Sagen wir / pro_accounts mit demselben auth.restrict, zu dem sie umgeleitet werden würden.
Alx
4
Das ist richtig. Ich req.session.redirect_to = req.path
leite
1
hm .. es ist nicht ganz was ich suche. Ich rufe sureAuthenticated auf, um herauszufinden, ob ein Benutzer bereits autorisiert ist oder nicht, wenn nicht, wird er zu / login umgeleitet. Aus dieser Sicht brauche ich einen Weg, um zu / account zurückzukehren. Wenn ich req.path in / login aufrufe, bekomme ich einfach / login zurück. Die Verwendung von Referer funktioniert auch nicht und es ist nicht ganz sicher, ob der Browser den Referer richtig einstellt ...
Alx
6
Sie müssen in req.session.redirect_to = req.pathIhrer Auth-Middleware festlegen . Dann lesen Sie es und löschen Sie es in der login.postRoute.
Chovy
Dies ist keine großartige Lösung für Reisepässe, die successRedirect als Option zu einem Zeitpunkt akzeptieren, an dem keine Anfrage verfügbar ist
Linuxdan
109

ensureAuthenticatedSpeichern Sie in Ihrer Methode die Rückgabe-URL in der Sitzung wie folgt:

...
req.session.returnTo = req.originalUrl; 
res.redirect('/login');
...

Dann können Sie Ihre passport.authenticate-Route auf Folgendes aktualisieren:

app.get('/auth/google/return', passport.authenticate('google'), function(req, res) {
    res.redirect(req.session.returnTo || '/');
    delete req.session.returnTo;
}); 
Linuxdan
quelle
7
Arbeitete am ersten Durchgang, aber ich musste nach der Umleitung in der passport.authenticate-Route Folgendes hinzufügen, um zu vermeiden, dass sie nach dem anschließenden Abmelden / Anmelden wieder zur returnTo-Adresse zurückgeführt wird: Weitere Informationen zum Standard-Abmelden des Passes finden req.session.returnTo = null;Sie in dieser Frage. Bei Prüfung der Quelle scheint nur die session.user und nicht die gesamte Sitzung zu löschen.
Rob Andren
Kann ich einfach eine Abfrage wie? callback = / profile anstelle von session
OMGPOP
@OMGPOP könnten Sie wahrscheinlich, aber das klingt nicht sehr sicher. Gibt es einen Grund, warum Sie die Sitzung nicht nutzen möchten?
Linuxdan
@linuxdan überhaupt nicht. Es ist jedoch nur mühsam, den Umleitungslink zu extrahieren und in die Sitzung zu setzen. Wenn der Benutzer zurückleitet, weisen Sie den Umleitungslink aus der Sitzung zurück
OMGPOP
5
Anstelle von req.pathwürde ich verwenden, req.originalUrlda es eine Kombination aus baseUrl und Pfad ist, siehe die Dokumentation
matthaeus
14

Werfen Sie einen Blick auf Connect-Sure-Login , das neben Passport genau das tut, was Sie wollen!

Jared Hanson
quelle
7

Meine Art Dinge zu tun:

const isAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next()
  }
  res.redirect( `/login?origin=${req.originalUrl}` )
};

GET / Login- Controller:

if( req.query.origin )
  req.session.returnTo = req.query.origin
else
  req.session.returnTo = req.header('Referer')

res.render('account/login')

POST / Login- Controller:

  let returnTo = '/'
  if (req.session.returnTo) {
    returnTo = req.session.returnTo
    delete req.session.returnTo
  }

  res.redirect(returnTo);

POST / Logout- Controller (nicht sicher, ob 100% in Ordnung sind, Kommentare sind willkommen):

req.logout();
res.redirect(req.header('Referer') || '/');
if (req.session.returnTo) {
  delete req.session.returnTo
}

ReturnTo-Middleware löschen (löscht returnTo von der Sitzung auf einer beliebigen Route außer Authentifizierungsrouten - für mich sind sie / login und / auth /: provider):

String.prototype.startsWith = function(needle)
{
  return(this.indexOf(needle) == 0)
}

app.use(function(req, res, next) {
  if ( !(req.path == '/login' || req.path.startsWith('/auth/')) && req.session.returnTo) {
    delete req.session.returnTo
  }
  next()
})

Dieser Ansatz weist zwei Merkmale auf :

  • Sie können einige Routen mit isAuthenticated Middleware schützen .
  • auf jeder Seite können Sie einfach auf Login - URL klicken und nach der Anmeldung Rückkehr zu dieser Seite;
deksden
quelle
Hier gab es so viele gute Antworten, dass ich Ihr Beispiel verwenden und es mit meinem Projekt zum Laufen bringen konnte. Danke dir!
user752746
5

Wenn Sie connect-sure-login verwenden gibt es eine supereinfache, integrierte Möglichkeit, dies mit Passport mithilfe des successReturnToOrRedirectParameters zu tun . Bei Verwendung des Passes werden Sie an die ursprünglich angeforderte URL zurückgesendet oder auf die von Ihnen angegebene URL zurückgegriffen.

router.post('/login', passport.authenticate('local', {
  successReturnToOrRedirect: '/user/me',
  failureRedirect: '/user/login',
  failureFlash: true
}));

https://github.com/jaredhanson/connect-ensure-login#log-in-and-return-to

Igneosaurier
quelle
Dies scheint ein Duplikat von @ jaredhansons Antwort zu sein
mikemaccana
1

Die Antworten von @chovy und @linuxdan haben den Fehler, dass nicht gelöscht wird, session.returnTowenn der Benutzer nach der Umleitung der Anmeldung (für die keine Authentifizierung erforderlich ist) zu einer anderen Seite wechselt und sich dort anmeldet . Fügen Sie diesen Code zu ihren Implementierungen hinzu:

// clear session.returnTo if user goes to another page after redirect to login
app.use(function(req, res, next) {
    if (req.path != '/login' && req.session.returnTo) {
        delete req.session.returnTo
    }
    next()
})

Wenn Sie einige Ajax-Anfragen von der Anmeldeseite ausführen, können Sie diese auch ausschließen.


Ein anderer Ansatz ist die Verwendung von Flash- InensureAuthenticated

req.flash('redirectTo', req.path)
res.redirect('/login')

Und dann in GET Login

res.render('login', { redirectTo: req.flash('redirectTo') })

In der Ansicht ein verstecktes Feld zum Anmeldeformular hinzufügen (Beispiel in Jade)

if (redirectTo != '')
    input(type="hidden" name="redirectTo" value="#{redirectTo}")

In der POST-Anmeldung

res.redirect(req.body.redirectTo || '/')

Beachten Sie, dass redirectTo nach dem ersten GET-Login damit gelöscht wird.

Alexander Danilov
quelle
0

Der einfachste (und richtigste) Weg, dies zu erreichen, sind Einstellungen failureRedirectund successRedirectOptionen .

Eray
quelle
Können Sie das näher erläutern? Ich kann sehen, successRedirectdass es auf dem Rückweg verwendet wird, aber dann würde das beabsichtigte Ziel (dh returnTooben) verloren gehen, oder?
Tom Söderlund