web-dev-qa-db-de.com

Node.js Authentifizierung mit Passport: Wie blinkt eine Nachricht, wenn ein Feld fehlt?

Ich verwende passport.js und ich möchte eine Nachricht flashen, wenn die Felder meines Formulars leer sind. Aber ich weiß nicht, wie das geht, da der Pass nicht den Strategie-Callback auslöst, wenn er fehlt. Ich möchte, dass dieser Anwendungsfall klarer wird, und ich möchte keinen Pass ändern. Ich habe das Gefühl, dass es einen Weg gibt, aber ich weiß nicht wo! Ich habe versucht, den Rückruf der Route (app.post) zu verwenden, aber es scheint nicht so zu funktionieren, wie ich es versucht habe.

Hier ist der Prototyp der Authentifizierungsfunktion:

Strategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var username = lookup(req.body, this._usernameField) || lookup(req.query, this._usernameField);
  var password = lookup(req.body, this._passwordField) || lookup(req.query, this._passwordField);
  // here is my problem
  if (!username || !password) {
    return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400);
  }

  var self = this;

  function verified(err, user, info) {
    if (err) { return self.error(err); }
    if (!user) { return self.fail(info); }
    self.success(user, info);
  }

  try {
    if (self._passReqToCallback) {
      this._verify(req, username, password, verified);
    } else {
      this._verify(username, password, verified);
    }
  } catch (ex) {
    return self.error(ex);
  }
};

Hier ist meine Strategie:  

 passport.use('local-login', new LocalStrategy({
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true 
    },
    function(req, email, password, done) { 
        // ...
        console.log("Hello");
        User.findOne({ 'local.email' :  email }, function(err, user) {
            if (err)
                return done(err);

            // if no user is found, return the message
            if (!user)
                return done(null, false, req.flash('loginMessage', 'Pas d\'utilisateur avec ce login.')); // req.flash is the way to set flashdata using connect-flash

            // if the user is found but the password is wrong
            if (!user.validPassword(password))
                return done(null, false, req.flash('loginMessage', 'Oops! Mauvais password.')); // create the loginMessage and save it to session as flashdata

            // all is well, return successful user
            return done(null, user);
        });

    }));

Und zum Schluss meine Route:

app.get('/login', function(req, res) {

    // render the page and pass in any flash data if it exists
    res.render('login', { title: "Connexion", message: req.flash('loginMessage') }); 
});

// process the login form
    app.post('/login', passport.authenticate('local-login', {
        successRedirect : '/profile', // redirect to the secure profile section
        failureRedirect : '/login', // redirect back to the signup page if there is an error
        failureFlash : true // allow flash messages
    }, function(err, user, info) {
         // Was trying this callback, does'nt work, post callback maybe ?
         console.log("Hello");
    }));
11
Mael Jarnole

Sie sollten in Ihrem Überprüfungsrückruf nicht req.flash aufrufen. Stattdessen sollten Sie eine Nachricht wie in der Dokumentation gezeigt zurückgeben. Passport legt die Nachricht als Flash-Nachricht zurück, wenn failureFlash: true.

Ihr überarbeiteter Überprüfungsrückruf:

passport.use('local-login', new LocalStrategy({...},
    function(email, password, done) { 
        User.findOne({ 'local.email' :  email }, function(err, user) {
            if (err)
                return done(err);
            if (!user)
                return done(null, false, {message: 'Pas d\'utilisateur avec ce login.'});
            if (!user.validPassword(password))
                return done(null, false, {message: 'Oops! Mauvais password.'});
            return done(null, user);
        });

    }));

Und Routen:

app.get('/login', function(req, res) {
    console.log(req.flash('error'));
    res.send();
});

app.post('/login', passport.authenticate('local-login', {
    successRedirect : '/profile',
    failureRedirect : '/login',
    failureFlash : true
}));

Bearbeiten: 

Hier ein voll funktionsfähiges Beispiel: https://Gist.github.com/vesse/9e23ff1810089bed4426

16
vesse

Es ist eine alte Frage, aber ich hatte Schwierigkeiten, eine Antwort zu finden. Hoffentlich hilft das anderen. 


