skynyrd
12/13/2016 - 5:50 PM

Create and consume a token using simple-jwt and PassportJS in ExpressJS

Create and consume a token using simple-jwt and PassportJS in ExpressJS

JWT Reminder Gist

Implementing Authentication and Authorization in Express using Simple-JWT, Passport and Bcrypt

Creating a token

In your config.js

module.exports = {
  secret: 'YOUR_SECRET'
}

Simple token creator

const jwt = require('jwt-simple');
const User = require('path');
const config = requer('path');

function tokenForUser(user){
  const timestamp = new Date().getTime();
  return jwt.encode({
    sub: user.id, //sub: subject in jwt standard
    iat: timestamp //iat: issued at time jwt standard
    }, config.secret);
}

Consuming a Token

For Signing Up

Passport JS configuration:

const passport = require('passport');
const User = require('path');
const config = require('path');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;

// Setup options for JWT Strategy
const jwtOptions = {
  jwtFromRequest: ExtractJwt.fromHeader('authorization'),  // Check auth header
  secretOrKey: config.secret // decode with secret
};

// Create JWT Strategy
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done){
  // payload: decoded token
  // See if the user id in the payload exists in our db
  // If it does, call 'done' with that other
  // else, call done without a user object
  
  User.findById(payload.sub, function(err, user){
    if(err){return done(err, false);}
    if(user){
      done(null, user);
    }
    else{
      done(null, false);
    }
  });
});

// Tell passport to use this strategy
passport.use(jwtLogin);

Use strategy in your controller:

const passport = require('passport');

const requireAuth = passport.authenticate('jwt', { session: false } // do not create session

...
app.get('/', requireAuth, function(req, res){
  res.send({ hi: 'there' });
});
...
For Signing In:

Add Compare Passwords method to your Mongoose object schema
Example Gist

userSchema.methods.comparePassword = function(candidatePassword, callback){
  bcrypt.compare(candidatePassword, this.password, function(err, isMatch){
    if(err) { return callback(err) }
    callback(null, isMatch);
  }
}

Add local strategy to passport config

const LocalStrategy = require('passport-local');

//Create local strategy
const localOptions = { usernameField: 'email'}; //in sign in request model, username field is: "email"
const localLogin = new LocalStrategy(localOptions, function(email, passport, done){
  // Verify this username and password, call done with the user
  // if it is the correct username and password,
  // call done
  // else call done with false
  
  User.findOne({email: email}, function(err,user) {
    if(err) { return done(err); }
    if(!user) { return done(null, false); }
    
    // compare passwords
    user.comparePassword(password, function(err, isMatch){
      if(err) { return done(err); }
      if(!isMatch) { return done(null,false);}
      
      return done(null, user);
    }
  });
});

passport.use(localLogin);

Use it in your signin endpoint:

const requireSignin = passport.authenticate('local', {session: false});

...

app.post('/signin', requireSignin, function(req, res, next){
  // User had already had their email and passowrd auth'd
  // We just need to give them a token
  res.send({ token: tokenForUser(req.user)};
});

Good to go!