Passport-local: Passport erlauben, Authentifizierungsfehlermeldungen über Variable zurückzugeben

Erstellt am 13. März 2012  ·  25Kommentare  ·  Quelle: jaredhanson/passport-local

Gemäß meiner Diskussion mit Jared über IM erfordert dieses Problem die Möglichkeit, Meldungen zu Authentifizierungsfehlern an die Funktion Passport.authenticate zurückzugeben. Vielleicht über:

Passport.authenticate(... func(err, user, info))

Das Problem besteht derzeit darin, dass es keine gute Möglichkeit gibt, eine Fehlermeldung von LocalStrategy zurückzugeben. Die einzige Möglichkeit besteht darin, entweder einen Serverfehler über 'err' zurückzugeben, was dazu führt, dass der Server einfach einen HTTP-Fehler 500 zurückgibt, oder einen Workaround zu verwenden, der das Benutzerobjekt verwendet, um die Fehlermeldung zurückzugeben.

Als Ergebnis sieht die done-Funktion wahrscheinlich eher so aus:

done(null, false, 'falsches Passwort')

Hier ist eine jsfiddle, bei der ich auf das Problem laufe. In diesem Beispiel gibt der Server für alle Fehler einfach einen 500-Fehler zurück – und ich möchte, dass er stattdessen eine Fehlerzeichenfolge zurückgibt:

http://jsfiddle.net/nd8LZ/1/

Hilfreichster Kommentar

@nickjj Ich stimme all deinen Punkten voll und ganz zu. Die im Verify-Callback auszuführenden Argumente werden genau an den benutzerdefinierten Authenticate-Callback übergeben. Wenn Sie also done() aufrufen:

done(null, false, { message: 'bad password' })

Das letzte Argument ist das info-Argument für den Authenticate-Callback:

app.get('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) { return next(err) }
    if (!user) {
      // *** Display message without using flash option
      // re-render the login form with a message
      return res.render('login', { message: info.message })
    }
    req.logIn(user, function(err) {
      if (err) { return next(err); }
      return res.redirect('/users/' + user.username);
    });
  })(req, res, next);
});

Von dort aus können Sie jede gewünschte Nachricht für die Anfrage anrufen (in diesem Fall render ) und alle Details verwenden, die in info .

Ich bin selbst nicht der größte Fan von Flash und bin mir bewusst, dass es in Express veraltet ist. (Ich vermute jedoch, dass Sie Middleware sehen werden, um es wieder einzufüllen.) Das heißt, wenn Sie die Option nie verwenden, hat es keine Auswirkungen auf Sie. Ich möchte nicht, dass das Passport-Framework agnostisch bleibt, und diese Optionen sind wirklich nur Abkürzungen für gängige Muster, Express oder andere. Flash-Nachrichten sind häufig genug, dass es sinnvoll ist, sie einzuschließen.

Ich bin offen für das Hinzufügen zusätzlicher Optionen, wenn es bessere Möglichkeiten für Nachrichten oder andere Standardvorgänge gibt. Abgesehen davon sind benutzerdefinierte Rückrufe der richtige Weg.

Erscheint Ihnen das in Ordnung?

Alle 25 Kommentare

So mache ich es im Grunde auch, außer dass ich json nicht zurückgebe. Solange die neue Funktion es Ihnen ermöglicht, eine beliebige Variable und nicht nur einen String zurückzugeben, und Sie diese Variable an res.xxx() in Ihrem Router übergeben können (damit Sie Authentifizierungsfehler einfach an Ihre Ansichten übergeben können), würde ich mich freuen .

Bearbeiten:
Jared, ich habe mir ein paar verwandte Probleme durchgelesen und du hast erwähnt, dass standardmäßig eine Nachricht angezeigt wird. Sprechen Sie über die Verwendung von Express' req.flash()? Ich würde mich davon fernhalten, da es in Express 3.0 veraltet ist:
https://github.com/visionmedia/express/wiki/Migration-from-2.x-to-3.x (überprüfen Sie den entfernten Abschnitt)

Passport-Local v0.1.2 behebt dieses Problem. Weitere Informationen finden Sie in diesem Kommentar zu Passport-Ausgabe #12 .

Beachten Sie auch, dass die obige Syntax funktioniert

