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.
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.
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.
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 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.
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.