librerie di autenticazione utente per node.js?


274

Esistono librerie di autenticazione utente esistenti per node.js? In particolare, sto cercando qualcosa che possa eseguire l'autenticazione con password per un utente (utilizzando un DB di autenticazione back-end personalizzato) e associare tale utente a una sessione.

Prima di scrivere una libreria di autenticazione, immaginavo che se la gente fosse a conoscenza delle biblioteche esistenti. Non è stato possibile trovare nulla di ovvio tramite una ricerca su Google.

-Shreyas


Per la ricerca: qualcosa di equivalente a omniauth(rotaie) o pitone social-auth. Gli utenti di PHP (e altre lingue comuni dei server Web) dovrebbero sentirsi liberi di aggiungere anche il loro equivalente.
forivall

Risposte:


233

Se stai cercando un framework di autenticazione per Connect o Express, vale la pena indagare su Passport: https://github.com/jaredhanson/passport

(Divulgazione: sono lo sviluppatore di Passport)

Ho sviluppato Passport dopo aver studiato sia connect-auth che everyauth. Sebbene siano entrambi ottimi moduli, non soddisfacevano le mie esigenze. Volevo qualcosa di più leggero e discreto.

Il passaporto è suddiviso in moduli separati, quindi puoi scegliere di utilizzare solo ciò di cui hai bisogno (OAuth, solo se necessario). Passport inoltre non monta alcun percorso nell'applicazione, offrendo la flessibilità di decidere quando e dove si desidera l'autenticazione e si aggancia per controllare cosa succede quando l'autenticazione ha esito positivo o negativo.

Ad esempio, ecco il processo in due passaggi per impostare l'autenticazione basata su form (nome utente e password):

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

Ulteriori strategie sono disponibili per l'autenticazione tramite Facebook, Twitter, ecc. Le strategie personalizzate possono essere collegate, se necessario.


Tra tutti i pacchetti di autorizzazione per il nodo ho selezionato il passaporto. È ben documentato e facile da usare e supporta più strategie.
tech-man,

Al momento sto usando il passaporto per un prototipo e non lo consiglio perché non sembra mantenuto e il design non è molto buono. Ad esempio, ti costringe a utilizzare connect-flash quando potrebbe semplicemente utilizzare req.session.messages e il passaporto-google pubblicizzato sul sito Web è obsoleto poiché utilizza Google OpenId che è obsoleto e non esiste alcun collegamento a passport- google-oauth che dovrebbe sostituirlo. Anche questa è la firma di un callback dopo l'autenticazione: done(null,false,{ message:'Incorrect username.' })è terribile poiché non sappiamo quali siano tutti quei parametri.
eloone,

1
@eloone Devo aggiornare i documenti per puntare ai nuovi metodi di autenticazione che Google preferisce ora. Come dici tu, il supporto esiste per quelli e funzionano bene. Per quanto riguarda le domande di progettazione, il passaporto non ti obbliga a utilizzare connect-flash e gli argomenti citati sono documentati nella guida. Se hai bisogno di aiuto per capire, ci sono forum in cui le persone possono aiutare e rispondere alle tue domande.
Jared Hanson,

Non per niente, ma ho appena finito di collegare Passport (ho usato l'esempio fornito). Super facile! Mi rendo conto che sono passati alcuni anni da quando i commenti più recenti. Consiglierei a chiunque di dare un'occhiata.
terary

89

Sessione + If

Immagino che il motivo per cui non hai trovato molte buone librerie sia che l'uso di una libreria per l'autenticazione sia in gran parte sovra ingegnerizzato.

Quello che stai cercando è solo un raccoglitore di sessione :) Una sessione con:

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

questo è tutto.


Non sono d'accordo con la tua conclusione che il plug-in connect-auth sia la strada da percorrere.

Sto usando anche connect ma non uso connect-auth per due motivi:

  1. IMHO rompe connect-auth l'architettura a cipolla molto potente e facile da leggere di connect. A no-go - la mia opinione :). Puoi trovare un articolo molto buono e breve su come funziona la connessione e l'idea dell'anello di cipolla qui .

  2. Se, come scritto, vuoi solo usare un login di base o http con database o file. Connect-auth è troppo grande. È più per cose come OAuth 1.0, OAuth 2.0 & Co