done(null, false, 'bad password')

aber die bevorzugte Form ist die Verwendung eines Options-Hashs

done(null, false, { message: 'bad password' })

da es für die zukünftige Verwendung erweiterbar ist.

Können Sie ändern, wie die Nachricht zurückgegeben wird? Die Verwendung von req.flash() ist albern. Vielleicht können Sie einfach einen Fehler machen und den Entwickler entscheiden lassen, wie er damit umgeht?

@nickjj Ich stimme all deinen Punkten voll und ganz zu. Die im Verify-Callback auszuführenden Argumente werden genau an den benutzerdefinierten Authenticate-Callback übergeben. Wenn Sie also done() aufrufen:

done(null, false, { message: 'bad password' })

Das letzte Argument ist das info-Argument für den Authenticate-Callback:

app.get('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) { return next(err) }
    if (!user) {
      // *** Display message without using flash option
      // re-render the login form with a message
      return res.render('login', { message: info.message })
    }
    req.logIn(user, function(err) {
      if (err) { return next(err); }
      return res.redirect('/users/' + user.username);
    });
  })(req, res, next);
});

Von dort aus können Sie jede gewünschte Nachricht für die Anfrage anrufen (in diesem Fall render ) und alle Details verwenden, die in info .

Ich bin selbst nicht der größte Fan von Flash und bin mir bewusst, dass es in Express veraltet ist. (Ich vermute jedoch, dass Sie Middleware sehen werden, um es wieder einzufüllen.) Das heißt, wenn Sie die Option nie verwenden, hat es keine Auswirkungen auf Sie. Ich möchte nicht, dass das Passport-Framework agnostisch bleibt, und diese Optionen sind wirklich nur Abkürzungen für gängige Muster, Express oder andere. Flash-Nachrichten sind häufig genug, dass es sinnvoll ist, sie einzuschließen.

Ich bin offen für das Hinzufügen zusätzlicher Optionen, wenn es bessere Möglichkeiten für Nachrichten oder andere Standardvorgänge gibt. Abgesehen davon sind benutzerdefinierte Rückrufe der richtige Weg.

Erscheint Ihnen das in Ordnung?

In 3.x verfügt Express jetzt über eine ansichtsspezifische Middleware, mit der Sie mit app.locals.use(...) in alle Ihre Ansichten Site-weite Variablen einfügen können.

Der zu verwendende Workflow (zum Beispiel) ist:

In app.js...

app.locals.use(function(req, res) {
   // Expose "error" and "message" to all views that are rendered.
   res.locals.error = req.session.error || '';
   res.locals.message = req.session.message || '';

   // Remove them so they're not displayed on subsequent renders.
   delete req.session.error;
   delete req.session.message;
});

Quelle: https://github.com/visionmedia/express/blob/master/examples/blog/app.js#L32

In einer Ihrer Routen würden Sie einfach req.session.message = info.message mit dem neuen Setup ausführen, aber die Route hat keinen Zugriff auf info. Wollen Sie damit sagen, dass wir immer noch einen benutzerdefinierten Callback verwenden müssen, um die Nachricht auf eine Weise auszugeben, die nicht von req.flash() abhängt?

Ich bin immer noch ziemlich neu in Node/Express/JS im Allgemeinen, sorry, wenn meine Fragen zurückgeblieben sind.

Ihre Fragen sind großartig. Sie haben das Geschehen gut im Griff.

Wollen Sie damit sagen, dass wir immer noch einen benutzerdefinierten Callback verwenden müssen, um die Nachricht auf eine Weise auszugeben, die nicht von req.flash() abhängt?

Wenn Sie Express 3 verwenden, ist das vorerst ja. So würde das funktionieren:

app.get('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) { return next(err) }
    if (!user) {
      // *** Display message using Express 3 locals
      req.session.message = info.message;
      return res.redirect('login');
    }
    req.logIn(user, function(err) {
      if (err) { return next(err); }
      return res.redirect('/users/' + user.username);
    });
  })(req, res, next);
});

Nun, ich gebe zu, das ist ein ziemlicher zusätzlicher Aufwand, nur um eine einfache Nachricht zu setzen. Hier meine Philosophie dazu:

