Benutzerauthentifizierungsbibliotheken für node.js?

274

Gibt es vorhandene Benutzerauthentifizierungsbibliotheken für node.js? Insbesondere suche ich nach etwas, das die Kennwortauthentifizierung für einen Benutzer (unter Verwendung einer benutzerdefinierten Backend-Authentifizierungsdatenbank) durchführen und diesen Benutzer einer Sitzung zuordnen kann.

Bevor ich eine Auth-Bibliothek schrieb, dachte ich, ich würde sehen, ob die Leute von vorhandenen Bibliotheken wussten. Über eine Google-Suche konnte nichts Offensichtliches gefunden werden.

-Shreyas

Shreddd
quelle
Zur Durchsuchbarkeit: Etwas, das omniauth(Schienen) oder Python entspricht social-auth. PHP-Benutzer (und andere gängige Webserversprachen) können auch ihre Entsprechung hinzufügen.
Forivall

Antworten:

233

Wenn Sie nach einem Authentifizierungsframework für Connect oder Express suchen, sollten Sie Passport untersuchen: https://github.com/jaredhanson/passport

(Offenlegung: Ich bin der Entwickler von Passport)

Ich habe Passport entwickelt, nachdem ich sowohl connect-auth als auch everyauth untersucht hatte. Obwohl sie beide großartige Module sind, entsprachen sie nicht meinen Bedürfnissen. Ich wollte etwas, das leichter und unauffälliger ist.

Passport ist in separate Module unterteilt, sodass Sie nur das verwenden können, was Sie benötigen (OAuth, nur bei Bedarf). Passport stellt auch keine Routen in Ihre Anwendung bereit, sodass Sie flexibel entscheiden können, wann und wo Sie eine Authentifizierung wünschen, und Hooks steuern können, was passiert, wenn die Authentifizierung erfolgreich ist oder fehlschlägt.

Im Folgenden finden Sie beispielsweise den zweistufigen Prozess zum Einrichten der formularbasierten Authentifizierung (Benutzername und Kennwort):

passport.use(new LocalStrategy(
  function(username, password, done) {
    // Find the user from your DB (MongoDB, CouchDB, other...)
    User.findOne({ username: username, password: password }, function (err, user) {
      done(err, user);
    });
  }
));

app.post('/login', 
  passport.authenticate('local', { failureRedirect: '/login' }),
  function(req, res) {
    // Authentication successful. Redirect home.
    res.redirect('/');
  });

Für die Authentifizierung über Facebook, Twitter usw. stehen zusätzliche Strategien zur Verfügung. Bei Bedarf können benutzerdefinierte Strategien eingebunden werden.

Jared Hanson
quelle
Unter allen Authentifizierungspaketen für den Knoten habe ich den Pass ausgewählt. Es ist gut dokumentiert und einfach zu bedienen und unterstützt mehr Strategien.
Techniker
Ich verwende derzeit einen Reisepass für einen Prototyp und empfehle ihn nicht, da er nicht gewartet zu werden scheint und das Design nicht sehr gut ist. Beispielsweise werden Sie gezwungen, Connect-Flash zu verwenden, wenn Sie einfach req.session.messages verwenden könnten, und das auf der Website beworbene Pass-Google ist veraltet, da es Google OpenId verwendet, das veraltet ist, und es gibt keinen Link zu Passport- google-oauth das sollte es ersetzen. Dies ist auch die Signatur eines Rückrufs nach der Authentifizierung:done(null,false,{ message:'Incorrect username.' }) Es ist schrecklich, da wir nicht wissen, was all diese Parameter sind.
Eloone
1
@eloone Ich muss die Dokumente aktualisieren, um auf die neuen Authentifizierungsmethoden zu verweisen, die Google jetzt bevorzugt. Wie Sie bereits erwähnt haben, gibt es Unterstützung für diese und sie funktionieren gut. Bei Designfragen zwingt Sie der Reisepass nicht zur Verwendung von Connect-Flash, und die von Ihnen genannten Argumente sind im Handbuch dokumentiert. Wenn Sie Hilfe beim Verständnis benötigen, gibt es Foren, in denen Personen Ihre Fragen beantworten können.
Jared Hanson
Nicht umsonst - aber ich habe gerade Passport eingesteckt (siehe das mitgelieferte Beispiel). Super einfach! Mir ist klar, dass seit den letzten Kommentaren einige Jahre vergangen sind. Ich würde jedem empfehlen, einen Blick darauf zu werfen.
19.
89