Un'autenticazione molto semplice con connect

(È completo. Basta eseguirlo per il test ma se si desidera utilizzarlo in produzione, assicurarsi di utilizzare https) (E per essere conforme ai principi REST è necessario utilizzare una richiesta POST anziché una richiesta GET b / c cambi uno stato :)

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

NOTA

Ho scritto questa dichiarazione oltre un anno fa e al momento non ho progetti di nodi attivi. Quindi ci possono essere cambiamenti API in Express. Per favore, aggiungi un commento se dovessi cambiare qualcosa.


Perché connect-auth interrompe il modello cipolla / strati? è perché non usa next ()? Può?
jpstrikesback,

3
Sì. Deve usare next () perché questa è l'idea alla base di connect. Connect ha un'architettura di livello / forma di struttura di codice. E ogni livello ha il potere di fermare l'esecuzione della richiesta non chiamando next (). Se parliamo di autenticazione: un livello di autenticazione controllerà se l'utente dispone delle autorizzazioni corrette. Se tutto va bene, il layer chiama next (). In caso contrario, questo livello di autorizzazione genera un errore e non chiamerà next ().
Matthias,

amico, questo è esattamente quello che stavo cercando. connect-auth mi stava dando un po 'di indigestione. Ho appena effettuato l'accesso alla mia app per la prima volta. grazie mille.
Andy Ray,

7
Ciò non aiuta ancora a rispondere a come connettersi a un back-end del database (preferibilmente con password crittografate). Apprezzo il tuo commento sul fatto che questa libreria sia troppo ingegnerizzata, ma sicuramente ce n'è una che non lo è. Inoltre, se avessi voluto scrivere il mio sistema di autenticazione avrei usato Struts in Java. proprio come l'OP, voglio sapere quali plugin lo faranno per me in 1 riga di codice.
Hendrixski,

4
ottima risposta Nivoc. Non funziona con le ultime versioni di Connect Tho. Ho dovuto cambiare ... cookieDecoder () -> cookieParser () e bodyDecoder () -> bodyParser () e rimuovere la chiamata successiva () dalla funzione helloWorldContent mentre stavo ricevendo un errore 'Impossibile impostare le intestazioni dopo che vengono inviati "
Michael Dausmann,

26

Sembra che il plug-in connect-auth per il middleware connect sia esattamente ciò di cui ho bisogno: http://wiki.github.com/ciaranj/connect-auth/creating-a-form-based-strategy

