passport-local com node-jwt-simple

87

Como posso combinar passaporte-local para retornar um token JWT na autenticação bem-sucedida?

Eu quero usar node-jwt-simple e olhando para passport.js não tenho certeza de como fazer.

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

É possível retornar o token ao chamar done ()? Algo assim ... (apenas pseudo código)

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

Se não, como posso devolver o token?

Cgiacomi
fonte

Respostas:

123

Eu descobri!

Em primeiro lugar, você precisa implementar a estratégia correta. No meu caso, LocalStrategy, e você precisa fornecer sua lógica de validação. Por exemplo, vamos usar o do passaporte 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);
    });
  }
));

o retorno de chamada de verificação que você fornecer function(username, password, done)se encarregará de encontrar seu usuário e verificar se a senha corresponde (além do escopo da pergunta e da minha resposta)

passport.js espera várias peças para que funcione, uma delas é que você retorne o usuário na estratégia. Eu estava tentando mudar essa parte do código e estava errado. O retorno de chamada espera falsese a validação falhar e um object(o usuário validado) se você for bem-sucedido.

Agora .... como integrar o JWT?

Em sua rota de login, você terá que lidar com uma autenticação bem ou malsucedida. E é aqui que você precisa adicionar a criação do token JWT. Igual a:

(lembre-se de desabilitar a sessão, caso contrário, você terá que implementar as funções de serializar e desserializar. E você não precisa delas se não estiver persistindo a sessão, o que não será feito se estiver usando uma autenticação baseada em token)

A partir de exemplos de passaportes locais: (com o token JWT adicionado)

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

E é isso! Agora, quando você chama / login e faz POST de nome de usuário e senha (que sempre deve ser por SSL), o primeiro trecho de código acima tentará encontrar um usuário com base no nome de usuário fornecido e, em seguida, verificará se a senha corresponde (é claro que você precisará altere isso para atender às suas necessidades).

Depois disso, sua rota de login será chamada e você poderá cuidar de retornar um erro ou um token válido.

Espero que isso ajude alguém. E se eu tiver cometido algum erro ou esquecido algo, me avise.

Cgiacomi
fonte
3
BasicStrategy ou DigestStrategy do Passport são duas outras opções. Não parece haver uma grande diferença entre as estratégias Básica e Local, entretanto, uma vez que nenhuma das sessões precisa funcionar - apenas que o Local pede URLs de redirecionamento (tornando-o um pouco menos amigável à API).
funseiki
1
Ei @cgiacomi você poderia dar um exemplo de uma rota que verifica o token?
Matt Kim
3
Ei @ matt-kim, na verdade eu não salvo o token, é temporário. Não sei se é a melhor forma ou não mas eu faço o seguinte: O usuário autentica, e eu gerei o token e o devolvo ao cliente. O token é armazenado em localStorage se o cliente for um site ou você pode armazená-lo em um aplicativo para iPhone / Android. Quando um cliente precisa fazer uma solicitação de recurso, ele envia o token salvo para o back-end. O Passport tratará do token. Aqui está um resumo da estratégia do Bearer para lidar com o token gist.github.com/cgiacomi/cd1efa187b8cccbe2a61 Espero que isso ajude! :)
cgiacomi
1
Ei @cgiacomi! talvez seja óbvio, mas você poderia descrever como desabilitar as sessões ao usar o retorno de chamada personalizado?
MrMuh
2
@MrMuh confira o link gist.github.com/cgiacomi/cd1efa187b8cccbe2a61 em meu comentário, mostro como desabilitar sessões: passport.authenticate ('portador', {sessão: falso})
cgiacomi
18

Esta é uma ótima solução, eu só quero adicionar isto:

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

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

Eu gosto de usar "express-jwt" para validar o token.

btw: este artigo é ótimo para aprender como lidar com o token no lado do cliente, usando Angular, a fim de enviá-lo de volta a cada solicitação

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

ZeroCR
fonte
2
Eu só costumava express-jwtfazer autenticação, mas lendo documentação de outros pacotes, como passport-jwt, acho que vou continuar express-jwt. Muito mais simples, muito mais agradável IMO
bobbyz
Apenas um express-jwt FYI não fornece suporte para tokens de atualização.
user3344977