Passport ist als generische Authentifizierungs-Middleware konzipiert, die in jedem App-Framework verwendet werden kann. Dies geschieht, indem die Kontrolle für Aufgaben, die logischerweise in der Verantwortung der Anwendung liegen, an die Anwendung zurückdelegiert wird. Rendern, Umleiten, Messaging usw. fallen alle in diese Kategorie, sodass alle benutzerdefinierten Arbeiten, die für Ihre Anwendung oder das zugrunde liegende Framework Ihrer Anwendung eindeutig sind, im Rückruf behandelt werden sollten.

Wenn in Frameworks gemeinsame Muster auftreten, bietet Passport Optionen, um diese Muster zu vereinfachen (also das Vorhandensein von failureRedirect, failureFlash usw.). Flash-Messaging ist ein solches allgemeines Muster, daher ist es nützlich, wenn Passport es unterstützt.

Ich persönlich befürworte die Entscheidung von req.flash() aus Express zu entfernen. Ich denke, diese Entscheidung wird dazu führen, dass diese Funktionalität von einer Middleware auf niedrigerer Ebene übernommen wird, die in Frameworks eingefügt und wiederverwendet werden kann. (Ich arbeite gerade an einer solchen Middleware; ich werde hier aktualisieren, wenn sie veröffentlicht wird).

Wenn ein anderes Messaging-Muster auftaucht, das für eine Vielzahl von Apps nützlich ist, erwäge ich, Passport eine integrierte Option hinzuzufügen (z. B. failureMessage oder so). Aber bis dahin können Sie mit dem benutzerdefinierten Rückruf alles erreichen, was Ihre App benötigt.

OK danke. Das klärt so ziemlich alles auf. Ich freue mich darauf, bei Passport zu bleiben.

@nickjj Ich habe gerade Connect-Flash veröffentlicht, eine Middleware, die Sie in eine Express 3.x-App
https://github.com/jaredhanson/connect-flash

Wenn Sie es verwenden, können Sie auch die Optionen successFlash und failureFlash mit passport.authenticate() .

Ich bin auch anti req.flash (), also werde ich das wahrscheinlich nicht verwenden, aber ich denke, viele Leute werden es genießen, wenn ich req.flash () im Beispiel Express gesehen habe Apps. Nebenbei bemerkt ist Ihre Connect-Flash-Middleware ein wirklich gutes/einfaches Beispiel dafür, wie man "echte" Middleware in Express richtig implementiert.

Wenn die Authentifizierung fehlschlägt, wird immer "Fehlende Anmeldeinformationen" angezeigt, die in der Pass-Bibliothek selbst fest codiert zu sein scheinen.

Wenn ich es überschreibe, indem ich dem benutzerdefinierten Callback-Beispiel folge und info.message ausgebe, wird immer noch "Fehlende Anmeldeinformationen" gemeldet, als würde alles in meinem Passport.use(LocalStrategy...) ignoriert.

Außerdem scheine ich immer "Fehler beim Deserialisieren des Benutzers außerhalb der Sitzung" zu erhalten, wenn ich mich erfolgreich anmelde. Ich habe deine Beispiele und Tutorials befolgt. Ich habe versucht, sowohl den Standard-Express-Sitzungsspeicher als auch Redis zu verwenden. Die Seite, an der ich arbeite, hat funktioniert, bevor ich Pass/Passport-Local auf die neueste Version aktualisiert habe. Irgendeine Idee, was los ist? Soll ich den ganzen Code posten?

@nickjj Hast du die Ursache deiner Probleme herausgefunden?

Bei mir funktioniert alles gut, und wenn Passport im Großen und Ganzen kaputt wäre, würde ich von anderen Leuten davon hören. Da ich es nicht bin, vermute ich, dass es etwas Spezifisches für Ihr Setup ist. Posten Sie einen Kern mit Code, der das Problem reproduziert, und lassen Sie mich wissen, ob ich Ihnen helfen kann.

Ja, ich habe eine Version von Express 3.x verwendet, die eine fehlerhafte Version von Connect hatte (ich glaube, es war 2.0.0 oder 2.0.1). Tj hat kürzlich einen Fehler in Connect behoben, der etwas dazu beigetragen hat, Sitzungen zu beheben. Nach dem Upgrade auf den neuesten Connect Passport funktioniert es wieder.