Sitzung + Wenn

Ich denke, der Grund, warum Sie nicht viele gute Bibliotheken gefunden haben, ist, dass die Verwendung einer Bibliothek zur Authentifizierung größtenteils überentwickelt ist.

Was Sie suchen, ist nur ein Sitzungsordner :) Eine Sitzung mit:

if login and user == xxx and pwd == xxx 
   then store an authenticated=true into the session 
if logout destroy session

das ist es.


Ich bin mit Ihrer Schlussfolgerung nicht einverstanden, dass das Connect-Auth-Plugin der richtige Weg ist.

Ich verwende auch connect, aber ich verwende connect-auth aus zwei Gründen nicht:

  1. IMHO bricht Connect-Auth die sehr leistungsfähige und einfach zu lesende Zwiebelring-Architektur von Connect. Ein No-Go - meine Meinung :). Einen sehr guten und kurzen Artikel über die Funktionsweise von connect und die Zwiebelringidee finden Sie hier .

  2. Wenn Sie - wie geschrieben - nur ein Basis- oder http-Login mit Datenbank oder Datei verwenden möchten. Connect-Auth ist viel zu groß. Es ist mehr für Sachen wie OAuth 1.0, OAuth 2.0 & Co.


Eine sehr einfache Authentifizierung mit Connect

(Es ist vollständig. Führen Sie es einfach zum Testen aus, aber wenn Sie es in der Produktion verwenden möchten, stellen Sie sicher, dass Sie https verwenden.) (Und um REST-Prinzip-konform zu sein, sollten Sie eine POST-Anfrage anstelle einer GET-Anfrage b / c verwenden du änderst einen Zustand :)

var connect = require('connect');
var urlparser = require('url');

var authCheck = function (req, res, next) {
    url = req.urlp = urlparser.parse(req.url, true);

    // ####
    // Logout
    if ( url.pathname == "/logout" ) {
      req.session.destroy();
    }

    // ####
    // Is User already validated?
    if (req.session && req.session.auth == true) {
      next(); // stop here and pass to the next onion ring of connect
      return;
    }

    // ########
    // Auth - Replace this example with your Database, Auth-File or other things
    // If Database, you need a Async callback...
    if ( url.pathname == "/login" && 
         url.query.name == "max" && 
         url.query.pwd == "herewego"  ) {
      req.session.auth = true;
      next();
      return;
    }

    // ####
    // This user is not authorized. Stop talking to him.
    res.writeHead(403);
    res.end('Sorry you are not authorized.\n\nFor a login use: /login?name=max&pwd=herewego');
    return;
}

var helloWorldContent = function (req, res, next) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('authorized. Walk around :) or use /logout to leave\n\nYou are currently at '+req.urlp.pathname);
}

var server = connect.createServer(
      connect.logger({ format: ':method :url' }),
      connect.cookieParser(),
      connect.session({ secret: 'foobar' }),
      connect.bodyParser(),
      authCheck,
      helloWorldContent
);

server.listen(3000);

HINWEIS

Ich habe diese Erklärung vor über einem Jahr geschrieben und habe derzeit keine aktiven Knotenprojekte. Es kann also zu API-Änderungen in Express kommen. Bitte fügen Sie einen Kommentar hinzu, wenn ich etwas ändern sollte.

