When building an API, there are times when you won’t want for your API to be publicly accessible to everyone. Whether it’s having a simple username and password, or a check that verifies that someone is a paid customer, you will need a way to guard your routes from users that aren’t authenticated. One of the best ways to do this is by using JWT. JWT is a stateless token that checks with your back-end system to validate your user requests.

In this example, I’ll be using my express ES6 blog API from a previous post here. The github repo can also be found here.

Project Setup

Assuming that you already have an API in place, you’ll want to add dependencies for authentication.

npm install –save passport passport-local passport-local-mongoose

Passport is a tool for authentication Node.js applications while Passport-Local and Passport-Local-Mongoose help to add the ability to use simple username and password authentication. Passport-Local-Mongoose specifically handles the passport hashing and salt in your User Document in Mongoose.

Now that we have those installed, we will walk to pull in dependencies for handling our JWT creating and authentication with express.

npm install –save express-jwt jsonwebtoken passport-jwt

Passport-jwt adds middleware to the Passport to allows for it to accept JSON web tokens as a valid authentication type. Now that we have all of our dependencies pulled in, let’s add Passport to our application.

First, we need to create a User model to represent our stored user and credential in our database.

import mongoose from 'mongoose';
const Schema = mongoose.Schema;
import passportLocalMongoose from  'passport-local-mongoose';


let userSchema = new Schema({
    firstName: String,
    lastName: String,
    email: String,
    password: String
});

userSchema.plugin(passportLocalMongoose);

let User = mongoose.model('User',userSchema);

export default User;

As you can see, we add our email and passport to the model and then right under it, we ass the passport-local-mongoose plugin in order to handle our password hashing. Even though this can be handled manually, you should follow the principles of DRY. If there is a package to handle a common task, don’t re-invent the wheel.

Now that we have our User model created, we can now add that to our passport pipeline and configure passport to connect with Express.

Add these two import statements to your server.js (or app.js )file

import passport from 'passport';
import User from './models/user';

Then choose the method of authentication and JWT configuration you wish to use.

const passportJWT = require("passport-jwt");
const JWTStrategy   = passportJWT.Strategy;
const ExtractJWT = passportJWT.ExtractJwt;

const LocalStrategy = require('passport-local').Strategy;
server.use(passport.initialize());
passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password'
    },
    User.authenticate()
));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
passport.use(new JWTStrategy({
        jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
        secretOrKey   : 'ILovePokemon'
    },
    function (jwtPayload, cb) {

        //find the user in db if needed. This functionality may be omitted if you store everything you'll need in JWT payload.
        return User.findById(jwtPayload.id)
            .then(user => {
                return cb(null, user);
            })
            .catch(err => {
                return cb(err);
            });
    }
));

As you can see here, we initialize an instance of passport, set the strategy to local, and then set the fields that we wish to use as well as the User model. The local Strategy just means using username and password.

Right after that, we tell passport how we plan to serials and deserialize our user, and how to configure our JWT with our JWT strategy. ExtractJWT.fromAuthHeaderAsBearerToken(), specifies that JWT tokens will be sent as Bearer tokens in incoming HTTP requests and that our secret key for encrypting our tokens is stored in secretOrKey.

Setting up User Login and JWT Authentication 

How that we’ve set up passport, we will want to create controllers handle our registrations of users and our login that returns a valid JWT.

Create a controller called auth.controller.js and import the following

import User from '../models/user';
import bodyParser from 'body-parser';

import passport from 'passport';
const AuthController = {};
import jwt from 'jsonwebtoken';

We import passport, our model as well as jsonwebtoken as a method of signing our tokens.

We also create an object called AuthController to export later into other files.

Now that that is done, create the function to register the user.

AuthController.register = async (req, res) => {
    try{
        User.register(new User({ username: req.body.email,
            firstName: req.body.firstName,
            lastName: req.body.lastName,
            }), req.body.password, function(err, account) {
            if (err) {
                return res.status(500).send('An error occurred: ' + err);
            }

            passport.authenticate(
                'local', {
                    session: false
                })(req, res, () => {
                res.status(200).send('Successfully created new account');
            });
        });
    }
    catch(err){
        return res.status(500).send('An error occurred: ' + err);
    }
};

 

The User.register() method takes in a new User model, the password and the mother of authentication as parameters.

Now create the method to login

AuthController.login = async (req, res, next) => {
    try {
        if (!req.body.email || !req.body.password) {
            return res.status(400).json({
                message: 'Something is not right with your input'
            });
        }
        passport.authenticate('local', {session: false}, (err, user, info) => {
            if (err || !user) {
                return res.status(400).json({
                    message: 'Something is not right',
                    user   : user
                });
            }
            req.login(user, {session: false}, (err) => {
                if (err) {
                    res.send(err);
                }
                // generate a signed son web token with the contents of user object and return it in the response
                const token = jwt.sign({ id: user.id, email: user.username}, 'ILovePokemon');
                return res.json({user: user.username, token});
            });
        })(req, res);
    }
    catch(err){
        console.log(err);
    }
};

This checks that the email and password fields are not empty.  This is using the passport-local authentication to check if it’s a valid user. Then it signs a token using that users information and returns that token as json.

Guarding routes

Now that we have the auth methods created, we’re going to have to create routes for those.

Create the two files auth.routes.js and user.routes.js

import { Router } from 'express';
import AuthController from '../controllers/auth.controller';

const router = new Router();

router.post('/register', (req, res) => {
    AuthController.register(req, res);
});

router.post('/login', (req, res, next) => {
    AuthController.login(req, res, next);
});

export default router;

 

Here you can see that we import Express and get the router to later export. We also create Post routes for our AuthController.

In our user.routes.js file

import { Router } from 'express';
const router = new Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
    res.send('respond with a resource');
});

/* GET user profile. */
router.get('/profile', function(req, res, next) {
    res.send(req.user);
});

export default router;

This returns the user that made the request.

Now if we go back to our server.js file we can add those route files to our express pipeline

import user from './routes/user.routes';
import auth from './routes/auth.routes';

server.use('/auth', auth);
server.use('/user', passport.authenticate('jwt', {session: false}), user);

Here we pass in passport as our method of authentication. The user must now have a JWT token in every header in order to make requests to that controller.

User Creation

User Creation

User Login

User Login

JWT user profile

JWT user profile

Conclusion

On top of having a JWT token in the header, you can also use this as a method of creating MongoDB documents that are related to a user. An example of this is having an authenticated user’s ID associated with a post or comment upon creation. Check out the GitHub repository for the full source code of this tutorial.

Codebrains Newsletter

Get weekly dev news and tutorials.

Powered by ConvertKit