Ich habe gerade Ihre Beispiel-App in Ihrem Passport-Local-Repository auf eine Express 3.x-App portiert und es funktioniert. Es funktioniert sowohl mit dem Standard-Connect-Sitzungsspeicher als auch mit einem Redis-Sitzungsspeicher (Connect-Redis-Modul).

Das andere Problem, das ich hatte, war meine Schuld. Als ich Ihre Scheindaten geändert habe, um auf eine echte Datenbank (mongodb) zuzugreifen, gab es Probleme beim Deserialisieren der Daten, weil ich die ID von deserializeUser () direkt als Zeichenfolge an mein mongo findOne () übergeben habe, aber sie muss in eine konvertiert werden ObjectID zuerst.

Ich bin immer noch nicht zu 100% mit diesem neuen Stack vertraut. Als ich also Ihre Tutorial-Anleitung las und mitmachte, zeigte sie Ihnen, wie Sie findOne () während eines deserializeUser () verwenden. Ich dachte mir nur "oh ok, er verwendet findOne (), also muss er auch Mongo verwenden und übergibt die ID einfach ohne Probleme".

Alles scheint jetzt gut zu sein, um Pass-Local zu verwenden.

Das eigentliche Problem ist jetzt, wenn ich einen benutzerdefinierten Rückruf verwende, um benutzerdefinierte Nachrichten mit Express 3.x zu erhalten, und ich meine eigene lokale Strategie definieren muss, je nachdem, was die Daten überprüft, warum verwende ich dann Passport-Local?

Warum nicht einfach eine Standardfunktion verwenden, um sich bei meiner Datenbank zu authentifizieren, und noch weniger Code in meine Route schreiben, um zu erkennen, ob es sich um einen guten oder schlechten Anmeldeversuch handelt?

Ich möchte auf jeden Fall Passport-xxx verwenden, wenn ich mich dafür entscheide, sich mit Diensten von Drittanbietern anzumelden, aber ich bin mir nicht sicher, warum sich local (mit benutzerdefinierten Rückrufen) überhaupt lohnt? Können Sie mir die Verwendung verkaufen?

Die einzige gute Möglichkeit, eine Fehlermeldung zu platzieren, die ich gefunden habe, war dieser Weg.

app.post("/login", passport.authenticate("local", 
  { successRedirect: "/", 
    failureRedirect: "/login", 
    failureMessage: "Invalid username or password" }));

Dann wird die Nachricht in req.session.messages gespeichert, einem Array, das einzige Problem ist, dass es statisch ist und ich denke, es gibt einen Fehler, bei dem das Array die gleiche Nachricht immer wieder verkettet. Dies wurde durch den folgenden Code behoben, nachdem die Antwort gerendert wurde: P

res.render("login", { login_errors: req.session.messages || [] });
req.session.messages = [];

Was bringt es, hier failureFlash ? Die Art und Weise, wie die Fehler angezeigt werden, ist von Fall zu Fall unterschiedlich, und warum können wir nicht einfach eine Rückruffunktion verwenden, um sie so zu behandeln, wie es sinnvoll ist?
Zitat aus dem Doc:

Wenn Sie die Option failureFlash auf true setzen, wird Passport angewiesen, eine Fehlermeldung zu blinken, indem die Nachricht verwendet wird, die durch den Verify-Callback der Strategie gegeben wird

  • erstmal möchte ich keine Fehlermeldung flashen
  • Zweitens scheint Passport nicht einmal eine Nachricht zu flashen, ohne eine andere Abhängigkeit zu installieren? und ich denke, ich installiere es wahrscheinlich nur, um eine Login-Fehlermeldung zu blinken.

Wie kann ich mehrere Nachrichten zurückgeben, zum Beispiel wenn die Anmeldung fehlschlägt Ich möchte eine entsprechende Fehlermeldung und die eingegebene E-Mail zurückgeben, damit ich die E-Mail-Eingabe erneut eingeben kann.

done(null, false, { message: 'bad password', email: emailVariable});

Das funktioniert nicht.

@silps

req.session.messages = [];

Was soll das? Sitzungsnachrichten zurücksetzen? Ich glaube nicht, dass das funktionieren wird.

Ich zeige nur die letzte der Nachrichten im Array an. So in EJS:

<% if (login_errors.length > 0) { %> <%= login_errors[login_errors.length -1]%><br> <% }%>

`

@GEOLYTIX ja, dieser Ansatz wird auch funktionieren, es ist nur so, dass jedes Mal, wenn ich die Site aktualisiert habe, eine weitere der gleichen Nachricht im Array war, also habe ich sie jedes Mal bereinigt, aber dein Weg funktioniert auch

@jaredhanson Ich habe eine Programmiersituation, in der ich wissen möchte, ob der Benutzer existiert, da ich unterschiedliche Anwendungsaktionen durchführen werde, je nachdem, ob sie bereits vorhanden sind oder nicht. Ich wünschte also, es gäbe eine Passport.js-URL, um Unterstützung für /exist bereitzustellen, wobei ich einen Benutzernamen angebe und Passport eine 0 oder 1 zurückgibt. Ok, das ist nicht verfügbar. Also poste ich auf /login mit einem Benutzernamen und einem Passwort. Das Passwort kann alles sein. In der Passport.use-Funktion, wenn der Benutzername nicht gefunden wird, kehre ich zurück-

    if (!user) {
        return done(null, false, { "rtnCode": 1 });    // user not found, user does not exist
    }

Wenn der Benutzer gefunden wurde, gebe ich stattdessen with- done(null, false, { "rtnCode": 2 })

                  // check if password is valid     
                   if (!validatePassword(password, passHash)) {
                        //return done(null, false);                         // as per passport.js example
                        return done(null, false, { "rtnCode": 2 });
                   }

Jetzt in Passport.authenticate('local-login', function(err, user, info)
info enthält entweder { "rtnCode": 1 } oder { "rtnCode": 2 }

Das bedeutet, dass in

        // incorrect Username or password
        if (!user) {
                //return res.redirect('/auth/login');     // as per passport.js example
            return res.json(info);               // rtn code- { "rtnCode": 1 } or { "rtnCode": 2 }
                                                                                 // user does not exist, or does exist
            }

Jetzt kann ich einen bestimmten Rückgabecode an meine App senden, der mir die Information gibt, ob der Benutzer existiert oder nicht. Hier ist das Problem. Auch wenn der Ausführungsablauf mit einem Return endet und somit nie ausgeführt wird.
req.logIn(user, function(err)

Wenn ich jetzt ein Ajax-GET mit dem Benutzernamen, von dem ich weiß, dass es existiert, und einem falschen Passwort ausstelle, komme ich rein und kann die Datenbank lesen.

Das ist sehr unerwartet, da ich req.logIn() nie ausgegeben habe. Und natürlich ist es ein Sicherheitsproblem. Der Punkt hier ist, dass ich bei einem "schlechten" Benutzernamen oder Passwort in meinem /login keine Webseite aufrufen möchte, sondern eine andere Vorgehensweise im Code ergreifen möchte.

```

Ich verwende angle4 mit Passport, kann ich trotzdem res ohne die Umleitungsfunktion im Passport erhalten?

So kommt es vor, dass der Reisepass eine Funktion hat, um das in ihrer Dokumentation zu lösen,
Ein benutzerdefinierter Rückruf für einen Abschnittsaufruf hat mir geholfen, das Problem zu lösen

//Beispiele für Routen
router.get('/', function (req, res, next) { res.render('administrator/login', { title: 'Login Admin', alerts: req.flash() }); });

//ejs-Handler
`<% if (alerts.error) { %>
<% alerts.error.forEach(function(msg) { %>

<%}); %>
<% } %>
<% if (alerts.success) { %>
<% alerts.success.forEach(function(msg) { %>

<%}); %>
<% } %>`

danke @jaredhanson !! du hast mich gerettet hhhh

Im Web herrscht reichlich Verwirrung darüber, wie die als info an den Verify-Callback übergebenen Informationen verwendet werden, zB hier . Das Hauptproblem, das die Leute verwirrt, ist, dass Folgendes nicht funktioniert:

const express = require('express');
const app = express();
const passport = require('passport');
const { Strategy: LocalStrategy } = require('passport-local');

app.use(express.json());

