bibliotecas de autenticación de usuario para node.js?

274

¿Hay alguna biblioteca de autenticación de usuario existente para node.js? En particular, estoy buscando algo que pueda hacer la autenticación de contraseña para un usuario (usando una base de datos de autenticación de back-end personalizada) y asociar a ese usuario con una sesión.

Antes de escribir una biblioteca de autenticación, pensé que vería si la gente supiera de las bibliotecas existentes. No se pudo encontrar nada obvio a través de una búsqueda en Google.

-Shreyas

shreddd
fuente
Para buscar: algo equivalente a omniauth(rails) o python social-auth. Los usuarios de PHP (y otros lenguajes de servidor web comunes) también deben sentirse libres de agregar su equivalente.
Forivall

Respuestas:

233

Si está buscando un marco de autenticación para Connect o Express, vale la pena investigar Passport: https://github.com/jaredhanson/passport

(Divulgación: soy el desarrollador de Passport)

Desarrollé Passport después de investigar tanto connect-auth como everyauth. Si bien ambos son excelentes módulos, no se adaptaron a mis necesidades. Quería algo que fuera más liviano y discreto.

El pasaporte se divide en módulos separados, por lo que puede elegir usar solo lo que necesita (OAuth, solo si es necesario). Passport tampoco monta ninguna ruta en su aplicación, lo que le brinda la flexibilidad de decidir cuándo y dónde desea la autenticación, y los ganchos para controlar lo que sucede cuando la autenticación tiene éxito o falla.

Por ejemplo, este es el proceso de dos pasos para configurar la autenticación basada en formularios (nombre de usuario y contraseña):

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

Hay estrategias adicionales disponibles para la autenticación a través de Facebook, Twitter, etc. Las estrategias personalizadas se pueden conectar, si es necesario.

Jared Hanson
fuente
Entre todos los paquetes de autenticación para el nodo, seleccioné el pasaporte. Está bien documentado y es fácil de usar, y admite más estrategias.
tech-man
En este momento estoy usando un pasaporte para un prototipo, y no lo recomiendo porque parece que no se mantiene y el diseño no es muy bueno. Por ejemplo, lo obliga a usar connect-flash cuando simplemente podría usar req.session.messages, y el pasaporte-google anunciado en el sitio web está desactualizado ya que usa Google OpenId que está en desuso, y no hay un enlace al pasaporte- google-oauth que debería reemplazarlo. Además, esta es la firma de una devolución de llamada después de la autenticación: done(null,false,{ message:'Incorrect username.' })es terrible ya que no sabemos cuáles son todos esos parámetros.
eloone
1
@eloone Necesito actualizar los documentos para señalar los nuevos métodos de autenticación que Google ahora prefiere. Como mencionas, existe apoyo para ellos y funcionan bien. En cuanto a las preguntas de diseño, el pasaporte no lo obliga a usar connect-flash, y los argumentos que menciona están documentados en la guía. Si necesita ayuda para comprender, hay foros donde las personas pueden ayudarlo y responder sus preguntas.
Jared Hanson
No por nada, pero acabo de terminar de conectar Passport (utilicé el ejemplo suministrado). ¡Muy facil! Me doy cuenta de que han pasado algunos años desde los comentarios más recientes. Recomendaría a cualquiera que eche un vistazo.
Terary
89

Sesión + Si

Supongo que la razón por la que no ha encontrado muchas bibliotecas buenas es que el uso de una biblioteca para la autenticación está sobredimensionado.

Lo que está buscando es solo una carpeta de sesión :) Una sesión con:

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

Eso es.


No estoy de acuerdo con su conclusión de que el complemento connect-auth es el camino a seguir.

También estoy usando connect pero no uso connect-auth por dos razones:

  1. En mi humilde opinión, connect-auth es la arquitectura de conexión de anillo de cebolla muy potente y fácil de leer. Un no-go - mi opinión :). Puede encontrar un artículo muy bueno y breve sobre cómo funciona connect y la idea del anillo de cebolla aquí .

  2. Si usted, como está escrito, solo quiere usar un inicio de sesión básico o http con una base de datos o un archivo. Connect-auth es demasiado grande. Es más para cosas como OAuth 1.0, OAuth 2.0 & Co


Una autenticación muy simple con connect