nivoc
quelle
Warum bricht connect-auth das Zwiebel- / Schichtmuster? liegt es daran, dass next () nicht verwendet wird? Kann es?
jpstrikesback
3
Ja. Es muss next () verwenden, da dies die Idee hinter connect ist. Connect hat eine Schichtarchitektur / Form der Codestruktur. Und jede Schicht kann die Anforderungsausführung stoppen, indem sie nicht next () aufruft. Wenn es sich um Authentifizierung handelt: Eine Authentifizierungsschicht prüft, ob der Benutzer über die richtigen Berechtigungen verfügt. Wenn alles in Ordnung ist, ruft die Ebene next () auf. Wenn nicht, generiert diese Auth-Schicht einen Fehler und ruft next () nicht auf.
Matthias
Mann, genau das habe ich gesucht. connect-auth gab mir ein bisschen Verdauungsstörungen. Ich habe mich gerade zum ersten Mal in meiner App angemeldet. vielen Dank.
Andy Ray
7
Dies hilft immer noch nicht bei der Beantwortung der Verbindung zu einem Datenbank-Backend (vorzugsweise mit verschlüsselten Kennwörtern). Ich freue mich über Ihren Kommentar, dass diese eine Bibliothek überentwickelt ist, aber es gibt sicherlich eine, die es nicht ist. Wenn ich mein eigenes Authentifizierungssystem schreiben wollte, hätte ich Struts in Java verwendet. Genau wie beim OP möchte ich wissen, welche Plugins das für mich in einer Codezeile erledigen.
Hendrixski
4
tolle Antwort Nivoc. Funktioniert nicht mit den neuesten Versionen von connect tho. Ich musste ... cookieDecoder () -> cookieParser () und bodyDecoder () -> bodyParser () ändern und den nächsten () Aufruf von der Funktion helloWorldContent entfernen, da ich die Fehlermeldung "Header können nicht nach ihnen gesetzt werden" erhielt werden gesendet '
Michael Dausmann
26

Das Connect-Auth-Plugin zur Connect-Middleware ist genau das, was ich brauche: http://wiki.github.com/ciaranj/connect-auth/creating-a-form-based-strategy

Ich verwende Express [ http://expressjs.com ], daher passt das Connect-Plugin sehr gut, da Express von Connect in eine Unterklasse (ok - Prototyp) unterteilt ist

Shreddd
quelle
1
Hey, hast du ein Beispiel dafür, was du getan hast? Wenn Sie lediglich connect-auth benötigen und ".authenticate" für "req" aufrufen, wird "TypeError: Object # hat für mich keine Methode" authenticate "" zurückgegeben.
Mischa Reyzlin
1
IMHO Dieses Plugin ist viel zu schwer für eine einfache http-Authentifizierung
Matthias
Und dieses Plugin arbeitet gegen die Connect-Zwiebelring-Architektur
Matthias
14

Ich suchte im Grunde das Gleiche. Insbesondere wollte ich Folgendes:

  1. Verwendung von express.js, das die Middleware-Funktionen von Connect einschließt
  2. "Formularbasierte" Authentifizierung
  3. Detaillierte Kontrolle darüber, welche Routen authentifiziert werden
  4. Ein Datenbank-Backend für Benutzer / Passwörter
  5. Verwenden Sie Sitzungen

Am Ende habe ich meine eigene Middleware-Funktion erstellt check_auth, die ich als Argument für jede Route übergeben möchte, die authentifiziert werden soll. check_authÜberprüft lediglich die Sitzung. Wenn der Benutzer nicht angemeldet ist, leitet er ihn wie folgt auf die Anmeldeseite weiter:

function check_auth(req, res, next) {

  //  if the user isn't logged in, redirect them to a login page
  if(!req.session.login) {
    res.redirect("/login");
    return; // the buck stops here... we do not call next(), because
            // we don't want to proceed; instead we want to show a login page
  }

  //  the user is logged in, so call next()
  next();
}

Dann stelle ich für jede Route sicher, dass diese Funktion als Middleware übergeben wird. Beispielsweise:

app.get('/tasks', check_auth, function(req, res) {
    // snip
});

Schließlich müssen wir den Anmeldevorgang tatsächlich abwickeln. Das ist unkompliziert:

app.get('/login', function(req, res) {
  res.render("login", {layout:false});
});

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

  // here, I'm using mongoose.js to search for the user in mongodb
  var user_query = UserModel.findOne({email:req.body.email}, function(err, user){
    if(err) {
      res.render("login", {layout:false, locals:{ error:err } });
      return;
    }

    if(!user || user.password != req.body.password) {
      res.render("login",
        {layout:false,
          locals:{ error:"Invalid login!", email:req.body.email }
        }
      );
    } else {
      // successful login; store the session info
      req.session.login = req.body.email;
      res.redirect("/");
    }
  });
});

In jedem Fall war dieser Ansatz meist flexibel und einfach gestaltet. Ich bin sicher, es gibt zahlreiche Möglichkeiten, dies zu verbessern. Wenn Sie welche haben, würde ich mich sehr über Ihr Feedback freuen.