Sto usando express [ http://expressjs.com ] quindi il plug-in di connessione si adatta molto bene poiché express è sottoclassato (ok - prototipato) da connect


1
ehi, hai un esempio di quello che hai fatto? semplicemente richiedendo connect-auth e chiamando ".authenticate" su "req" restituisce "TypeError: Object # non ha alcun metodo" autenticare "" per me.
Misha Reyzlin,

1
IMHO Questo plugin è troppo pesante per la semplice autenticazione http
Matthias,

E questo plugin funziona contro l'architettura connessa con l'anello di cipolla
Matthias,

14

In pratica stavo cercando la stessa cosa. In particolare, volevo quanto segue:

  1. Per utilizzare express.js, che racchiude la capacità del middleware di Connect
  2. Autenticazione "basata sul modulo"
  3. Controllo granulare su quali percorsi sono autenticati
  4. Un back-end di database per utenti / password
  5. Usa le sessioni

Quello che ho finito per fare è stato creare la mia funzione middleware check_authche passo come argomento per ogni route che voglio autenticare. check_authcontrolla semplicemente la sessione e se l'utente non ha effettuato l'accesso, quindi li reindirizza alla pagina di accesso, in questo modo:

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

Quindi, per ogni percorso, mi assicuro che questa funzione sia passata come middleware. Per esempio:

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

Infine, dobbiamo effettivamente gestire il processo di accesso. Questo è semplice:

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

Ad ogni modo, questo approccio è stato principalmente progettato per essere flessibile e semplice. Sono sicuro che ci sono molti modi per migliorarlo. Se ne hai, mi piacerebbe molto il tuo feedback.

EDIT: questo è un esempio semplificato. In un sistema di produzione, non vorresti mai archiviare e confrontare le password in testo semplice. Come sottolinea un commentatore, ci sono librerie che possono aiutare a gestire la sicurezza delle password.


2
questo è buono, tranne che dovresti usare bcrypt per archiviare la password (non testo normale in db). C'è un buon post qui a riguardo: devsmash.com/blog/…
chovy,


7

Ecco un po 'di codice per l'autenticazione di base da uno dei miei progetti. Lo uso contro CouchDB con e una cache di dati di autenticazione aggiuntiva, ma ho rimosso quel codice.

Avvolgere un metodo di autenticazione attorno alla richiesta di gestione e fornire un secondo callback per l'autenticazione non riuscita. La callback riuscita otterrà il nome utente come parametro aggiuntivo. Non dimenticare di gestire correttamente le richieste con credenziali errate o mancanti nel callback dell'errore:

/**
 * 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;
    }

};

Volevo evitare l'autent di base in favore dell'autent basata su form. Questa è sicuramente una soluzione elegante al problema di autenticazione di base. Penso di aver trovato un buon framework di autenticazione però (connect-auth - si trova in cima a connectjs)
shreddd

4

Un'autenticazione diversa sull'autenticazione è Passwordless, un modulo di autenticazione basato su token per express che elude il problema intrinseco delle password [1]. È veloce da implementare, non richiede troppi moduli e offre una migliore sicurezza per l'utente medio (divulgazione completa: sono l'autore).

[1]: le password sono obsolete


3

Sono passati alcuni anni e vorrei presentare la mia soluzione di autenticazione per Express. Si chiama Lockit . Puoi trovare il progetto su GitHub e una breve introduzione sul mio blog .

Quindi quali sono le differenze rispetto alle soluzioni esistenti?

  • facile da usare: impostare il DB, NPM installare, require('lockit'), lockit(app), fatto
  • percorsi già integrati (/ iscrizione, / login, / password dimenticata, ecc.)
  • viste già integrate (basate su Bootstrap ma puoi usare facilmente le tue viste)
  • supporta la comunicazione JSON per le app a pagina singola AngularJS / Ember.js
  • NON supporta OAuth e OpenID. Solo usernamee password.
  • funziona con diversi database (CouchDB, MongoDB, SQL) pronti all'uso
  • ha dei test (non sono riuscito a trovare alcun test per il muro a secco)
  • è attivamente mantenuto (rispetto a everyauth)
  • verifica dell'email e processo di password dimenticata (invia email con token, non supportato da Passport)
  • modularità: usa solo ciò di cui hai bisogno
  • flessibilità: personalizzare tutte le cose

Dai un'occhiata agli esempi .


2

Esiste un progetto chiamato Drywall che implementa un sistema di accesso utente con Passport e ha anche un pannello di amministrazione della gestione utenti. Se stai cercando un sistema di autenticazione e gestione degli utenti completo simile a quello che ha Django ma per Node.js, questo è tutto. Ho trovato un ottimo punto di partenza per la creazione di un'app nodo che richiedeva un sistema di gestione e autenticazione utente. Vedi la risposta di Jared Hanson per informazioni su come funziona Passport.



1

Esempio semplice e veloce usando mongo, per un'API che fornisce l'autenticazione utente per client Angular

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

per il tuo percorso qualcosa del genere:

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

Quindi nei tuoi percorsi che richiedono auth puoi semplicemente controllare la sessione utente:

if (!req.session.user) {
    res.send(403);
}

0

Ecco una nuova libreria di autenticazione che utilizza token con data e ora. I token possono essere inviati tramite e-mail o SMS agli utenti senza la necessità di archiviarli in un database. Può essere utilizzato per l'autenticazione senza password o per l'autenticazione a due fattori.

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

Divulgazione: sono lo sviluppatore di questa libreria.


0

Se è necessaria l'autenticazione con SSO (Single Sign On) con account utente di Microsoft Windows. Puoi provare a https://github.com/jlguenego/node-expose-sspi .

Ti darà un req.ssooggetto che contiene tutte le informazioni sull'utente del client (login, nome visualizzato, sid, gruppi).

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

Disclaimer: sono l'autore di node-expose-sspi.


0

sweet-auth

Un modulo di autenticazione utente leggero, a configurazione zero. Non ha bisogno di un database sperate.

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

È semplice come:

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
    }
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.