(Está completo. Simplemente ejecútelo para la prueba, pero si desea usarlo en producción, asegúrese de usar https) (Y para cumplir con el Principio REST, debe usar una Solicitud POST en lugar de una Solicitud GET b / c cambias un estado :)

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

Escribí esta declaración hace más de un año y actualmente no tengo proyectos de nodos activos. Por lo tanto, puede haber cambios de API en Express. Agregue un comentario si debo cambiar algo.

nivoc
fuente
¿Por qué connect-auth rompe el patrón de cebolla / capas? ¿es porque no usa next ()? ¿Podría?
jpstrikesback
3
Si. Debe usar next () porque esa es la idea detrás de connect. Connect tiene una arquitectura de capa / forma de estructura de código. Y cada capa tiene el poder de detener la ejecución de la solicitud al no llamar a next (). Si hablamos de autenticación: una capa de autenticación verificará si el usuario tiene los permisos correctos. Si todo está bien, la capa llama a next (). Si no, esta capa de autenticación genera un error y no llamará a next ().
Matthias
hombre, esto es exactamente lo que estaba buscando. connect-auth me estaba dando un poco de indigestión. Acabo de iniciar sesión en mi aplicación por primera vez. muchas gracias.
Andy Ray
77
Esto todavía no ayuda a responder cómo conectarse a un servidor de base de datos (preferiblemente con contraseñas cifradas). Agradezco su comentario de que esta biblioteca está sobre diseñada, pero seguramente hay una que no lo es. Además, si quisiera escribir mi propio sistema de autenticación, habría usado Struts en Java. Al igual que el OP, quiero saber qué complementos harán eso por mí en 1 línea de código.
hendrixski
44
gran respuesta Nivoc. No funciona con las últimas versiones de connect tho. Tuve que cambiar ... cookieDecoder () -> cookieParser () y bodyDecoder () -> bodyParser () y eliminar la siguiente llamada () de la función helloWorldContent ya que recibía un error 'No se pueden establecer encabezados después de que son enviados '
Michael Dausmann
26

Parece que el complemento connect-auth para conectar el middleware es exactamente lo que necesito: http://wiki.github.com/ciaranj/connect-auth/creating-a-form-based-strategy