EDIT: Dies ist ein vereinfachtes Beispiel. In einem Produktionssystem möchten Sie niemals Passwörter im Klartext speichern und vergleichen. Wie ein Kommentator hervorhebt, gibt es Bibliotheken, die bei der Verwaltung der Kennwortsicherheit helfen können.

Tom
quelle
2
Dies ist gut, außer dass Sie bcrypt zum Speichern des Passworts verwenden sollten (kein einfacher Text in der Datenbank). Es gibt hier einen guten Beitrag darüber: devsmash.com/blog/…
chovy
13

Auch einen Blick auf everyauth wenn Sie Dritter / soziales Netzwerk Login Integration wollen.

Peter Lyons
quelle
7

Hier ist ein Code für die grundlegende Authentifizierung aus einem meiner Projekte. Ich benutze es gegen CouchDB mit und zusätzlichem Authentifizierungsdaten-Cache, aber ich habe diesen Code entfernt.

Wickeln Sie eine Authentifizierungsmethode um Ihre Anforderungsbearbeitung und geben Sie einen zweiten Rückruf für eine nicht erfolgreiche Authentifizierung an. Der erfolgreiche Rückruf erhält den Benutzernamen als zusätzlichen Parameter. Vergessen Sie nicht, Anforderungen mit falschen oder fehlenden Anmeldeinformationen im Fehlerrückruf korrekt zu behandeln:

/**
 * Authenticate a request against this authentication instance.
 * 
 * @param request
 * @param failureCallback
 * @param successCallback
 * @return
 */
Auth.prototype.authenticate = function(request, failureCallback, successCallback)
{
    var requestUsername = "";
    var requestPassword = "";
    if (!request.headers['authorization'])
    {
        failureCallback();
    }
    else
    {
        var auth = this._decodeBase64(request.headers['authorization']);
        if (auth)
        {
            requestUsername = auth.username;
            requestPassword = auth.password;
        }
        else
        {
            failureCallback();
        }
    }


    //TODO: Query your database (don't forget to do so async)


    db.query( function(result)
    {
        if (result.username == requestUsername && result.password == requestPassword)
        {
            successCallback(requestUsername);
        }
        else
        {
            failureCallback();
        }
    });

};


/**
 * Internal method for extracting username and password out of a Basic
 * Authentication header field.
 * 
 * @param headerValue
 * @return
 */
Auth.prototype._decodeBase64 = function(headerValue)
{
    var value;
    if (value = headerValue.match("^Basic\\s([A-Za-z0-9+/=]+)$"))
    {
        var auth = (new Buffer(value[1] || "", "base64")).toString("ascii");
        return {
            username : auth.slice(0, auth.indexOf(':')),
            password : auth.slice(auth.indexOf(':') + 1, auth.length)
        };
    }
    else
    {
        return null;
    }

};
b_erb
quelle
Ich wollte eine grundlegende Authentifizierung zugunsten einer formularbasierten Authentifizierung vermeiden. Dies ist definitiv eine elegante Lösung für das grundlegende Authentifizierungsproblem. Ich glaube, ich habe vielleicht ein gutes Auth-Framework gefunden (connect-auth - sitzt oben auf connectjs)
shreddd
4

Eine andere Sichtweise der Authentifizierung ist Passwordless, eine tokenbasierte Authentifizierung für Express, das das inhärente Problem von Passwörtern umgeht [1]. Es ist schnell zu implementieren, erfordert nicht zu viele Formulare und bietet eine bessere Sicherheit für den durchschnittlichen Benutzer (vollständige Offenlegung: Ich bin der Autor).

[1]: Passwörter sind veraltet

Florian
quelle
3

Einige Jahre sind vergangen und ich möchte meine Authentifizierungslösung für Express vorstellen. Es heißt Lockit . Sie finden das Projekt auf GitHub und ein kurzes Intro in meinem Blog .

