pasaporte-local con node-jwt-simple


87

¿Cómo puedo combinar passport-local para devolver un token JWT en una autenticación exitosa?

Quiero usar node-jwt-simple y mirando passport.js no estoy seguro de cómo hacerlo.

var passport = require('passport')
  , LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function(err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    });
  }
));

¿Es posible devolver el token al llamar a done ()? Algo como esto ... (solo pseudo código)

if(User.validCredentials(username, password)) {
  var token = jwt.encode({username: username}, tokenSecret);
  done(null, {token : token}); //is this possible?
}

Si no es así, ¿cómo puedo devolver el token?

Respuestas:


123

¡Me lo imaginé!

En primer lugar, debe implementar la estrategia correcta. En mi caso, LocalStrategy, y debe proporcionar su lógica de validación. Por ejemplo, usemos el de passport-local.

var passport = require('passport')
  , LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function(err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    });
  }
));

la devolución de llamada de verificación que proporcione function(username, password, done)se encargará de encontrar a su usuario y verificar si la contraseña coincide (más allá del alcance de la pregunta y mi respuesta)

passport.js espera varias piezas para que funcione, una es que devuelva al usuario en la estrategia. Estaba tratando de cambiar esa parte del código y eso estaba mal. La devolución de llamada espera falsesi la validación falla y un object(el usuario validado) si tiene éxito.

Ahora ... ¿cómo integrar JWT?

En su ruta de inicio de sesión, tendrá que manejar una autenticación exitosa o una no exitosa. Y es aquí donde debe agregar la creación del token JWT. Al igual que:

(recuerde deshabilitar la sesión, de lo contrario, tendrá que implementar las funciones de serialización y deserialización. Y no las necesita si no persiste la sesión, lo cual no es así si está utilizando una autenticación basada en token)

De ejemplos de pasaporte local: (con el token JWT agregado)

// POST /login
//   This is an alternative implementation that uses a custom callback to
//   achieve the same functionality.
app.post('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) { return next(err) }
    if (!user) {
      return res.json(401, { error: 'message' });
    }

    //user has authenticated correctly thus we create a JWT token 
    var token = jwt.encode({ username: 'somedata'}, tokenSecret);
    res.json({ token : token });

  })(req, res, next);
});

¡Y eso es todo! Ahora, cuando llame / inicie sesión y POST nombre de usuario y contraseña (que siempre debe ser sobre SSL), el primer fragmento de código anterior intentará encontrar un usuario en función del nombre de usuario que proporcionó y luego comprobará que la contraseña coincida (por supuesto, deberá cámbielo para adaptarlo a sus necesidades).

Después de eso, se llamará a su ruta de inicio de sesión y allí podrá encargarse de devolver un error o un token válido.

Espero que esto ayude a alguien. Y si he cometido algún error u olvido algo, avíseme.


3
Del pasaporte BasicStrategy o DigestStrategy son otras dos opciones. Sin embargo, no parece haber una gran diferencia entre las estrategias básicas y locales, ya que ninguna de las dos necesita sesiones para funcionar, solo que Local solicita URL de redireccionamiento (lo que lo hace un poco menos compatible con API).
funseiki

1
Hola @cgiacomi, ¿podrías dar un ejemplo de una ruta que verifique el token?
Matt Kim

3
Hey @ matt-kim, en realidad no guardo la ficha, es transitoria. No sé si es la mejor manera o no, pero esto es lo que hago: el usuario se autentica, genero el token y se lo devuelvo al cliente. El token se almacena en localStorage si el cliente es un sitio web, o puede almacenarlo en una aplicación de iPhone / Android. Cuando un cliente tiene que realizar una solicitud de un recurso, envía el token guardado al backend. Passport se encargará del token. Aquí hay un resumen de la estrategia Bearer para manejar el token gist.github.com/cgiacomi/cd1efa187b8cccbe2a61 ¡ Espero que esto ayude! :)
cgiacomi

1
¡Hola @cgiacomi! tal vez sea obvio, pero ¿podría describir cómo deshabilita las sesiones cuando usa la devolución de llamada personalizada?
MrMuh

2
@MrMuh revisa el enlace gist.github.com/cgiacomi/cd1efa187b8cccbe2a61 en mi comentario, muestro cómo deshabilitar las sesiones: passport.authenticate ('bearer', {session: false})
cgiacomi

18

Esta es una gran solución, solo quiero agregar esto:

var expressJwt = require('express-jwt');

app.use('/api', expressJwt({secret: secret}));

Me gusta usar "express-jwt" para validar el token.

Por cierto: este artículo es excelente para aprender cómo manejar el token en el lado del cliente, usando Angular, para enviarlo de vuelta con cada solicitud

https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/


2
Solo solía express-jwthacer autenticación, pero leyendo la documentación de otros paquetes como passport-jwt, creo que seguiré cumpliendo express-jwt. Mucho más simple, mucho más agradable OMI
bobbyz

Solo un FYI express-jwt no proporciona soporte para tokens de actualización.
user3344977

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.