Ich denke, die Dokumentation ist ein wenig unvollständig, wenn es um die Verwendung von connect-flash geht. Man sagt: 

Hinweis: Die Verwendung von Flash-Nachrichten erfordert eine Funktion req.flash (). Express 2.x stellte diese Funktionalität zur Verfügung, wurde jedoch aus Express 3.x entfernt. Es wird empfohlen, Connect-Flash-Middleware zu verwenden, um diese Funktionalität bei der Verwendung von Express 3.x bereitzustellen.

Die Verwendung von req.flash im done()-Rückruf wird jedoch nicht erwähnt. Basierend auf dem scotch.io-Tutorial , müssen Sie tatsächlich sollte Aufruf req.flash() direkt im Callback aufrufen. Für mich geht das. 

// In your strategy
...
if (user) {
    return done( null, false, req.flash('loginMessage','Pas d\'utilisateur avec ce login.') );
...

Sie müssen natürlich passReqToCallback verwenden. Stellen Sie außerdem sicher, dass failureFlash auf true eingestellt ist. OP macht das schon richtig. 

Jetzt können Sie die Flash-Nachricht in der Route überprüfen. Beachten Sie, dass connect-flash ein Array von Nachrichten sendet. Das könnte das Problem von OP sein, wenn seine Vorlage eine Zeichenfolge erwartet. 

// In your routes
app.get('/login', function(req, res) {

    // Test flash messages in the console
    console.log( req.flash('loginMessage') ); // This returns an array
    console.log( req.flash('loginMessage')[0] ); // This returns a string

    // render the page and pass in any flash data if it exists
    res.render('login', {
        title: "Connexion",
        message: req.flash('loginMessage')[0] // Don't forget the index! 
    });

});

Wenn die Möglichkeit besteht, dass mehrere Anmeldungsnachrichten auf einer Seite angezeigt werden, übergeben Sie das gesamte req.flash('loginMessage')-Array und durchlaufen Sie es in Ihrer Vorlage. Unten ist ein Beispiel mit nunjucks


Protip:

Wenn Sie viele Routen mit Flash-Nachrichten haben, können Sie diese immer in einer Middleware-Route auf res.locals setzen. Dies wirkt sich nicht auf andere Einheimische aus, wie title. Hier ist meine Implementierung mit bootstrap alerts

In meiner Strategie: 

...
if (!user){
    return done( null, false, req.flash('danger','No account exists for that email.') );
}
...

In meinen routen.js: 

// Set flash messages
router.get('*', function(req,res,next){
    res.locals.successes = req.flash('success');
    res.locals.dangers = req.flash('danger');
    res.locals.warnings = req.flash('warning');
    next();
});

// Login route
app.get('/login', function(req, res) {
    res.render('login', { title: 'Login'}); 
});

In meiner Nunjucks-Basisvorlage:

<!--Messages-->
{% for danger in dangers %}
    <div class='header alert alert-danger alert-dismissible'>
        <strong><i class="fa fa-exclamation-circle"></i> ERROR:</strong> {{ danger | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
{% for warning in warnings %}
    <div class='header alert alert-warning alert-dismissible'>
        <strong><i class="fa fa-check-circle"></i> Warning:</strong> {{ warning | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
{% for success in successes %}
    <div class='header alert alert-success alert-dismissible'>
        <strong><i class="fa fa-check-circle"></i> Success!</strong> {{ success | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
5
Keith

Sie müssen badRequestMessage und failureFlash: true einstellen. 

So was:

passport.authenticate('login', {
    successRedirect : '/',
    failureRedirect : '/login',
    badRequestMessage : 'Missing username or password.',
    failureFlash: true
})
4
MrR0FL0LMA0MG

Nach monatelangem Ein- und Ausschalten des Versagens von Failure Flash habe ich endlich eine Lösung gefunden, die die FailureFlash-Funktion nicht verwendet. Ich habe im Grunde eine neue Route erstellt und die Flash-Nachricht gesendet.

app.post('/login',
  passport.authenticate('local', {failureRedirect: "/loginfailed"}),
  function(req, res) {
    if (!req.user.isActive){
      req.flash("success","Your account needs to be verified. Please check your email to verify your account");
      req.logout();
      res.redirect("back")
    }else{
      res.redirect("/");
    }
  });

  //Route to login page if user failed to login. I created this to allow flash messages and not interfere with regular login route
  app.get("/loginfailed", function(req, res){
    if (!req.user){
      req.flash("success", "Username or password is incorrect.");
      res.redirect("/login");
    }
  });
2
Joel

Wenn Felder fehlen, die für die Authentifizierung erforderlich sind, löst passport.authenticateden Strategie-Callback nicht aus, wie das OP zeigt.
Dies muss innerhalb der benutzerdefinierten Rückruf (Seite nach unten rollen) der Authentifizierungsfunktion mit dem Parameter info behandelt werden.
Falls der OP-Code wie folgt lautet: 

app.post('/login', function (req, res, next) { 
    passport.authenticate('local-login',
    {
      successRedirect: '/profile',
      failureRedirect: '/login',
      failureFlash: true,
    },
    function (error, user, info) {
      //This will print: 'Missing credentials'
      console.log(info.message);
      //So then OP could do something like 
      req.flash(info.message);
      //or in my case I didn't use flash and just did 
      if (info)
        res.status(400).send(info.message);
      ...
    })(req, res, next);
  });

Ich weiß, dass diese Frage alt ist, aber ich bin selbst auf dieses Thema gestoßen und sehe, dass es immer noch keine akzeptierte Antwort gibt. Des Weiteren denke ich, dass alle Antworten falsch interpretiert haben, wonach das OP eigentlich gefragt hat - eine Möglichkeit, auf badRequestMessage zuzugreifen.
PassportJS-Dokumente sind auch nicht sehr hilfreich:

Wenn die Authentifizierung fehlgeschlagen ist, wird der Benutzer auf "false" gesetzt. Wenn eine Ausnahme aufgetreten ist, wird err gesetzt. Es wird ein optionales Info-Argument übergeben, das zusätzliche Informationen enthält, die vom Überprüfungsrückruf der Strategie bereitgestellt werden.

Dies bedeutet eigentlich, dass der Parameter info als dritter Parameter aus Ihrer Strategie übergeben werden kann, wie folgt: done(error,user,info). Er erwähnt jedoch nicht, dass dieser Parameter standardmäßig verwendet wird, wenn Anmeldeinformationen fehlen. Insgesamt denke ich, dass PassportJS-Dokumente mit einigen Überholungen fertig werden könnten, da ihnen Details und Link zu nicht vorhandenen Beispielen fehlen.

Diese Antwort hat mir geholfen zu verstehen, dass die fehlende Anmeldeinformationsnachricht im Parameter info übergeben wird.

1
Dawid Stróżak

Ich hatte das gleiche Problem und ich habe es gelöst.
Ihre Erfolgs- und Misserfolgsmeldungsvariablen müssen mit dem von JS verwendeten Pass übereinstimmen. Nach dem Herumspielen stelle ich fest, dass Passport JS die Variable Erfolg verwendet, um den Erfolgsblitz anzuzeigen, und Fehler, um den Fehlerblitz anzuzeigen.

Als erstes können Sie eine super globale Variable wie diese in Ihrer app.js erstellen:

app.use(function(req, res, next) {
    res.locals.error = req.flash("error");
    res.locals.success = req.flash("success");
    next();
});

Dann benutze diese Variablen in deinem Tempel. Ich benutze EJS, so sieht es so aus:

<%if(error && error.length > 0){%>
    <div class="alert alert-danger"><%=error%></div>
<%}%>
    <%if(success && success.length > 0){%>
    <div class="alert alert-success"><%=success%></div>
<%}%>

Und schließlich sollte Ihr Passport-JS-Code so lauten:

router.post("/login",passport.authenticate("local", {
    successFlash : "Hey, Welcome back",
    successRedirect : "/mountains",
    failureFlash : true,
    failureRedirect :"/login"
    }), function(req, res){
});
0
youssouf diarra