bibliothèques d'authentification des utilisateurs pour node.js?

274

Existe-t-il des bibliothèques d'authentification utilisateur existantes pour node.js? En particulier, je recherche quelque chose qui peut faire l'authentification par mot de passe pour un utilisateur (en utilisant une base de données d'authentification backend personnalisée) et associer cet utilisateur à une session.

Avant d'écrire une bibliothèque d'authentification, je me suis dit que je verrais si les gens connaissaient les bibliothèques existantes. Impossible de trouver quelque chose d'évident via une recherche Google.

-Shreyas

shreddd
la source
Pour la recherche: quelque chose d'équivalent à omniauth(rails) ou python social-auth. Les utilisateurs de PHP (et d'autres langages de serveur Web courants) devraient également se sentir libres d'ajouter leur équivalent.
Forivall

Réponses:

233

Si vous recherchez un cadre d'authentification pour Connect ou Express, Passport mérite d'être étudié: https://github.com/jaredhanson/passport

(Divulgation: je suis le développeur de Passport)

J'ai développé Passport après avoir enquêté sur connect-auth et everyauth. Bien qu'ils soient tous deux d'excellents modules, ils ne répondaient pas à mes besoins. Je voulais quelque chose de plus léger et discret.

Le passeport est divisé en modules séparés, vous pouvez donc choisir d'utiliser uniquement ce dont vous avez besoin (OAuth, uniquement si nécessaire). Passport ne monte également aucune route dans votre application, ce qui vous donne la possibilité de décider quand et où vous voulez l'authentification, et des hooks pour contrôler ce qui se passe lorsque l'authentification réussit ou échoue.

Par exemple, voici le processus en deux étapes pour configurer l'authentification basée sur le formulaire (nom d'utilisateur et mot de passe):

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

Des stratégies supplémentaires sont disponibles pour l'authentification via Facebook, Twitter, etc. Des stratégies personnalisées peuvent être connectées, si nécessaire.

Jared Hanson
la source
Parmi tous les packages d'authentification pour le nœud, j'ai sélectionné un passeport. Il est bien documenté et facile à utiliser, et prend en charge plus de stratégies.
tech-man
J'utilise actuellement un passeport pour un prototype, et je ne le recommande pas car il ne semble pas maintenu et le design n'est pas très bon. Par exemple, il vous oblige à utiliser connect-flash alors qu'il pourrait simplement utiliser req.session.messages, et le passeport-google annoncé sur le site Web est obsolète car il utilise Google OpenId qui est obsolète, et il n'y a pas de lien vers le passeport- google-oauth qui devrait le remplacer. C'est aussi la signature d'un rappel après authentification: done(null,false,{ message:'Incorrect username.' })c'est terrible car on ne connaît pas tous ces paramètres.
eloone
1
@eloone J'ai besoin de mettre à jour les documents pour pointer vers les nouvelles méthodes d'authentification que Google préfère maintenant. Comme vous l'avez mentionné, il existe un soutien pour ces personnes et elles fonctionnent bien. En ce qui concerne les questions de conception, le passeport ne vous oblige pas à utiliser connect-flash, et les arguments que vous mentionnez sont documentés sur le guide. Si vous avez besoin d'aide pour comprendre, il existe des forums où les gens peuvent vous aider et répondre à vos questions.
Jared Hanson
Pas pour rien - mais je viens de terminer de brancher Passport (en utilisant l'exemple fourni). Super facile! Je me rends compte que cela fait quelques années depuis les commentaires les plus récents. Je recommanderais à quiconque de jeter un œil.
terary
89

Session + If

Je suppose que la raison pour laquelle vous n'avez pas trouvé beaucoup de bonnes bibliothèques est que l'utilisation d'une bibliothèque pour l'authentification est généralement trop conçue.

Ce que vous cherchez est juste un classeur de session :) Une session avec:

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

c'est tout.


Je ne suis pas d'accord avec votre conclusion selon laquelle le plug-in connect-auth est la voie à suivre.

J'utilise également connect mais je n'utilise pas connect-auth pour deux raisons:

  1. IMHO brise connect-auth l'architecture très puissante et facile à lire de l'oignon de connect. A ne pas faire - mon avis :). Vous pouvez trouver un très bon et court article sur le fonctionnement de connect et l'idée de la rondelle d'oignon ici .

  2. Si vous - comme écrit - souhaitez simplement utiliser une connexion de base ou http avec une base de données ou un fichier. Connect-auth est beaucoup trop volumineux. C'est plus pour des trucs comme OAuth 1.0, OAuth 2.0 & Co


Une authentification très simple avec connect

