passport-local with node-jwt-simple

39,116

Solution 1

I figured it out!

First of all you need to implement the correct strategy. In my case LocalStrategy, and you need to provide your validation logic. For example sake let's use the one in 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);
    });
  }
));

the verify call back you provide function(username, password, done) will take care of finding your user and checking if the password matches (beyond the scope of the question and my answer)

passport.js expects several pieces for it to work, one is that you return the user in the strategy. I was trying to change that part of the code, and that was wrong. The callback expects false if the validation fails and an object (the validated user) if you are successful.

Now.... how to integrate JWT?

In your login route you will have to handle a successful auth or an unsuccessful one. And it is here that you need to add the JWT token creation. Like so:

(remember to disable the session, otherwise you will have to implement the serialize and deserialize functions. And you don't need those if you are not persisting the session, which you are not if you are using a token based auth)

From passport-local examples: (with the JWT token added)

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

And that is it! Now when you call /login and POST username and password (which should always be over SSL) the first code snippet above will try to find a user based on the username you provided and then check that the password matches (Of course you will need to change that to suit your needs).

After that your login route will be called and there you can take care of returning an error or a valid token.

Hope this will help someone. And if I have made any mistakes or forgot something let me know.

Solution 2

This is a great solution, I just want to add this:

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

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

I like to use "express-jwt" to validate the token.

btw: this article is great to learn how to handle the token in the client side, using Angular, in order to send it back with every request

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

Solution 3

Here's a boiler-plate I'm working on for specifically using api tokens only (no sessions...not that session are bad of course; just we're using token approach): https://github.com/roblevintennis/passport-api-tokens

Share:
39,116
cgiacomi
Author by

cgiacomi

Updated on January 26, 2020

Comments

  • cgiacomi
    cgiacomi over 4 years

    How can I combine passport-local to return a JWT token on successful authentication?

    I want to use node-jwt-simple and looking at passport.js I am not sure how to go about.

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

    Is it possible to return the token when calling done() ? Something like this... (just pseudo code)

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

    If not, how can I return the token?

  • funseiki
    funseiki about 10 years
    Passport's BasicStrategy or DigestStrategy are two other options. There doesn't seem to be a huge difference between Basic and Local strategies, though, since neither need sessions to work - just that Local asks for redirect URLs (making it slightly less API friendly).
  • Matt Kim
    Matt Kim almost 10 years
    Hey @cgiacomi could you give an example of a route that checks the token?
  • cgiacomi
    cgiacomi almost 10 years
    Hey @matt-kim actually I don't save the token, it's transient. I don't know if it the best way or not but this is what I do: The user authenticates, and I generate the token and return it to the client. The token is stored in localStorage if the client is a website, or you could store it in an iPhone/Android app. When a client has to make a request for a resource it sends the saved token to the backend. Passport will handle the token. Here is a gist with the Bearer strategy to handle the token gist.github.com/cgiacomi/cd1efa187b8cccbe2a61 Hope this helps! :)
  • MrMuh
    MrMuh over 9 years
    Hey @cgiacomi! maybe it is obvious, but could you describe how you disable the sessions when using the custom callback?
  • Jobsamuel
    Jobsamuel over 9 years
    Have you tried using a twitter strategy? I haven't figure it out how to implement passport-twitter with jwt.
  • cgiacomi
    cgiacomi over 9 years
    @Jobsamuel No I am sorry, I haven't tried any other strategy. The reason being that I wanted my clients to authenticate using email and password, which would result in a token that could be used in subsequent API calls to my REST backend. This would allow me to use for example, Angular from a browser and native iPhone and Android apps. Yet the authentication mechanism would be the same no matter what the client is.
  • cgiacomi
    cgiacomi over 9 years
    @MrMuh checkout the link gist.github.com/cgiacomi/cd1efa187b8cccbe2a61 in my comment I show how to disable sessions: passport.authenticate('bearer', { session: false })
  • Mark Chorley
    Mark Chorley about 8 years
    There is now a passport jwt strategy at npmjs.com/package/passport-jwt
  • bobbyz
    bobbyz about 8 years
    I've only used express-jwt to do authentication, but reading through documentation of other packages such as passport-jwt, I think I'll be sticking to express-jwt. Much simpler, much nicer IMO
  • user3344977
    user3344977 over 7 years
    Just an FYI express-jwt does not provide support for refresh tokens.