Outline

Configuring Passport for Local Authentication

Passport is an authentication system made for Node.js. We'll have to make some changes to our application to authenticate with JWT's since Passport uses session authentication by default. If you're not familiar with the differences between token and/or session based authentication, we highly recommend reading this post.

We'll be using passport as a middleware for our login endpoint. This middleware will use the passport-local strategy, which is meant for username/password authentication. We'll need to look up our user using the information in the request body and try to find the corresponding user, then see if the password given to the user was valid.

Configure Passport using the passport-local strategy.

Create a new file in config/passport.js with the following:

var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var User = mongoose.model('User');

passport.use(new LocalStrategy({
  usernameField: 'user[email]',
  passwordField: 'user[password]'
}, function(email, password, done) {
  User.findOne({email: email}).then(function(user){
    if(!user || !user.validPassword(password)){
      return done(null, false, {errors: {'email or password': 'is invalid'}});
    }

    return done(null, user);
  }).catch(done);
}));

LocalStrategy usually looks at the username and password field of the request body coming from the front-end. We're expecting our request body from the front-end will be in the format of:

{
  "user": {
    "email": "jake@example.com".
    "password": "mypasswordisjake"
  }
}

so we provided the following configuration the passport-local strategy

{
  usernameField: 'user[email]',
  passwordField: 'user[password]'
}

to get the correct fields in our callback.

To use the passport middleware, we'll need to require it in app.js before our routes.

Register passport with our application
require('./models/User');
+require('./config/passport');

app.use(require('./routes'));

Configuring Middleware to Validate JWT's

There are two different cases in our applications for handling JWT's which we'll create separate middleware for optional authentication and required authentication. Authentication will be required for routes that require a logged-in user, such as feed (since we can't have a personalized feed for someone who can't be identified). When a JWT isn't provided to a route that requires authentication, the route will fail with a 401 status code. Authentication will be optional for some routes that are exposed to the public, such as fetching the global list of articles. The route will still respond if it's optionally authenticated, otherwise,if a JWT is provided, the user's data will be used to enhance the data being returned (such as if they've favorited an article or not). We'll be using the express-jwt package to validate JWT's.

Create a new file in routes/auth.js with the following:
var jwt = require('express-jwt');
var secret = require('../config').secret;

Since our JWT's were generated with the secret in config/index.js (the generateJWT() method we made on the user model uses it), we'll need to use the same secret to validate the JWT tokens from the front-end. To handle the two different authentication cases (optional and required), we'll need to export two separately configured express-jwt middlewares. You can view the configuration options for express-jwt here

Create a route middleware to handle decoding JWT's

Create routes/auth.js with the following:

var jwt = require('express-jwt');
var secret = require('../config').secret;

+function getTokenFromHeader(req){
+  if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Token') {
+    return req.headers.authorization.split(' ')[1];
+  }
+
+  return null;
+}
+
+var auth = {
+  required: jwt({
+    secret: secret,
+    userProperty: 'payload',
+    getToken: getTokenFromHeader
+  }),
+  optional: jwt({
+    secret: secret,
+    userProperty: 'payload',
+    credentialsRequired: false,
+    getToken: getTokenFromHeader
+  })
+};
+

The getTokenFromHeader() function is a helper function that both middlewares use to extract the JWT from the Authorization header. The only difference between the required and optional middlewares is that the optional middleware is configured with credentialsRequired: false so that requests without a token will still succeed. userProperty is the property where the JWT payloads will be attached to each request, so we can access the data using req.payload.

Finally, we'll have to export our middlewares in order for the rest of our application to use them.

Export the auth object

At the bottom of auth.js, add the following:

module.exports = auth;

We should have all the middleware necessary to implement authentication now! To recap, we configured Passport to authenticate users with email and password, and then we made two JWT middlewares, one for requests that require authentication, and the other for requests where authentication is optional.

 

I finished! On to the next chapter