Authentication in REST APIs
Tags: node, mysql, api, auth
Published: 2021-01-19 11:00:17

Built Authentication REST APIs using expressJS, MySQL, Sequelize and JWT. We will use following dependencies to built authentication APIs

### Required Tool
Let’s take a moment to review the tools we’re going to be using:
- [NodeJS](https://nodejs.org/en/) : We’re going to use this to run JavaScript code on the server. I’ve decided to use the latest version of Node, v6.3.0 at the time of writing, so that we’ll have access to most of the new features introduced in ES6.
- [Express](https://expressjs.com/) : As per their website, Express is a “Fast, unopinionated, minimalist web framework for Node.js”, that we’re going to be building our Todo list application on.
- [NPM](https://www.npmjs.com/) : for the package management (both server, frontend, and development packages). It’ll be easier to maintain one package management system, than using NPM and Bower together.
- [MySQL](https://www.mysql.com/) : This is a powerful open-source database that we’re going to use to store our Todos.
- [Sequelize](https://sequelize.org/) : In addition, we’re going to use Sequelize, which is a database ORM that will interface with the Mysql database for us.
- [Postman](https://www.postman.com/) : A Chrome app that we’ll use to practically test our API.

### Create Project
Let’s begin by setting up our workspace. 
You all are familiar with NPM. Before setup the project, open the terminal and check node and npm version. If version is displaying its means node and npm installed. If not then you must have to install the node and npm.
- Open CLI and go to the project directory
- Now type `npm init` to initialize the node project.

This command prompts you for a number of things, such as the name and version of your application. For now, you can simply hit RETURN to accept the defaults for most of them, with the following exception:

> entry point: (index.js)

Enter `app.js`, or whatever you want the name of the main file to be. If you want it to be index.js, hit RETURN to accept the suggested default file name.
This command will generate `package.json` file in the project folder. 

### Setup Express
Initially I will make routes for the project. Install Express and a few of it’s dependencies.
- Open CLI and go to the project directory
- Type `npm i --save express cors body-parser dotenv`

The `--save` flag will save these packages to the dependencies section of your package.json file.
- Create a file in the root folder and call it `app.js`.
- In this file, let’s create our Express application.
```
const express = require("express"),
    bodyParser = require('body-parser'),
    cors = require('cors'),
    PORT = 8080;
require('dotenv').config()    

const app = express()
app.use(cors())
app.use(bodyParser.json())

app.get("/", (req, res) => {
    res.json({ "message": "Hello ChampDecay" })
})
app.listen(PORT, async () => {
    console.log(`App is running on http://localhost:${PORT}`);
})
```

The application will be run scuccessfully on `http://localhost:8080`    
We’ll need a way to restart the server every time we change something in our code. For that, we’ll use nodemon npm package.
> `npm i nodemon`

Then, open up your `package.json` file and create a command to run the server. That command will be created under the scripts section. Edit your package.jsonin the scripts section as follows:
```
...
"scripts": {
    "dev": "nodemon app.js"
},
...
```
Now try running the application by executing following command in cli. 
>  `npm run dev`

and visiting `http://localhost:8080`. You should see 
```
{
  "message": "Hello ChampDecay"
}
```
At this point in time, your project structure should look like:
```
root
├── app.js
├── package.json
└── node_modules
```
### Sequelize Setup

For this part, we are going to install MySQL.
Next, we will require Sequelize. This is an ORM that will interface with the MYSQL database for us.
We will use of the Sequelize CLI package to bootstrap the project for us. It will also help us generate database migrations.

So Let’s install Sequelize CLI package.following command will install sequelize-cli globally
> `npm install -g sequelize-cli`

- Now we will install Sequelize package, alongside its dependencies. Sequelize is an easy-to-use multi SQL dialect ORM for Node.js. We gonna use MySQL as our database. So let install Sequelize ORM and mysql2 dialect.
> `npm i sequelize mysql2`

### Initialize sequelize
Let's generate generate migrations, seeders, config and models directories and config file using sequelize cli. 
> `sequelize init`

Above command will generate boilerplate code in your project, Now the project structure should look like:
```
root
├── app.js
├── package.json
├── config
│   └── config.json
├── migrations
├── models
│   └── index.js
└── seeders
```
In `models/index.js` file, It establishes database connection by using `config/config.json`. So let's configure `config.json`.

```
{
  "development": {
    "username": "root", // Database Username
    "password": null,   // Database Password
    "database": "blog", // Database Name
    "host": "127.0.0.1",// Database Host
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}
```
Now as we want to create a authentication, we have to create a table for `Users`. So we would generate migration and model for Users. Let's create model and migration by sequelize cli command as follows:
> `sequelize model:create --name User --attributes username:string,email:string,password:string`

This will generate `user.js` file in model directory and `<timestamp>-create-user.js` migration in migration directory. 

Generated `user.js` model file will be look like:
```
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here
    }
  };
  User.init({
    username: DataTypes.STRING,
    email: DataTypes.STRING,
    password: DataTypes.STRING
  }, {
    sequelize,
    modelName: 'User',
  });
  return User;
};
```
and generated `<timestamp>-create-user.js` migration file will be look like: 
```
'use strict';
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      username: {
        type: Sequelize.STRING
      },
      email: {
        type: Sequelize.STRING
      },
      password: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('Users');
  }
};
```
Now we have to add some settings in model and migration files like unique key and allow/disallow null values. So let's modify `user.js` model file: 
```
...
User.init({
    username: {
      type: DataTypes.STRING,
      unique: true,
      allowNull: false
    },
    email: {
      type: DataTypes.STRING,
      unique: true,
      allowNull: false
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    }
  }
...
```
Here, We have added `unique` and `allowNull` in fields and same as model, add these parameters in migration file as well. 

Finally, `models/user.js` will look like:
```
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here
    }
  };
  User.init({
    username: {
      type: DataTypes.STRING,
      unique: true,
      allowNull: false
    },
    email: {
      type: DataTypes.STRING,
      unique: true,
      allowNull: false
    },
    password: {
      type: DataTypes.STRING,
      allowNull: false
    }
  }, {
    sequelize,
    modelName: 'User',
  });
  return User;
};
```
and `migrations/<timestamp>-create-user.js` file will look like: 
```
'use strict';
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      username: {
        type: Sequelize.STRING,
        unique: true,
        allowNull: false
      },
      email: {
        type: Sequelize.STRING,
        unique: true,
        allowNull: false
      },
      password: {
        type: Sequelize.STRING,
        allowNull: false
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('Users');
  }
};
```
After setting up model and migrations, we will setup routes and controllers for user.

### Setup Route
Create a file `routes/user.js` at root directory of the project. 
It will be look like: 
```
const express = require('express'),
    router = express.Router();

router.post('/signup', (req, res) => {
    res.json({ "msg": "Signup Route" })
});
router.post('/signin', (req, res) => {
    res.json({ "msg": "Signin Route" })
});

module.exports = router
```
Here we have create two routes, one for signup and another for signin. now this file should be called in `app.js` 

Add following code in `app.js` before app.listen command.
```
app.use('/api', require('./routes/user'))
```
That's it! We have setup routes for signup and signin. Our next step will be settingup the controller for user.

### Setup Controller
Here, we will use jsonwebtokens for api authentications, So let's install `bcryptjs` and `jsonwebtoken` packages. 
> `npm i bcryptjs jsonwebtoken`

After adding two packages, create a `controllers/user.js` file and add following code: 
```
const bcrypt = require("bcryptjs"),
    jwt = require('jsonwebtoken'),
    db = require("../models/index"),
    JWT_SECRET = process.env.JWT_SECRET

exports.signUp = async (req, res) => {
    const { username, email, password: plainTextPassword } = req.body;
    const password = await bcrypt.hash(plainTextPassword, 10)
    try {
        const user = await db.User.create({
            username,
            email,
            password
        })
        res.status(201).json({ "status": "ok", "message": "User registered", user })
    } catch (error) {
        res.status(401).json({ "status": "error", "message": error.errors[0].message })
    }
}

exports.signIn = async (req, res) => {
    const { email, password } = req.body;
    const user = await db.User.findOne({ where: { email: email } })
    if (!user) {
        return res.status(401).json({ status: 'error', error: 'Invalid username/password' })
    }
    if (await bcrypt.compare(password, user.password)) {
        const payload = { id: user.id, username: user.username };
        const options = { expiresIn: '2d', issuer: 'http://localhost:8080' };
        const secret = JWT_SECRET;
        const token = jwt.sign(payload, secret, options)
        return res.status(200).json({ status: 'ok', "message": "User signin successful", token })
    }
    return res.status(401).json({ "status": "error", "message": "Invalid Username/Password" })
}
exports.getUsers = async (req, res) => {
    try {
        const users = await db.User.findAll()
        return res.status(200).json({ status: 'ok', users })
    } catch (error) {
        return res.status(401).json({ "status": "error", error })
    }
}
```

In this file, we have imported `bcryptjs`, `jsonwebtoken` and `index` model. We have define `JWT_SECRET` variable which should be hidden and ideally fetch from `.env` file. Thereafter we have exported two functions. 
##### Signup function
Here we get all request parameters and define it by `const { username, email, password: plainTextPassword } = req.body;` Then we have to hashed the password so no one can see it from database. To hash the password, we have used **bcrypt's hash** function. we have used 2 paramaters in hash function, first is plaintext password which should be encoded and second is salt. 

After it, we have to store the values including new hashed password to the database. so using sequelize's **create** function we have stored it in databse. 
##### Signin function
Here we get all request parameters same as signup function. Thereafter we fetch row from users table of the database using **findOne** function.
If it doesn't return any row that means a user enters wrong email so we have responsed invalid message wirh 401 status.
If It returns a row, we have to compare the password from database and user input. So again bcrypt will be used. the **compare** function of bcrypt will do the comparisation. If comparisation is true, we will generate access token using jwt otherwise returns error message with 401 status. 
We have used username and id to create jwt token. Also we have set expire time and issuer of the token. 
##### Get Users Function
This function simply fetch users from database. But to access this route, user should pass valid access token with the request and to validate the access token we have to make a middle ware. 
Create a new file `middleware/auth.js` and that file should have following code:
```
const jwt = require('jsonwebtoken');

module.exports = {
    validateToken: async (req, res, next) => {
        const authHeader = req.headers.authorization;
        if (authHeader) {
            const token = authHeader.split(' ')[1];
            try {
                const result = await jwt.verify(token, process.env.JWT_SECRET)
                req.decoded = result;
                next()
            } catch (error) {
                return res.status(401).json({ "status": "error", "message": "Invalid Authentication.", error })
            }
        } else {
            return res.status(401).json({ "status": "error", "message": "Authentication error. Token required." })
        }
    }
}
```
Here we have imported only jsonwebtoken and create a function called `validateToken`. It will take access token from the authorization headers and verify it using `jwt.verify()` function. If it is verfied successfully, it will go for the next process by `next()` function, otherwise it returns a error message of invalide access token with status code 401. 

So finally we have created controller and route. Let's connect route with the controller. Open `routes/user.js` file and replace the following code:
```
const express = require('express'),
    router = express.Router(),
    User = require("../controllers/user")
router.post('/signup', User.signUp);
router.post('/signin', User.signIn);
router.get('/users', Auth.validateToken, User.getUsers);
module.exports = router
```
Here in `/users` route, we have used middleware as second argument and That's all. 

Finally, The code is ready..!! 

`This is my first post. Let me know your views by commenting on it.`

[![Run in Postman](https://run.pstmn.io/button.svg)](https://god.postman.co/run-collection/59d725a55a2ea7083b2a)

[![forthebadge](https://forthebadge.com/images/badges/check-it-out.svg)](https://github.com/dhruvangg/api-auth)

[![Gmail](https://img.shields.io/badge/Gmail-D14836?style=for-the-badge&logo=gmail&logoColor=white)](mailto:dhruvangg@gmail.com)