The page is loading...

Creating a Role-Based User Authentication System with Angular, Express and MySQL — Part 1: Setting up the Backend

In this series, we will go through the process of creating role-based user authentication system using Express, MySQL, Sequelize, Passport and JSON Web Tokens (JWT) on the backend and use Angular 1.x to drive our application’s front-end.

Our goal by the end of this tutorial is to create a Single Page Application with a RESTful API that:

  • Allows users to sign-up and login.
  • Supports multiple user roles. (Guest, User, Admin)
  • Restricts route access based on user role.

This post is the first part of the Creating a Role-Based User Authentication System with Angular, Express and MySQL series.

GitHub Repository

The code from this post is available on the part-1 branch in the project’s GitHub repository.

View on GitHub

Prerequisites:

  • Git – 2.10
  • Node – 6.9
  • NPM – 3.10
  • MySQL – 5.6
  • Visual Studio Code – 1.10

Make sure you download and install everything listed above. As a code editor, I decided to try out Visual Studio Code — and it’s really nice — but you can use whatever you like.

Quick note on MySQL: You can download and install the standalone version of the MySQL server or use one from a bundle like WAMP or MAMP.

Project and Database Setup

Let’s start by creating our empty project folder somewhere on our drive. I’ll call my project folder james-auth since we’ll be working with JWTs, Angular, MySQL, Express and Sequelize, but you can name yours however you’d like. — Maybe Natalie? … No?

Inside our little project’s folder run npm init -y to generate our package.json file. You can skip the -y flag if you feel more specific about your project and you also happen to like answering a bunch of questions.

Finally, connect to your MySQL server and create the james_auth database.

Folder Structure

For the sake of simplicity, I won’t bother with the folder structure much. There are a plethora of great resources on the subject and you can follow which one fits you best. As you can see, I’m going for a dumbed down version of the MVC structure here.

/james-auth
├── app
│   ├── /controllers
│   ├── /models
│   ├── /views
│   ├── /services
│   ├── /routes
│   ├── server.js
│   └── ...
├── public
│   ├── /app
│   │   ├── /controllers
│   │   ├── /views
│   │   ├── /services
│   │   └── app.js
│   ├── /assets
│   │   └── /css
└── ...

Installing Dependencies

In order to ensure maximum compatibility with the tutorial and make sure we’re on the same page, it would be best if you used the same package versions I used while writing the tutorial. However, if you want to use the latest stable versions just remove the @ parts.

npm i -S [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]

Setting Up the Server

Let’s start by creating our server.js file inside the /app folder — if not already created — and set-up our basic server. I included numbered comments for each block so we can go through the code easier next.

# app/server.js
'use strict';

// 1: NPM dependencies.
var express = require('express'),
    bodyParser = require('body-parser'),
    morgan = require('morgan'),
    sequelize = require('sequelize'),
    passport = require('passport'),
    jwt = require('jsonwebtoken');

// 2: App related modules.
// ... Nothing here, yet!

// 3: Initializations.
var app = express();

// 4: Parse as urlencoded and json.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 5: Hook up the HTTP logger.
app.use(morgan('dev'));

// 6: Hook up Passport.
app.use(passport.initialize());

// 7: Set the static files location.
app.use(express.static(__dirname + '../../public'));

// 8: Home route.
app.get('/', function(req, res) {
    res.send('Nice meeting you wizard, I\'m Gandalf!');
});

// 9: Start the server.
app.listen('8080', function() {
    console.log('Magic happens at http://localhost:8080/! We are all now doomed!');
});

Now, let’s go through all the stuff that happens above step by step and see what everything does.

  1. First, we’re including some of the NPM dependencies we’ve just installed a few minutes ago.

  2. I like separating the NPM modules from the app related modules so in step 2 I left some space for future inclusions.

  3. Next, we initialize our Express app.

  4. The next lines hook up the bodyParser.urlencoded() and bodyParser.json() middleware methods. These are basically telling Express to parse the data as URL encoded data — how browsers use to send the data from forms — and also as JSON.

  5. In the next step, we hook up the morgan HTTP request logger middleware and set the format for development use.

  6. Next, we initialize and hook up the Passport authentication middleware.

  7. We then set up the static file locations from which we’ll later serve our front-end content.

  8. We create our home route and return a string.

  9. Finally, we listen for connections on localhost:8080.

Open your terminal of choice, navigate to your project’s location and start the app by running node app/server.js. Now, if you point your browser to localhost:8080 and your browser welcomes you similar to the screenshot below, you’re a wizard! Congrats! You should stop coding altogether and pursue your career as a wizard.

The Config File

“Everything has its beauty, but there’s nothing like config files.” — Confucius