(Il est complet. Il suffit de l'exécuter pour les tests mais si vous souhaitez l'utiliser en production, assurez-vous d'utiliser https) (Et pour être conforme aux principes REST, vous devez utiliser une requête POST au lieu d'une requête GET b / c vous changez d'état :)

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

REMARQUE

J'ai écrit cette déclaration il y a plus d'un an et je n'ai actuellement aucun projet de nœud actif. Il peut donc y avoir des changements d'API dans Express. Veuillez ajouter un commentaire si je dois changer quoi que ce soit.

nivoc
la source
Pourquoi connect-auth brise-t-il le modèle oignon / couches? est-ce parce qu'il n'utilise pas next ()? Pourrait-il?
jpstrikesback le
3
Oui. Il doit utiliser next () car c'est l'idée derrière connect. Connect a une architecture de couche / forme de structure de code. Et chaque couche a le pouvoir d'arrêter l'exécution de la demande en n'appelant pas next (). Si nous parlons d'authentification: une couche d'authentification vérifiera si l'utilisateur dispose des autorisations appropriées. Si tout va bien, le calque appelle next (). Sinon, cette couche d'authentification génère une erreur et n'appellera pas next ().
Matthias
mec, c'est exactement ce que je cherchais. connect-auth me donnait un peu d'indigestion. Je viens de me connecter à mon application pour la première fois. Merci beaucoup.
Andy Ray
7
Cela n'aide toujours pas à savoir comment se connecter à un backend de base de données (de préférence avec des mots de passe cryptés). J'apprécie votre commentaire selon lequel cette bibliothèque est sur-conçue, mais il y en a sûrement une qui ne l'est pas. De plus, si j'avais voulu écrire mon propre système d'authentification, j'aurais utilisé Struts en Java. tout comme l'OP, je veux savoir quels plugins feront cela pour moi en 1 ligne de code.
hendrixski
4
grande réponse Nivoc. Ne fonctionne pas avec les dernières versions de connect tho. J'ai dû changer ... cookieDecoder () -> cookieParser () et bodyDecoder () -> bodyParser () et supprimer l'appel suivant () de la fonction helloWorldContent car j'obtenais une erreur 'Impossible de définir les en-têtes après qu'ils sont envoyés '
Michael Dausmann
26

On dirait que le plugin connect-auth au middleware connect est exactement ce dont j'ai besoin: http://wiki.github.com/ciaranj/connect-auth/creating-a-form-based-strategy

J'utilise express [ http://expressjs.com ] donc le plugin connect s'intègre très bien car express est sous-classé (ok - prototypé) de connect

shreddd
la source
1
hé, avez-vous un exemple de ce que vous avez fait? exiger simplement connect-auth et appeler ".authenticate" sur "req" renvoie "TypeError: Object # n'a pas de méthode 'authenticate'" pour moi.
Misha Reyzlin
1
À mon humble avis, ce plugin est trop lourd pour une simple authentification http
Matthias
Et ce plugin fonctionne contre l'architecture connect oignon ring
Matthias
14

Je cherchais essentiellement la même chose. Plus précisément, je voulais ce qui suit:

  1. Pour utiliser express.js, qui englobe la capacité de middleware de Connect
  2. Authentification "basée sur le formulaire"
  3. Contrôle granulaire des itinéraires authentifiés
  4. Un back-end de base de données pour les utilisateurs / mots de passe
  5. Utiliser des sessions

J'ai fini par créer ma propre fonction middleware check_authque je transmets comme argument à chaque route que je veux authentifier. check_authvérifie simplement la session et si l'utilisateur n'est pas connecté, puis les redirige vers la page de connexion, comme ceci:

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

Ensuite, pour chaque itinéraire, je m'assure que cette fonction est passée en tant que middleware. Par exemple:

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

Enfin, nous devons réellement gérer le processus de connexion. C'est simple:

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

Quoi qu'il en soit, cette approche a été conçue principalement pour être flexible et simple. Je suis sûr qu'il existe de nombreuses façons de l'améliorer. Si vous en avez, j'aimerais beaucoup vos commentaires.

EDIT: Ceci est un exemple simplifié. Dans un système de production, vous ne voudriez jamais stocker et comparer des mots de passe en texte brut. Comme le souligne un commentateur, il existe des bibliothèques qui peuvent aider à gérer la sécurité des mots de passe.

À M
la source
2
c'est bien, sauf que vous devez utiliser bcrypt pour stocker le mot de passe (pas du texte brut dans db). Il y a un bon article ici à ce sujet: devsmash.com/blog/…
chovy
13

Jetez également un œil à everyauth si vous souhaitez intégrer une connexion tierce / réseau social.

Peter Lyons
la source
7

Voici du code pour l'authentification de base de l'un de mes projets. Je l'utilise contre CouchDB avec et un cache de données d'authentification supplémentaire, mais j'ai supprimé ce code.

Enveloppez une méthode d'authentification autour de la gestion des demandes et fournissez un deuxième rappel pour l'authentification infructueuse. Le rappel de réussite obtiendra le nom d'utilisateur comme paramètre supplémentaire. N'oubliez pas de gérer correctement les demandes avec des informations d'identification incorrectes ou manquantes dans le rappel d'échec:

/**
 * 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
la source
Je voulais éviter l'authentification de base au profit d'une authentification basée sur les formulaires. C'est certainement une solution élégante au problème d'authentification de base. Je pense que j'ai peut-être trouvé un bon cadre d'authentification (connect-auth - se trouve au-dessus de connectjs)
shreddd
4

Une autre approche de l'authentification est Passwordless, un module d' authentification basé sur des jetons pour express qui contourne le problème inhérent aux mots de passe [1]. Il est rapide à mettre en œuvre, ne nécessite pas trop de formulaires et offre une meilleure sécurité pour l'utilisateur moyen (divulgation complète: je suis l'auteur).

[1]: Les mots de passe sont obsolètes

florian
la source
3

Quelques années se sont écoulées et j'aimerais vous présenter ma solution d'authentification pour Express. Ça s'appelle Lockit . Vous pouvez trouver le projet sur GitHub et une courte introduction sur mon blog .

Quelles sont donc les différences avec les solutions existantes?

  • facile à utiliser: configurer votre DB, NPM installer, require('lockit'), lockit(app), fait
  • routes déjà intégrées (/ signup, / login, / Forgot-Password, etc.)
  • vues déjà intégrées (basées sur Bootstrap mais vous pouvez facilement utiliser vos propres vues)
  • il prend en charge la communication JSON pour vos applications à page unique AngularJS / Ember.js
  • il ne prend PAS en charge OAuth et OpenID. Seulement usernameet password.
  • il fonctionne avec plusieurs bases de données (CouchDB, MongoDB, SQL) hors de la boîte
  • il a des tests (je n'ai trouvé aucun test pour les cloisons sèches)
  • il est activement entretenu (par rapport à chaque mois)
  • vérification des e-mails et processus de mot de passe oublié (envoyer un e-mail avec un jeton, non pris en charge par Passport)
  • modularité: n'utilisez que ce dont vous avez besoin
  • flexibilité: personnalisez tout

Jetez un œil aux exemples .

zemirco
la source
2

Il existe un projet appelé Drywall qui implémente un système de connexion utilisateur avec Passport et dispose également d'un panneau d'administration de gestion des utilisateurs. Si vous recherchez un système d'authentification et de gestion des utilisateurs complet, similaire à quelque chose comme Django, mais pour Node.js, c'est tout. J'ai trouvé que c'était un très bon point de départ pour créer une application de nœud qui nécessitait un système d'authentification et de gestion des utilisateurs. Voir la réponse de Jared Hanson pour plus d'informations sur le fonctionnement de Passport.

lk145
la source
1

Exemple simple et rapide utilisant mongo, pour une API qui fournit l'authentification de l'utilisateur pour le client angulaire, par exemple

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

pour votre itinéraire quelque chose comme ceci:

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

Ensuite, dans vos itinéraires qui nécessitent une authentification, vous pouvez simplement vérifier la session utilisateur:

if (!req.session.user) {
    res.send(403);
}
Monsieur P
la source
0

Voici une nouvelle bibliothèque d'authentification qui utilise des jetons horodatés. Les jetons peuvent être envoyés par e-mail ou par SMS aux utilisateurs sans avoir besoin de les stocker dans une base de données. Il peut être utilisé pour l'authentification sans mot de passe ou pour l'authentification à deux facteurs.

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

Divulgation: je suis le développeur de cette bibliothèque.

sffc
la source
0

Si vous avez besoin d'une authentification avec SSO (Single Sign On) avec un compte d'utilisateur Microsoft Windows. Vous pouvez essayer de https://github.com/jlguenego/node-expose-sspi .

Il vous donnera un req.ssoobjet qui contient toutes les informations utilisateur client (login, nom d'affichage, sid, groupes).

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

Avertissement: je suis l'auteur de node-expose-sspi.

jlguenego
la source
0

sweet-auth

Un module d'authentification utilisateur léger et sans configuration. Il n'a pas besoin d'une base de données sperate.

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

C'est aussi simple que:

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
la source