passport.use(new LocalStrategy({ failWithError: true },
    (username, password, cb) => {
        console.log(`executing local strategy ${username} and ${password}`);
        // user expects 'message: `user not found`' to be forwarded to the 
        // client along with the 401 Unauthorized response
        cb(null, false, { message: `user not found` });
    })
);

app.post('/login', passport.authenticate('local', { session: false }))

app.listen(3000, () => {
  console.log(`Run:`);
  console.log(`curl -v --header "Content-Type: application/json" -d '{"username":"dauser","password":"dapass"}' -X POST http://localhost:3000/login`);
});

Mit anderen Worten, die Standardaktion der Passport-Local-Middleware besteht darin, zu ignorieren, was an verify(null, false, info) im info Slot übergeben wird, es sei denn, der Benutzer ruft die von Passport.authenticate() erzeugte Middleware-Funktion auf. direkt über passport.authenticate(...)(req, res, next) und sendet die Antwort selbst. Der Kommentar von @jaredhanson aus dem Jahr 2012 scheint darauf hinzudeuten, dass dies zumindest ab 2012 das gewünschte Verhalten ist.

Dies verwirrt Benutzer, die erwarten, Passport.authenticate() als Middleware vor ihren eigenen Anforderungshandlern zu verwenden.

Erschwerend kommt hinzu, dass Passport.authenticate mehrere Optionen nutzt, von denen keine das gewünschte Verhalten hat, die Informationen an den Client weiterzugeben.

Ich frage mich, ob das überarbeitet werden könnte/sollte. Es scheint mir ein perfekt etabliertes Muster zu sein, das als info Objekt standardmäßig als JSON-Body in der Antwort an den Client zu senden. Wenn nicht standardmäßig, könnte eine Option sendErrorInfoToClient bereitgestellt werden. Oder irre ich mich und es ist die beste Vorgehensweise, nichts mit einer 401 zu senden?

Es ist 7 Jahre her und ich glaube nicht, dass diese Funktion jemals implementiert werden wird. Sie können jedoch eine Problemumgehung erstellen, um die Strategie als Middleware-Funktion beizubehalten und dennoch benutzerdefinierte Nachrichten an den Client zurücksenden zu lassen (und die Funktion kann auf Wunsch für andere Controller wiederverwendet werden):

Express-Route:

const { login } = require("../controllers/login)";
const localLogin = require("../services/strategies)";

module.exports = app => {
  app.post("/api/login", localLogin, login);
};

Pass-Middleware

const bcrypt = require("bcryptjs");
const passport = require("passport");

// reusable func to return errors to client
const sendError = (err, res) => res.status(400).json({ err: err.toString() });

const badCredentials = "There was a problem with your login credentials. Please make sure your username and password are correct.";

passport.use(
  "local-login",
  new LocalStrategy(
    {
      usernameField: "email",
      passwordField: "password",
      passReqToCallback: true,
    },
    async (req, email, password, done) => {
      try {
        // check to see if the user already exists
        const existingUser = await User.findOne({ email });
        if (!existingUser) return done(badCredentials, false);

        // compare password to existingUser password
        const validPassword = await bcrypt.compare(
          password,
          existingUser.password,
        );
        if (!validPassword) return done(badCredentials, false);

        return done(null, existingUser);
      } catch (err) {
        return done(err, false);
      }
    },
  ),
);

// exporting a wrapper function that will invoke the passport.authenticate method
module.exports = (req, res, next) => {
  const { email, password } = req.body;

  // if email or password are missing, send an error back to the client
  if (!email || !password) return sendError(badCredentials, res);

  passport.authenticate("local-login", (err, user) => {
    // if an error was returned by the strategy, send it to the client
    if (err) return sendError(err, res);

    // manually setting the logged in user to req.user 
    // optionally, you can set it to "req.session" if you're using some sort of session
    req.user = user;

   // invoking "next" to continue to the controller
    next();
  })(req, res, next);
};

// alternatively, you can use a promise as well...
module.exports = async (req, res, next) => {
  const { email, password } = req.body;

  if (!email || !password) return sendError(badCredentials, res);

  try {
    const user = await new Promise((resovle, reject) => {
      passport.authenticate("local-login", (err, user) =>
        err ? reject(err) : resolve(user),
      )(req, res, next);
    });

    req.user = user;
    next();
  } catch (err) {
    return sendError(err, res);
  }
}

Express-Controller

exports.login = (req, res, done) => {
  // do any additional aggregation with "req.user"

  res.status(202).json({ ...req.user, ...etc });
};

Der größte Vorteil dieses Ansatzes besteht darin, dass Sie die Authentifizierungsstrategie jetzt getrennt vom Controller testen können. Der Nachteil ist, dass dies immer noch keine Passport-Sitzungen unterstützt (was bei der Verwendung benutzerdefinierter Rückrufe anscheinend nicht unterstützt wird).

Es ist 7 Jahre her und ich glaube nicht, dass diese Funktion jemals implementiert werden wird. Sie können jedoch eine Problemumgehung erstellen, um die Strategie als Middleware-Funktion beizubehalten und dennoch benutzerdefinierte Nachrichten an den Client zurücksenden zu lassen (und die Funktion kann auf Wunsch für andere Controller wiederverwendet werden):

Express-Route:

const { login } = require("../controllers/login)";
const localLogin = require("../services/strategies)";

module.exports = app => {
  app.post("/api/login", localLogin, login);
};

Pass-Middleware

const bcrypt = require("bcryptjs");
const passport = require("passport");

// reusable func to return errors to client
const sendError = (err, res) => res.status(400).json({ err: err.toString() });

const badCredentials = "There was a problem with your login credentials. Please make sure your username and password are correct.";

passport.use(
  "local-login",
  new LocalStrategy(
    {
      usernameField: "email",
      passwordField: "password",
      passReqToCallback: true,
    },
    async (req, email, password, done) => {
      try {
        // check to see if the user already exists
        const existingUser = await User.findOne({ email });
        if (!existingUser) return done(badCredentials, false);

        // compare password to existingUser password
        const validPassword = await bcrypt.compare(
          password,
          existingUser.password,
        );
        if (!validPassword) return done(badCredentials, false);

        return done(null, existingUser);
      } catch (err) {
        return done(err, false);
      }
    },
  ),
);

// exporting a wrapper function that will invoke the passport.authenticate method
module.exports = (req, res, next) => {
  const { email, password } = req.body;

  // if email or password are missing, send an error back to the client
  if (!email || !password) return sendError(badCredentials, res);

  passport.authenticate("local-login", (err, user) => {
    // if an error was returned by the strategy, send it to the client
    if (err) return sendError(err, res);

    // manually setting the logged in user to req.user 
    // optionally, you can set it to "req.session" if you're using some sort of session
    req.user = user;

   // invoking "next" to continue to the controller
    next();
  })(req, res, next);
};

// alternatively, you can use a promise as well...
module.exports = async (req, res, next) => {
  const { email, password } = req.body;

  if (!email || !password) return sendError(badCredentials, res);

  try {
    const user = await new Promise((resovle, reject) => {
      passport.authenticate("local-login", (err, user) =>
        err ? reject(err) : resolve(user),
      )(req, res, next);
    });

    req.user = user;
    next();
  } catch (err) {
    return sendError(err, res);
  }
}

Express-Controller

exports.login = (req, res, done) => {
  // do any additional aggregation with "req.user"

  res.status(202).json({ ...req.user, ...etc });
};

Der größte Vorteil dieses Ansatzes besteht darin, dass Sie die Authentifizierungsstrategie jetzt getrennt vom Controller testen können. Der Nachteil ist, dass dies immer noch keine Passport-Sitzungen unterstützt (was bei der Verwendung benutzerdefinierter Rückrufe anscheinend nicht unterstützt wird).

Vielen Dank, das war sehr nützlich und sauber.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen
bleepcoder.com verwendet öffentlich lizenzierte GitHub-Informationen, um Entwicklern auf der ganzen Welt Lösungen für ihre Probleme anzubieten. Wir sind weder mit GitHub, Inc. noch mit anderen Entwicklern affiliiert, die GitHub für ihre Projekte verwenden. Wir hosten keine der Videos oder Bilder auf unseren Servern. Alle Rechte gehören ihren jeweiligen Eigentümern.
Quelle für diese Seite: Quelle

Beliebte Programmiersprachen
Beliebte GitHub Projekte
Mehr GitHub Projekte

© 2024 bleepcoder.com - Contact
Made with in the Dominican Republic.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.