No one could have said it better than Confucius himself. So let’s be wise for a moment and make a config.js file inside the james-auth/app folder. In it, we’ll store our app’s config data for future reference.

# app/config.js
// Application configuration.
'use strict';

var config = module.exports;

config.db = {
    user: 'root', 
    password: 'root',
    name: 'james_auth'
};

config.db.details = {
    host: 'localhost',
    port: 8889,      
    dialect: 'mysql'
};

config.keys = {
    secret: '/jVdfUX+u/Kn3qPY4+ahjwQgyV5UhkM5cdh1i2xhozE=' // Not anymore...
};

There’s nothing much going on there other than setting up the MySQL credentials and the name of the database we created earlier. Below, I set the host, port, and dialect in a separate key of the config.db object. It’s not much of a personal preference the way I did that, it’s just for convenience so I could later pass the entire config.db object to Sequelize.

Finally, I set the secret key — which can be anything — and which we’ll be using later for signing our JWTs. I believe there are more secure ways of handling secret keys, but for now, I’ll dial my paranoia down and just keep it here.

The Database Service

In order to access the database easier, let’s create a database.js file inside the app/services folder and in it, let’s create a new Sequelize instance.

# app/services/database.js
'use strict';

var config = require('./../config'),
    Sequelize = require('sequelize');

module.exports = new Sequelize(
    config.db.name,
    config.db.user,
    config.db.password,
    config.db.details
);

We’re requiring the config file, the Sequelize package and exporting a new instance of it using the database credentials we set up earlier. That’s it! Simple, right?

The User Model

At last! The fun part we’ve been waiting for so long! We’ve spent the last couple of minutes working hard in order to have a decent project structure and now we’re finally ready to get our hands dirty with something more interesting than configuration.

Inside our app/models let’s create a user.js file and inside define our User model.

# app/models/user.js
// The User model.
'use strict'; 

var Sequelize = require('sequelize'),
    bcrypt = require('bcrypt');

var config = require('../config'),
    db = require('../services/database');

// 1: The model schema.
var modelDefinition = {
    username: {
        type: Sequelize.STRING,
        unique: true,
        allowNull: false
    },

    password: {
        type: Sequelize.STRING,
        allowNull: false
    }
};

// 2: The model options.
var modelOptions = {
    instanceMethods: {
        comparePasswords: comparePasswords
    },
    hooks: {
        beforeValidate: hashPassword
    }
};

// 3: Define the User model.
var UserModel = db.define('user', modelDefinition, modelOptions);

// Compares two passwords.
function comparePasswords(password, callback) {
    // TODO: Password comparison logic.
}

// Hashes the password for a user object.
function hashPassword(user) {
    // TODO: Password hashing logic.
}

module.exports = UserModel;

That’s a bunch of code. I’ll be skipping the require() statements at the top as they’re pretty self explanatory. Let’s go through everything else step by step:

  1. In the modelDefinition object we define the username and password objects which will eventually become columns in our DB’s users table. Both use the Sequelize.STRING type and have the allowNull key set to false, as we won’t allow NULL values. The only key difference between the two is that the username has an extra unique key set to true, and this is to prevent duplicate usernames.

  2. Inside the modelOptions object we define an object for instanceMethods and another one for hooks. Namings are important here as they are key objects used by Sequelize behind the scenes.

  3. Finally, we use our database service to db.define() our UserModel by passing three parameters. The first one —in our case user represents the model name (singular). It will eventually become the users table in our database (notice, plural). The second and the third parameters are the modelDefinition and the modelOptions objects, both defined earlier.

I feel bad for that confusion cloud I just sent in your direction, so next, I’ll do my best to clear it out:

  • Instance Methods are helper methods declared during model definition which can be called later on model instances.

  • Hooks are callbacks called by Sequelize during specific lifetime events. In our example, we’re using only the beforeValidate hook which — as the name implies – is run before model validation.

Following our model definition, we create our comparePasswords instance method and the hashPassword hook used above inside our modelOptions object. As you can see above they’re both empty for now, let’s fix that.

Comparing Passwords

function comparePasswords(password, callback) {
    bcrypt.compare(password, this.password, function(error, isMatch) {
        if(error) {
            return callback(error);
        }

        return callback(null, isMatch);
    });
}

The comparePasswords function accepts two arguments, the password that needs to be compared with and the callback that will be called after the comparison. The logic is very simple here as we’re using bcrypt to compare the two passwords — the model’s password ( this.password ) and the one that needs to be compared with (the password argument).

Hashing Passwords

function hashPassword(user) {
    if(user.changed('password')) {
        return bcrypt.hash(user.password, 10).then(function(password) {
            user.password = password;
        });
    }
}

The hashPassword function accepts the user instance as parameter and checks if user.changed('password'). If so, it returns the promise of hashing and updating the user model’s password.