Was sind also die Unterschiede zu den bestehenden Lösungen?

  • einfach zu bedienen: Einrichten Ihre DB, npm zu installieren require('lockit'),lockit(app) getan
  • bereits integrierte Routen (/ Anmeldung, / Login, / Passwort vergessen usw.)
  • bereits integrierte Ansichten (basierend auf Bootstrap, aber Sie können problemlos Ihre eigenen Ansichten verwenden)
  • Es unterstützt die JSON-Kommunikation für Ihre AngularJS / Ember.js Single-Page-Apps
  • OAuth und OpenID werden NICHT unterstützt. Nur usernameundpassword .
  • Es funktioniert mit mehreren Datenbanken (CouchDB, MongoDB, SQL)
  • Es hat Tests (ich konnte keine Tests für Trockenbau finden)
  • es wird aktiv gepflegt (im Vergleich zu everyauth)
  • E-Mail-Überprüfung und Passwort vergessen (E-Mail mit Token senden, von Passport nicht unterstützt)
  • Modularität: Verwenden Sie nur das, was Sie brauchen
  • Flexibilität: Passen Sie alle Dinge an

Schauen Sie sich die Beispiele an .

Zemirco
quelle
2

Es gibt ein Projekt namens Drywall , das ein Benutzeranmeldesystem mit Passport implementiert und über ein Administrationsfenster für die Benutzerverwaltung verfügt. Wenn Sie nach einem voll funktionsfähigen Benutzerauthentifizierungs- und -verwaltungssystem suchen, das dem ähnelt, was Django nur für Node.js bietet, dann ist dies genau das Richtige. Ich fand es ein wirklich guter Ausgangspunkt für die Erstellung einer Knoten-App, für die ein Benutzerauthentifizierungs- und -verwaltungssystem erforderlich ist. Siehe Jared Hanson Antwort für Informationen darüber , wie Passport funktioniert.

lk145
quelle
1

Schnelles einfaches Beispiel mit Mongo für eine API, die Benutzerauthentifizierung für den Angular-Client bereitstellt

in app.js.

var express = require('express');
var MongoStore = require('connect-mongo')(express);

// ...

app.use(express.cookieParser());
// obviously change db settings to suit
app.use(express.session({
    secret: 'blah1234',
    store: new MongoStore({
        db: 'dbname',
        host: 'localhost',
        port: 27017
    })
}));

app.use(app.router);

für deine Route so etwas:

// (mongo connection stuff)

exports.login = function(req, res) {

    var email = req.body.email;
    // use bcrypt in production for password hashing
    var password = req.body.password;

    db.collection('users', function(err, collection) {
        collection.findOne({'email': email, 'password': password}, function(err, user) {
            if (err) {
                res.send(500);
            } else {
                if(user !== null) {
                    req.session.user = user;
                    res.send(200);
                } else {
                    res.send(401);
                }
            }
        });
    });
};

Dann können Sie in Ihren Routen, für die eine Authentifizierung erforderlich ist, einfach nach der Benutzersitzung suchen:

if (!req.session.user) {
    res.send(403);
}
Herr P.
quelle
0

Hier ist eine neue Authentifizierungsbibliothek, die zeitgestempelte Token verwendet. Die Token können per E-Mail oder SMS an Benutzer gesendet werden, ohne dass sie in einer Datenbank gespeichert werden müssen. Es kann für die kennwortlose Authentifizierung oder für die Zwei-Faktor-Authentifizierung verwendet werden.

https://github.com/vote539/easy-no-password

Offenlegung: Ich bin der Entwickler dieser Bibliothek.

sffc
quelle
0

Wenn Sie eine Authentifizierung mit SSO (Single Sign On) mit einem Microsoft Windows-Benutzerkonto benötigen. Sie können https://github.com/jlguenego/node-expose-sspi ausprobieren .

Sie erhalten ein req.ssoObjekt, das alle Client-Benutzerinformationen enthält (Login, Anzeigename, Sid, Gruppen).

const express = require("express");
const { sso, sspi } = require("node-expose-sspi");

sso.config.debug = false;

const app = express();

app.use(sso.auth());

app.use((req, res, next) => {
  res.json({
    sso: req.sso
  });
});

app.listen(3000, () => console.log("Server started on port 3000"));

Haftungsausschluss: Ich bin der Autor von node-expose-sspi.

jlguenego
quelle
0

sweet-auth

Ein leichtes Benutzerauthentifizierungsmodul ohne Konfiguration. Es wird keine sperate Datenbank benötigt.

https://www.npmjs.com/package/sweet-auth

Es ist so einfach wie:

app.get('/private-page', (req, res) => {

    if (req.user.isAuthorized) {
        // user is logged in! send the requested page
        // you can access req.user.email
    }
    else {
        // user not logged in. redirect to login page
    }
})
Naveen
quelle