Estoy usando express [ http://expressjs.com ] por lo que el complemento de conexión encaja muy bien ya que express está subclasificado (ok - prototipo) de connect

shreddd
fuente
1
oye, ¿tienes un ejemplo de lo que hiciste? simplemente requiriendo connect-auth y llamando ".authenticate" en "req" devuelve "TypeError: Object # no tiene ningún método 'autenticar'" para mí.
Misha Reyzlin
1
En mi humilde opinión, este complemento es demasiado pesado para la autenticación HTTP simple
Matthias
Y este complemento funciona contra la arquitectura de anillo de cebolla de conexión
Matthias
14

Básicamente estaba buscando lo mismo. Específicamente, quería lo siguiente:

  1. Para usar express.js, que envuelve la capacidad de middleware de Connect
  2. Autenticación "basada en formulario"
  3. Control granular sobre qué rutas se autentican
  4. Una base de datos para usuarios / contraseñas
  5. Usar sesiones

Lo que terminé haciendo fue crear mi propia función de middleware check_authque paso como argumento a cada ruta que quiero autenticar. check_authsimplemente verifica la sesión y, si el usuario no ha iniciado sesión, los redirige a la página de inicio de sesión, de la siguiente manera:

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

Luego, para cada ruta, me aseguro de que esta función se pase como middleware. Por ejemplo:

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

Finalmente, necesitamos manejar el proceso de inicio de sesión. Esto es sencillo:

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

En cualquier caso, este enfoque fue diseñado principalmente para ser flexible y simple. Estoy seguro de que hay numerosas formas de mejorarlo. Si tiene alguno, me gustaría mucho su opinión.

EDITAR: Este es un ejemplo simplificado. En un sistema de producción, nunca querrás almacenar y comparar contraseñas en texto plano. Como señala un comentarista, hay bibliotecas que pueden ayudar a administrar la seguridad de la contraseña.

Tom
fuente
2
esto es bueno, excepto que debe usar bcrypt para almacenar la contraseña (no texto sin formato en db). Hay un post bueno aquí sobre ello: devsmash.com/blog/...
Chovy
13

También eche un vistazo a everyauth si desea la integración de inicio de sesión de redes sociales / de terceros.

Peter Lyons
fuente
7

Aquí hay un código para la autenticación básica de uno de mis proyectos. Lo uso contra CouchDB con caché de datos de autenticación adicional, pero eliminé ese código.

Envuelva un método de autenticación alrededor de su manejo de solicitudes y proporcione una segunda devolución de llamada para una autenticación fallida. La devolución de llamada exitosa obtendrá el nombre de usuario como un parámetro adicional. No olvide manejar correctamente las solicitudes con credenciales incorrectas o faltantes en la devolución de llamada fallida:

/**
 * 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
fuente
Quería evitar la autenticación básica a favor de la autenticación basada en formularios. Esta es definitivamente una solución elegante para el problema básico de autenticación. Sin embargo, creo que pude haber encontrado un buen marco de autenticación (connect-auth - se encuentra encima de connectjs)
shreddd
4

Una versión diferente de la autenticación es Passwordless, un módulo de autenticación basado en token para express que evita el problema inherente de las contraseñas [1]. Es rápido de implementar, no requiere demasiados formularios y ofrece una mayor seguridad para el usuario promedio (divulgación completa: soy el autor).

[1]: las contraseñas son obsoletas

florian
fuente
3

Han pasado algunos años y me gustaría presentar mi solución de autenticación para Express. Se llama Lockit . Puedes encontrar el proyecto en GitHub y una breve introducción en mi blog .

Entonces, ¿cuáles son las diferencias con las soluciones existentes?

  • Fácil de usar: configurar su base de datos, la NGP instalar, require('lockit'), lockit(app), hecho
  • rutas ya incorporadas (/ registro, / inicio de sesión, / contraseña olvidada, etc.)
  • vistas ya integradas (basadas en Bootstrap pero puede usar fácilmente sus propias vistas)
  • admite comunicación JSON para sus aplicaciones de página única AngularJS / Ember.js
  • NO es compatible con OAuth y OpenID. Solo usernamey password.
  • Funciona con varias bases de datos (CouchDB, MongoDB, SQL) listas para usar
  • tiene pruebas (no pude encontrar ninguna prueba para Drywall)
  • se mantiene activamente (en comparación con everyauth)
  • proceso de verificación de correo electrónico y contraseña olvidada (envíe un correo electrónico con token, no compatible con Passport)
  • modularidad: use solo lo que necesita
  • flexibilidad: personalizar todas las cosas

Echa un vistazo a los ejemplos .

zemirco
fuente
2

Hay un proyecto llamado Drywall que implementa un sistema de inicio de sesión de usuario con Passport y también tiene un panel de administración de administración de usuarios. Si está buscando un sistema de autenticación y administración de usuarios con todas las funciones similar a algo como lo que Django tiene pero para Node.js, este es el lugar. Me pareció un buen punto de partida para crear una aplicación de nodo que requiriera un sistema de autenticación y gestión de usuarios. Consulte la respuesta de Jared Hanson para obtener información sobre cómo funciona Passport.

lk145
fuente
1

Ejemplo simple y rápido con mongo, para una API que proporciona autenticación de usuario para clientes angulares

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

para su ruta algo como esto:

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

Luego, en las rutas que requieren autenticación, puede verificar la sesión del usuario:

if (!req.session.user) {
    res.send(403);
}
Señor P
fuente
0

Aquí hay una nueva biblioteca de autenticación que usa tokens con marca de tiempo. Los tokens pueden enviarse por correo electrónico o por mensaje de texto a los usuarios sin la necesidad de almacenarlos en una base de datos. Se puede usar para la autenticación sin contraseña o para la autenticación de dos factores.

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

Divulgación: soy el desarrollador de esta biblioteca.

sffc
fuente
0

Si necesita autenticación con SSO (inicio de sesión único) con la cuenta de usuario de Microsoft Windows. Puede intentar https://github.com/jlguenego/node-expose-sspi .

Le dará un req.ssoobjeto que contiene toda la información del usuario del cliente (inicio de sesión, nombre para mostrar, sid, grupos).

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

Descargo de responsabilidad: soy el autor de node-expose-sspi.

jlguenego
fuente
0

dulce-auth

Un módulo de autenticación de usuario ligero y de configuración cero. No necesita una base de datos sperate.

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

Es simple como:

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
fuente