This concludes our UserModel definition.

Setting the Passport JWT Strategy

Passport.js is a Node.js authentication middleware that we’ll use to protect our valuable routes. It supports a huge amount of authentication strategies — 300+ at the time of writing — including Twitter, Facebook, Auth0 so forth and so on. You can learn more about it and the available strategies on the official website.

We already hooked it up a while ago during the 6th step of our app/server.js creation process. Now it’s time to configure the passport-jwt strategy which we already installed earlier.

First, let’s proceed by creating a passportStrategy.js file inside our app/services folder.

The Passport strategy configuration isn’t exactly a service per se and the location I’ve chosen here isn’t the best, but for our small API, it’s ok to keep it there for now. I encourage you to consider improving your project’s structure if you’re planning on extending it more than the purpose of this guide.

Inside our passportStrategy.js let’s include the dependencies and modules we’ll use and set up some boilerplate code.

# app/services/passportStrategy.js
'use strict';

var JWTStrategy = require('passport-jwt').Strategy,
    ExtractJwt = require('passport-jwt').ExtractJwt;

var User = require('./../models/user'),
    config = require('./../config');

// Hooks the JWT Strategy.
function hookJWTStrategy(passport) {
    // TODO: Set up Passport to use the JWT Strategy.
}

module.exports = hookJWTStrategy;
  1. As you can see at the very top we include the JWT Strategy as JWTStrategy and the ExtractJwt helper. We’ll go through these in a few moments. Right below we also include our User model and the config file.

  2. Following that, we created an empty function that accepts passport as an argument and is supposed to actually set it up to use the JWT Strategy.

  3. Finally, we export the hookJWTStrategy function for later use.

Let’s leave everything as it is for now here and hook the boilerplate code we wrote so we actually receive the passport object. We will come back shortly to complete the hookJWTStrategy function.

Inside our app/server.js file we need to include the passportStrategy.js module we’ve made and make sure that passport is passed through. At the top of our server.js file we left some space earlier for app specific modules, so let’s proceed by including the hookJWTStrategy function there.

# app/server.js
// ...

var hookJWTStrategy = require('./services/passportStrategy');

// ...

Following that, right below we initialize and hook the Passport middleware, let’s pass the passport object to our hookJWTStrategy.

# app/server.js
// ...

// 6: Hook up Passport.
app.use(passport.initialize());

// Hook the passport JWT strategy.
hookJWTStrategy(passport);

// ...

Well, this was simple enough and now we have the passport object inside our hookJWTStrategy function the way we wanted, great! Let’s go back now to our function to fill in the remaining logic needed there.

# app/services/passportStrategy.js
// ...

// Hooks the JWT Strategy.
function hookJWTStrategy(passport) {
    var options = {};
    
    options.secretOrKey = config.keys.secret;
    options.jwtFromRequest = ExtractJwt.fromAuthHeader();
    options.ignoreExpiration = false;

    passport.use(new JWTStrategy(options, function(JWTPayload, callback) {
        User.findOne({ where: { username: JWTPayload.username } })
            .then(function(user) {
                if(!user) {
                    callback(null, false);
                    return;
                }

                callback(null, user);
            });
    }));
}

// ...

There are a few cool things going on here, let’s go through them step by step:

  1. First, we define an empty options object on which we then set the secretOrKey to our secret key from the config file, the jwtFromRequest to the ExtractJwt.fromAuthHeader() extractor and the ignoreExpiration flag to false. Basically we specify which secret key needs to be used to decrypt the JWT, what extractor needs to be used in order to read the JWT from the Authorization header and specify that the token expiration needs to be also checked.

  2. Second, we set our passport reference to use a new instance of the JWTStrategy module we required earlier in which we pass the options we just defined and as the second parameter, an anonymous function that receives the request’s JWT payload and a callback.

  3. Finally, we use our User model to find a single user in the database where the username matches the JWTPayload.username. We pass false to the callback if there’s no user available, otherwise the user.

We’re now officially done with setting up the JWT Passport Strategy. Congrats!

Let’s make sure that the engine still starts before we conclude this article. Go back to your terminal — close the existing process if it’s already running — and run node app/server.js. If all is good, magic should happen at http://localhost:8080/.

Conclusion

Phew! It was a great journey and we did a lot of progress by setting up a big part of the back-end API. We still have a lot more code to craft until we have something functional and working. Clean the sweat of your brow and drink up that remaining coffee as you’ll need it for the remaining part of the series.

Don’t be shy! Share your thoughts about this guide or ask for help if you’re stuck in the comments below. I’ll be glad to assist.

Next Up:

Sharing is Caring

If you 💖 this post, I’d appreciate if you’d let your friends know about it too. You can use the floating buttons to share this post on various social networks. Thanks! 🙂