Authentication API with JWT access token and refresh token - NodeJS
Tags: tutorial, api, node, javascript
Published: 2023-05-30 12:39:05

<small><b>part 5</b></small>

_**Summary:** This article walks you through how to implement JSON Web Token(JWT) Authentication to secure an API. Tricky concepts on **access token** and **refresh token** are demystified with how they are used to add login functionality and to protect API endpoints._

> _**Note 🔔**_:  
> _You can jump ahead to the final work, the complete API and front-end can be found on [Github](https://github.com/hane-smitter/MERN_login_system)✩._

## The Common Misconception

The most common advice is **do not waste time implementing login**. Now as years go by and you climb the career ladder, it is important to _understand how the whole system works_ to elevate yourself as an architect and make more educated design decisions. Authentication is one of the areas you need to understand how it consolidates into a system. More reasons why you should know authentication is listed in [this blog](https://dev.to/smitterhane/3-tier-authentication-understand-and-conceptualize-the-process-2g3m#why-build-auth).

When thinking about authentication, the common mental picture people have is a login HTML page submitting data to a backend API that cross checks submitted data with what is in the DB. While that is true for the bare bones of an authentication system, there is finer details that need to be done right which we would discuss in the rest of the blog.

<details>
  <summary><b>Table of Contents</b></summary>
  <ol>
    <li><a href="#getting-started">Getting started</a>
		<ul>
			<li><a href="#dependencies-used">Dependencies used</a></li>
			<li><a href="#what-we-will-do">What we will do</a></li>
			<li><a href="#directory-structure">Directory structure</a></li>
		</ul>
	</li>
    <li><a href="#what-we-will-do">What we will do</a>
      <ul>
        <li><a href="#declare-environment-variables">Declare environment variables</a></li>
        <li><a href="#create-raw-user-endraw-database-model">Create <code>User</code> database model</a></li>
        <li><a href="#add-email-service">Add email service</a></li>
        <li><a href="#create-validators">Create validators</a></li>
        <li><a href="#create-authentication-middleware">Create authentication middleware</a></li>
        <li><a href="#create-controller-functions">Create controller functions</a></li>
        <li><a href="#create-application-routes"> Create application routes </a></li>
        <li><a href="#register-routes">Register routes</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
  </ol>
</details>

## Getting Started

This is a coding tutorial. And to save length of the article better spent on the _"fun stuff"_, there is a starting repo from [Github](https://github.com/hane-smitter/MERN_login_system_starter) to base your work on. Change into **`server` directory** which is what we need in this blog.

_**Note:** This article assumes you already have [NodeJS](https://nodejs.org/en/download/) installed in your computer._

### Project Dependencies

The following third-party dependencies are used:

-   [Express](https://npmjs.com/package/express) - A web framework hosted on NodeJS runtime, to handling requests coming to our server.
-   [Express Validator](https://npmjs.com/package/express-validator) - To run server-side validation.
-   [Bcryptjs](https://npmjs.com/package/bcryptjs) - To crumble plain text into irreversible hashes.
-   [Cookie Parser](https://npmjs.com/package/cookie-parser) - To parse cookies in incoming HTTP request.
-   [Cors](https://npmjs.com/package/cors) - To configure our backend to understand CORS protocol.
-   [Dotenv](https://npmjs.com/package/dotenv) - To load variables declared in `.env` file into the project's environment.
-   [Jsonwebtoken](https://npmjs.com/package/jsonwebtoken) - To facilitate token-based authentication, _i.e access and refresh token_.
-   [Mongoose](https://npmjs.com/package/mongoose) - An object modelling tool for MongoDB to enforce a specific mongoDB schema.
-   [Nodemailer](https://npmjs.com/package/nodemailer) - To send emails from NodeJs application.
-   [Nodemon](https://npmjs.com/package/nodemon) - To restart server app whenever file changes occur.

These dependencies are already listed in `package.json` of the starter repo. To include them in your project, ensure you are in `server` directory root and run `npm install` from the terminal.

Starter repository has some steps covered:

<ol>
    <li id="db-integration">
        <span>
            Database integration
            <i>(in <a href="https://dev.to/smitterhane/database-integration-in-nodeexpress-a-maintainable-approach-dfa">part
                    2</a>)
            </i>
        </span>
    </li>
    <li id="cors-integration">
        <span>
            Cross Origin Resource Sharing
            <i>(in <a href="https://dev.to/smitterhane/keep-cors-error-under-control-2099">part
                    3</a>)
            </i>
        </span>
    </li>
    <li id="error-handling-integration">
        <span>
            Express.js error handling
            <i>(in <a href="https://dev.to/smitterhane/deftly-handle-exceptions-in-nodeexpress-application-208m">part
                    4</a>)
            </i>
        </span>
    </li>
</ol>

### What we will do

An overview outline:

1. **Declare environment variables** in `.env` file that will be loaded into application environment.
1. **Create `User` database model** to represent a user in our application.
1. **Add email service** to send emails from our application.
1. **Create validators** to validate data coming from client.
1. **Create authentication middleware** to check for authentication on protected routes.
1. **Create controller functions** to handle HTTP request and process a response for a matched route.
1. **Create routes**. So a specific functionality is provided at each route.
1. **Register routes**, So internal routing mechanism can match HTTP request to a route.

### Directory structure

Final directory structure we will have:

```diff
📂server/
    ├── 📄.env
    └── 📂src/
        ├── 📄index.js
+       ├── 📂models/ 📄User.js
+       ├── 📂services/
+       │   └── 📂email/
+       │       ├── 📄email.js
+       │       └── 📄sendEmail.js
+       ├── 📂validators/
+       │   ├── 📄index.js
+       │   ├── 📄auth-validators.js
+       │   └── 📄user-validators.js
+       ├── 📂middlewares/ 📄authCheck.js
+       ├── 📂controllers/
+       │   └── 📂user/
+       │       ├── 📄index.js
+       │       ├── 📄auth.controller.js
+       │       └── 📄user.controller.js
+       ├── 📂routes/
+       │   ├── 📄index.js
+       │   └── 📄user.routes.js
        ├── 📂config/
        │   └── ...
        └── 📂dbConn/
             └── ...
```

Let's dive in<br/>
![Child jumps into shallow pool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6j6fgwd2xydueb8ujuf.gif)

1. ## Declare environment variables

    Environment variables are stored in the RAM of the Operating System. A running applcation will read the variables it needs from the memory of its enclosing OS.

    We will use environment variables to store variables that are personal to you, e.g API keys and should not to be shared through code repositories like github.

    Let's _declare environment variables_ our application will use. Create `.env` file at `server` directory root, i.e `server/`(<i><a href="#directory-structure">refer above</a></i>). Declare the variables as follows:

    ```
    AUTH_REFRESH_TOKEN_SECRET=2Kp6BjSKl3boT6+Zf3D0tw
    AUTH_REFRESH_TOKEN_EXPIRY=1d
    AUTH_ACCESS_TOKEN_SECRET=bkZaWfqssqzE0fg1TMCHqQ
    AUTH_ACCESS_TOKEN_EXPIRY=120s
    RESET_PASSWORD_TOKEN_EXPIRY_MINS=15
    ```

    <!-- prettier-ignore -->
    For the _"TOKEN\_SECRETs"_, you can input any value. Random characters is ideal. NodeJs can help generate random characters on the fly; In your terminal run: `node -e "console.log(crypto.randomBytes(32).toString('base64'))"`.

    Realize how we set expiration time of our authentication tokens _i.e **Refresh Token** expiration time(`AUTH_REFRESH_TOKEN_EXPIRY`) is longer than **Access Token**'s (`AUTH_ACCESS_TOKEN_EXPIRY`)_. Also, the **Access Token** expiration time is reasonably short.

    `AUTH_REFRESH_TOKEN_SECRET` - Holds value of the **secret to sign** JWT _Refresh Token_.<br />
    `AUTH_REFRESH_TOKEN_EXPIRY` - Holds value of the **expiration time** of the JWT _Refresh Token_.<br />
    `AUTH_ACCESS_TOKEN_SECRET` - Holds value of the **secret to sign** JWT _Access Token_.<br />
    `AUTH_ACCESS_TOKEN_EXPIRY` - Holds value of the **expiration time** of the JWT _Access Token_.<br />
    `RESET_PASSWORD_TOKEN_EXPIRY_MINS` - Expiry time of the password reset link.

    _**Note:** `.env` file **declares** environment variables in plain text(it does not load them into OS environment). We will use `dotenv` library to read variables in `.env` file and actually load them into our application's environment(OS). Also **be sure to add `.env` file to your `.gitignore`** to avoid sharing your `.env` variables._

1. ## Create `User` database model

    A **database model**, is an interface for interacting with the database to query for operations like _reading, creating, updating_ and _deleting_ records.

    `User` model will represent a user of our application. This is the only model we will need for this tutorial. We'll use Mongoose to create this model.

    Create a file **`User.js`** at the path: `server/src/models/`(<i><a href="#directory-structure">refer</a></i>). Paste the following code in this file:

    ```javascript
    const mongoose = require("mongoose");
    const bcrypt = require("bcryptjs");
    const jwt = require("jsonwebtoken");
    const crypto = require("crypto");

    const CustomError = require("../config/errors/CustomError");

    // Pull in Environment variables
    const ACCESS_TOKEN = {
    	secret: process.env.AUTH_ACCESS_TOKEN_SECRET,
    	expiry: process.env.AUTH_ACCESS_TOKEN_EXPIRY,
    };
    const REFRESH_TOKEN = {
    	secret: process.env.AUTH_REFRESH_TOKEN_SECRET,
    	expiry: process.env.AUTH_REFRESH_TOKEN_EXPIRY,
    };
    const RESET_PASSWORD_TOKEN = {
    	expiry: process.env.RESET_PASSWORD_TOKEN_EXPIRY_MINS,
    };

    /* 
    1. CREATE USER SCHEMA
    */

    /* 
    2. SET SCHEMA OPTION
    */

    /* 
    3. ATTACH MIDDLEWARE
    */

    /* 
    4. ATTACH CUSTOM STATIC METHODS
    */

    /* 
    5. ATTACH CUSTOM INSTANCE METHODS
    */

    /* 
    6. COMPILE MODEL FROM SCHEMA
    */

    module.exports = UserModel;
    ```

    The snippet above imports dependencies we will use. We have pulled in environment variables, available in `process.env` and assigned them to constants for ease of access. We have **6 parts**(<i>commented</i>) in the code snippet above.

    Under the <b>1<sup>st</sup> part</b>(`1. CREATE USER SCHEMA`), add the following code:

    ```javascript
    const User = mongoose.Schema;
    const UserSchema = new User({
    	firstName: { type: String, required: [true, "First name is required"] },
    	lastName: { type: String, required: [true, "Last name is required"] },
    	email: {
    		type: String,
    		required: [true, "Email is required"],
    		unique: true,
    	},
    	password: {
    		type: String,
    		required: true,
    	},
    	tokens: [
    		{
    			token: { required: true, type: String },
    		},
    	],
    	resetpasswordtoken: String,
    	resetpasswordtokenexpiry: Date,
    });
    ```

    We have defined the schema for a user in our application. In other words, the attributes a user entity will have. Hence users stored in the database will be expected to each have these attributes. Some are optional while others are required.<br />
    _**Note** that we have defined `tokens` in the database schema where we will store **refresh tokens** generated for a user_.

    In the <b>2<sup>nd</sup> part</b>(`2. SET SCHEMA OPTION`), add the following code:

    ```javascript
    UserSchema.set("toJSON", {
    	virtuals: true,
    	transform: function (doc, ret, options) {
    		delete ret.password;
    		delete ret.tokens;
    		return ret;
    	},
    });
    ```

    This ensures everytime we retrieve a document from `User` model, we delete some fields that we do not wish to be included in the result.

    In the <b>3<sup>rd</sup> part</b>(`3. ATTACH MIDDLEWARE`), add the following code:

    ```javascript
    UserSchema.pre("save", async function (next) {
    	try {
    		if (this.isModified("password")) {
    			const salt = await bcrypt.genSalt(10);
    			this.password = await bcrypt.hash(this.password, salt);
    		}
    		next();
    	} catch (error) {
    		next(error);
    	}
    });
    ```

    This is a mongoose middleware that runs before a document is saved to the database. And it ensures that _password_ attribute of a _user_ is hashed _if it was modified_. Therefore, when a user is newly created, password of the user is hashed. The next time it is hashed once again is **only** when a user's password attribute is changed. Probably when a user has updated their password.

    In the following parts, we have custom methods we are setting on the `UserSchema`. They will be available on the `User` model and are helpful in avoiding redundant code from the controllers.

    Under the <b>4<sup>th</sup> part</b>(`4. ATTACH CUSTOM STATIC METHODS`), add the following code:

    ```javascript
    UserSchema.statics.findByCredentials = async (email, password) => {
    	const user = await UserModel.findOne({ email });
    	if (!user)
    		throw new CustomError(
    			"Wrong credentials!",
    			400,
    			"Email or password is wrong!"
    		);
    	const passwdMatch = await bcrypt.compare(password, user.password);
    	if (!passwdMatch)
    		throw new CustomError(
    			"Wrong credentials!!",
    			400,
    			"Email or password is wrong!"
    		);
    	return user;
    };
    ```

    We have created a **static method** that is invokable directly on _`User` model_ rather than its _instance_, enabled by the `statics` property on the `UserSchema`. This method simply finds a user by `email` and `password`. It throws an exception if the find is not successful.

    _**Note** how we generate user-defined exception using custom error constructor(`throw new CustomError(...)`). That is explained in [Express.js error handling](#error-handling-integration)_. Otherwise you can use the in-built `new Error()`.

    In the <b>5<sup>th</sup> part</b>(`5. ATTACH CUSTOM INSTANCE METHODS`), we will add **three instance methods** that will be invokable on the `User` model instance:

    Add the **first instance method**:

    ```javascript
    UserSchema.methods.generateAcessToken = function () {
    	const user = this;

    	// Create signed access token
    	const accessToken = jwt.sign(
    		{
    			_id: user._id.toString(),
    			fullName: `${user.firstName} ${user.lastName}`,
    			email: user.email,
    		},
    		ACCESS_TOKEN.secret,
    		{
    			expiresIn: ACCESS_TOKEN.expiry,
    		}
    	);

    	return accessToken;
    };
    ```

    This instance method generates an _access token_. The _access token_ is a _signed jwt_ embedded with user instance data.

    _**Note:** We use traditional functions because they have `this` binding. Using arrow functions will not produce expected results._

    Add the **second instance method**:

    ```javascript
    UserSchema.methods.generateRefreshToken = async function () {
    	const user = this;

    	// Create signed refresh token
    	const refreshToken = jwt.sign(
    		{
    			_id: user._id.toString(),
    		},
    		REFRESH_TOKEN.secret,
    		{
    			expiresIn: REFRESH_TOKEN.expiry,
    		}
    	);

    	// Create a 'refresh token hash' from 'refresh token'
    	const rTknHash = crypto
    		.createHmac("sha256", REFRESH_TOKEN.secret)
    		.update(refreshToken)
    		.digest("hex");

    	// Save 'refresh token hash' to database
    	user.tokens.push({ token: rTknHash });
    	await user.save();

    	return refreshToken;
    };
    ```

    Similar to _first instance method_, this method generates a **refresh token** which is a _signed jwt_ embedded with user instance data. We store the refresh token in the DB. It will help implememt a log out from all devices feature as seen later in the blog.<br />
    _**Note:** We store a hashed version of the refresh token in the database which is a security practice to prevent changing users' password should the database be compromised_.

    Add the **third instance method**:

    ```javascript
    UserSchema.methods.generateResetToken = async function () {
    	const resetTokenValue = crypto.randomBytes(20).toString("base64url");
    	const resetTokenSecret = crypto.randomBytes(10).toString("hex");
    	const user = this;

    	// Separator of `+` because generated base64url characters doesn't include this character
    	const resetToken = `${resetTokenValue}+${resetTokenSecret}`;

    	// Create a hash
    	const resetTokenHash = crypto
    		.createHmac("sha256", resetTokenSecret)
    		.update(resetTokenValue)
    		.digest("hex");

    	user.resetpasswordtoken = resetTokenHash;
    	user.resetpasswordtokenexpiry =
    		Date.now() + (RESET_PASSWORD_TOKEN.expiry || 5) * 60 * 1000; // Sets expiration age

    	await user.save();

    	return resetToken;
    };
    ```

    This instance method creates a **reset token** and a **reset token hash**. We use NodeJs native `crypto` module to create `resetTokenValue` and `resetTokenSecret`. A `resetToken` is obtained by concatenating these parts. We also create a `resetTokenHash` from these parts. We return plaintext `resetToken` while `resetTokenHash` is saved to the database. In the database, we also set an expiry to the reset token so that it is expired after a duration or after use.

    The first part(`resetTokenValue`) is created with **`base64url`** encoding. The second part(`resetTokenSecret`) is created with **`hex`** encoding. These encodings allow to concatenate the two parts, to form a **resultant `resetToken`**. A plus(`+`) separator is specially used; since both of these encodings will never generate the `+` character.<br />
    _`hex` encoding may be straight forward, i.e represented by only 16 symbols(`0-9` and `A-F`). Learn more about `base64url` encoding [here](https://www.rfc-editor.org/rfc/rfc4648#section-5)_.

    In the <b>6<sup>th</sup> part</b>(`6. COMPILE MODEL FROM SCHEMA`): we **compile model from schema**:

    ```javascript
    const UserModel = mongoose.model("User", UserSchema);
    ```

1. ## Add email service

    Our application needs to send emails, an example usecase is sending password reset link to a user.
    We will use nodemailer as a [Mail User Agent](https://help.returnpath.com/hc/en-us/articles/220569547-What-is-a-Mail-User-Agent-MUA-)(_MUA_) to send emails from our NodeJs application; by connecting to an SMTP server.

    We will use [Mailtrap](https://mailtrap.io/) as our **SMTP server**, responsible to deliver emails.<br />
    Mailtrap can be used for email sending. Apart from that, it offers a feature great for testing, that is, it can be used as fake SMTP server. This means _we can use Mailtrap's SMTP server to receive emails from our NodeJs application and emulate sending without actual delivery_. This service is ideal **to test and view emails we send without spamming real recipients or flooding your own inboxes**.<br />
    Later on if you want to _send emails to real inboxes_, you can update on your Mailtrap dashboard to do actual email delivery. Or you may as well change configuration options in nodemailer with another provider _such as sendgrid_.

    You will need to get credentials from mailtrap to use their SMTP service. Follow this [guide](https://help.mailtrap.io/article/5-smtp-integration) to get credentials for SMTP integration.

    After following the guide, you should have the following credentials:

    1. Host
    1. Port
    1. Username
    1. Password

    We will add **Host** and **Port** directly to nodemailer configuration options. Meanwhile, we add **Username** and **Password** to environment variables as these will be secret and unique to an individual's account.<br />
    Open `.env` file located at `server/` directory root(<i><a href="#directory-structure">refer above</a></i>). Append new variables:

    ```bash
    AUTH_EMAIL_USERNAME=<Username>
    AUTH_EMAIL_PASSWORD=<Password>
    EMAIL_FROM=<Email sender>
    ```

    `EMAIL_FROM` is the sender in an email delivery. You can fill with your own email.

    Create the file: **`email.js`**. According to the directory structure for this tutorial(<i><a href="#directory-structure">refer above</a></i>), it should be at the path: `server/src/services/email/`.<br />

    Paste in the following code:

    ```javascript
    const nodemailer = require("nodemailer");

    // Pull in Environments variables
    const EMAIL = {
    	authUser: process.env.AUTH_EMAIL_USERNAME,
    	authPass: process.env.AUTH_EMAIL_PASSWORD,
    };

    async function main(mailOptions) {
    	// Create reusable transporter object using the default SMTP transport
    	const transporter = nodemailer.createTransport({
    		host: "smtp.mailtrap.io",
    		port: 2525,
    		auth: {
    			user: EMAIL.authUser,
    			pass: EMAIL.authPass,
    		},
    	});

    	// Send mail with defined transport object
    	const info = await transporter.sendMail({
    		from: mailOptions?.from,
    		to: mailOptions?.to,
    		subject: mailOptions?.subject,
    		text: mailOptions?.text,
    		html: mailOptions?.html,
    	});

    	return info;
    }

    module.exports = main;
    ```

    In above snippet we have exported a `main()` function that connects our application to Mailtrap SMTP server facilitated with nodemailer's `createTransport(...)`. Emails from our application are forwarded to SMTP server for actual sending using nodemailer's `transporter.sendMail(...)`. An `info` object is returned containing the delivery information.

    Open/create a second file `sendEmail.js`. It should be located at `server/src/services/email/`, beside `email.js`(<i><a href="#directory-structure">refer above</a></i>).

    Paste in the following code:

    ```javascript
    const main = require("./email.js");

    const fixedMailOptions = {
    	from: process.env.EMAIL_FROM,
    };

    function sendEmail(options = {}) {
    	const mailOptions = Object.assign({}, options, fixedMailOptions);
    	return main(mailOptions);
    }

    module.exports.sendEmail = sendEmail;
    ```

    Short and sweet ✔. We are calling imported `main()` function and passing to it `mailOptions` to run email sending. We then export the `sendEmail()` that will be used in our application.

1. ## Create validators

    Form data submitted from client to server **cannot be trusted**. We need to _enforce validation rules_ for these data before we can start processing them in our server.

    For server-side validation, we'll use [express-validator](https://express-validator.github.io/docs/) which according to the docs:

    > _express-validator_ is a set of [express.js](http://expressjs.com/) middlewares that wraps [validator.js](https://github.com/validatorjs/validator.js) validator and sanitizer functions.

    Being wrapped in express.js middlewares, means it would be easy to work with in an express.js application.

    Let's create our **first set** of validators.<br />
    Create a file `auth-validators.js` located at `server/src/validators/`(<i><a href="#directory-structure">refer above</a></i>). Add the following code in it:

    ```javascript
    const { body, param } = require("express-validator");

    const User = require("../models/User");

    module.exports.loginValidator = [
    	body("email")
    		.trim()
    		.notEmpty()
    		.withMessage("Email CANNOT be empty")
    		.bail()
    		.isEmail()
    		.withMessage("Email is invalid"),
    	body("password").notEmpty().withMessage("Password CANNOT be empty"),
    ];

    module.exports.signupValidator = [
    	body("firstName")
    		.trim()
    		.notEmpty()
    		.withMessage("Firstname CANNOT be empty"),
    	body("lastName")
    		.trim()
    		.notEmpty()
    		.withMessage("Lastname CANNOT be empty"),
    	body("email")
    		.trim()
    		.notEmpty()
    		.withMessage("Email CANNOT be empty")
    		.bail()
    		.isEmail()
    		.withMessage("Email is invalid")
    		.bail()
    		.custom(async (email) => {
    			// Finding if email exists in Database
    			const emailExists = await User.findOne({ email });
    			if (emailExists) {
    				throw new Error("E-mail already in use");
    			}
    		}),
    	body("password")
    		.notEmpty()
    		.withMessage("Password CANNOT be empty")
    		.bail()
    		.isLength({ min: 4 })
    		.withMessage("Password MUST be at least 4 characters long"),
    ];

    module.exports.forgotPasswordValidator = [
    	body("email")
    		.trim()
    		.notEmpty()
    		.withMessage("Email CANNOT be empty")
    		.bail()
    		.isEmail()
    		.withMessage("Email is invalid"),
    ];

    module.exports.resetPasswordValidator = [
    	param("resetToken").notEmpty().withMessage("Reset token missing"),
    	body("password")
    		.notEmpty()
    		.withMessage("Password CANNOT be empty")
    		.bail()
    		.isLength({ min: 4 })
    		.withMessage("Password MUST be at least 4 characters long"),
    	body("passwordConfirm").custom((value, { req }) => {
    		if (value !== req.body.password) {
    			throw new Error("Passwords DO NOT match");
    		}

    		return true;
    	}),
    ];
    ```

    The code snippet above exports validation rules to be used on requests carrying data received from client-side forms. These are rules about a user's authentication.

    For the **second set** of validators, create `user-validators.js` located at `server/src/validators/`(<i><a href="#directory-structure">refer above</a></i>).

    And paste in the following code:

    ```javascript
    const { param } = require("express-validator");

    module.exports.fetchUserProfileValidator = [
    	param("id").notEmpty().withMessage("User id missing"),
    ];
    ```

    The above snippet exports validation rule(s) that act on user object in our application.

    Lastly, we would like to export all the validation rules from a single file.<br />
    Create `index.js` at `server/src/validators/`(<i><a href="#directory-structure">refer above</a></i>). And paste in:

    ```javascript
    const authValidators = require("./auth-validators");
    const userValidators = require("./user-validators");

    module.exports = {
    	...authValidators,
    	...userValidators,
    };
    ```

    Simply, we are combining all validation rules and exporting as a single object.

1. ## Create authentication middleware

    An authentication middleware will intercept HTTP requests to check for authentication.<br />
    A request is allowed to proceed to a resource after it has passed authentication check.

    Let's create this middleware. So create `authCheck.js`. According to directory structure(<i><a href="#directory-structure">refer above</a></i>), it should be at the path: `server/src/middlewares/`.<br />

    Paste in the following code:

    ```javascript
    const jwt = require("jsonwebtoken");

    const AuthorizationError = require("../config/errors/AuthorizationError.js");

    // Pull in Environment variables
    const ACCESS_TOKEN = {
    	secret: process.env.AUTH_ACCESS_TOKEN_SECRET,
    };

    module.exports.requireAuthentication = async (req, res, next) => {
    	try {
    		const authHeader = req.header("Authorization");
    		if (!authHeader?.startsWith("Bearer "))
    			throw new AuthorizationError(
    				"Authentication Error",
    				undefined,
    				"You are unauthenticated!",
    				{
    					error: "invalid_access_token",
    					error_description: "unknown authentication scheme",
    				}
    			);

    		const accessTokenParts = authHeader.split(" ");
    		const aTkn = accessTokenParts[1];

    		const decoded = jwt.verify(aTkn, ACCESS_TOKEN.secret);

    		// Attach authenticated user and Access Token to request object
    		req.userId = decoded._id;
    		req.token = aTkn;
    		next();
    	} catch (err) {
    		// Authentication didn't go well
    		console.log(err);

    		const expParams = {
    			error: "expired_access_token",
    			error_description: "access token is expired",
    		};
    		if (err.name === "TokenExpiredError")
    			return next(
    				new AuthorizationError(
    					"Authentication Error",
    					undefined,
    					"Token lifetime exceeded!",
    					expParams
    				)
    			);

    		next(err);
    	}
    };
    ```

    Above snippet is an express middleware that checks if a request is authenticated in the following steps:

    1. The request has an _access token_ present in the `Authorization` header. A **Bearer** token is expected, so we use the split function to get everything after the space. Any errors thrown here will end up in the `catch` block.
    1. Verify if the _access token_ is valid.

    A request is considered authenticated if all goes well in the above steps. It would then be allowed to proceed to a protected resource.<br />
    Otherwise, an exception is `throw`n with a custom error constructor `AuthorizationError()` which would return an authentication failure response regarding the [HTTP specs](https://www.rfc-editor.org/rfc/rfc9110.html#field.www-authenticate) for returning _unauthorized/unauthenticated error_.

1. ## Create controller functions

    In this part we write controllers; the program logic for routes in our application. In express, they are also known as _route handlers_.

    Typically in a routing mechanism, a controller registered for a particular path/route will be run when that route matches. Below are **routes** in our application. We will write controllers for these route.

    1. **`/api/users/login`** - User login.
    1. **`/api/users/signup`** - User signup.
    1. **`/api/users/logout`** - User logout.
    1. **`/api/users/master-logout`** - User logout from all devices.
    1. **`/api/users/reauth`** - Refresh Access Token.
    1. **`/api/users/forgotpass`** - Forgot password.
    1. **`/api/users/resetpass`** - Reset password.
    1. **`/api/users/me`** - Get profile of logged in user.
    1. **`/api/users/:id`** - Get profile of user by Id.

    So let's get started with creating controllers.

    Create **`auth.controller.js`**. It should be at the path: `server/src/controllers/user/`(<i><a href="#directory-structure">refer above</a></i>).<br />
    This file will contain controllers that alter user's authentication.

    Paste the following code inside the file:

    ```javascript
    const { validationResult } = require("express-validator");
    const jwt = require("jsonwebtoken");
    const crypto = require("crypto");

    const User = require("../../models/User");
    const { sendEmail } = require("../../services/email/sendEmail");
    // 👇Were created in part 4
    const CustomError = require("../../config/errors/CustomError");
    const AuthorizationError = require("../../config/errors/AuthorizationError");

    // Top-level constants
    const REFRESH_TOKEN = {
    	secret: process.env.AUTH_REFRESH_TOKEN_SECRET,
    	cookie: {
    		name: "refreshTkn",
    		options: {
    			httpOnly: false,
    			sameSite: "None",
    			secure: true,
    			maxAge: 24 * 60 * 60 * 1000,
    		},
    	},
    };
    const ACCESS_TOKEN = {
    	secret: process.env.AUTH_ACCESS_TOKEN_SECRET,
    };
    const RESET_PASSWORD_TOKEN = {
    	expiry: process.env.RESET_PASSWORD_TOKEN_EXPIRY_MINS,
    };

    /*
      1. LOGIN USER
    */

    /*
      2. SIGN UP USER 
    */

    /*
      3. LOGOUT USER
    */

    /*
      4. LOGOUT USER FROM ALL DEVICES
    */

    /*
      5. REGENERATE NEW ACCESS TOKEN
    */

    /*
      6. FORGOT PASSWORD
    */

    /*
      7. RESET PASSWORD
    */
    ```

    The snippet above has imported modules/dependencies to aid writing logic in controller functions. Global scoped constants which are objects, are also declared. They store environment variables.<br />
    Moreover, `const REFRESH_TOKEN` constant holds _cookie configurations_ in `cookie` property we will use to set cookie(s) on a HTTP response.

    _**WHAT YOU NEED TO KNOW ABOUT COOKIES** in decoupled applications communicating to each other via an API._<br />
    By default, **browsers will not send cookies in cross-origin requests**. Since we are building an API that we intend to consume from _client-side sitting on a different origin_, we have to _set some configurations on the cookie to store in a user's browser_. These configurations will allow the cookie to be sent back when cross-site request to our server is made.<br />
    These configurations are what are contained in `cookie` property of `const REFRESH_TOKEN` object. More specifically in the `options` property:

    ```javascript
    const REFRESH_TOKEN = {
    	// ...
    	cookie: {
    		name: "refreshTkn",
    		options: {
    			sameSite: "None",
    			secure: true,
    			httpOnly: true,
    			maxAge: 24 * 60 * 60 * 1000,
    		},
    	},
    };
    ```

    We have set **`sameSite`** property to `"None"` which in a cookie context means that the browser can send the cookie with cross site requests.<br />
    However, setting a `SameSite` attribute to `None` will also require you to set `Secure` attribute, _implying a cookie is only sent in an encrypted request over the HTTPS protocol_. Hence in cookie configurations above, we also set **`secure`** to `true`. _Localhost_ is not a https protocol but it is treated special _i.e cookie should still be sent even with `secure=true`_. You can learn more about _cookie sameSite attribute_ [here](https://web.dev/samesite-cookies-explained/).<br />
    Another property is **`httpOnly`** set to `true`. In cookie context, a cookie with `httponly` attribute stored in the browser is **inaccessible** to scripts running on the browser.<br />
    **`maxAge`** property sets the age of the cookie to expire in browser.

    In the snippet above; where we shared code for `auth.controller.js`, we have **7 parts** numbered each in a multiline comment. We will write controller functions in parts.

    In the <b>1<sup>st</sup> part</b>(`1. LOGIN USER`), Add the following code:

    ```javascript
    module.exports.login = async (req, res, next) => {
    	try {
    		const errors = validationResult(req);
    		if (!errors.isEmpty()) {
    			throw new CustomError(
    				errors.array(),
    				422,
    				errors.array()[0]?.msg
    			);
    		}

    		const { email, password } = req.body;

    		/* Custom methods on user are defined in User model */
    		const user = await User.findByCredentials(email, password); // Identify and retrieve user by credentials
    		const accessToken = await user.generateAcessToken(); // Create Access Token
    		const refreshToken = await user.generateRefreshToken(); // Create Refresh Token

    		// SET refresh Token cookie in response
    		res.cookie(
    			REFRESH_TOKEN.cookie.name,
    			refreshToken,
    			REFRESH_TOKEN.cookie.options
    		);

    		// Send Response on successful Login
    		res.json({
    			success: true,
    			user,
    			accessToken,
    		});
    	} catch (error) {
    		console.log(error);
    		next(error);
    	}
    };
    ```

    **Notice🕵** that we create controllers with the signature of an express middleware, _i.e `(req, res, next) => {...}`_. Creating this way will allow us to use `next()` to pass error encountered here to the error handling midleware configured [earlier on](#error-handling-integration).

    You'll see this piece of code frequently:

    ```javascript
    // ...
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
    	throw new CustomError(errors.array(), 422, errors.array()[0]?.msg);
    }
    // ...
    ```

    It captures validation errors on the `req`uest object. Validators are _route level middlewares_ added before controllers that need user data validation in a route. They enforce validation rules on data in `req`uest object. Validation result including errors are **aggregated** on the request object, hence validation result is available in `req`uest object from our controllers. We `throw` an error using a `CustomError` constructor if data validation errors are found in `req`uest object.

    This exported controller stipulates control flow for a user login:

    1. Identify user by login credentials received.
    1. Generate Access and Refresh Tokens for the identified user.
    1. Set a cookie in response with _Refresh Token_ as its value.
    1. Send response with _Access Token_ in the body.

    In the <b>2<sup>nd</sup> part</b>(`2. SIGN UP USER`), Add the following code:

    ```javascript
    module.exports.signup = async (req, res, next) => {
    	try {
    		const errors = validationResult(req);
    		if (!errors.isEmpty()) {
    			throw new CustomError(
    				errors.array(),
    				422,
    				errors.array()[0]?.msg
    			);
    		}
    		const { firstName, lastName, email, password } = req.body;

    		/* Custom methods on newUser are defined in User model */
    		const newUser = new User({ firstName, lastName, email, password });
    		await newUser.save(); // Save new User to DB
    		const accessToken = await newUser.generateAcessToken(); // Create Access Token
    		const refreshToken = await newUser.generateRefreshToken(); // Create Refresh Token

    		// SET refresh Token cookie in response
    		res.cookie(
    			REFRESH_TOKEN.cookie.name,
    			refreshToken,
    			REFRESH_TOKEN.cookie.options
    		);

    		// Send Response on successful Sign Up
    		res.status(201).json({
    			success: true,
    			user: newUser,
    			accessToken,
    		});
    	} catch (error) {
    		console.log(error);
    		next(error);
    	}
    };
    ```

    The flow of user sign up:

    1. Save new user with user data from `req`uest body.
    1. Generate Access and Refresh Tokens for the new user.
    1. Set a cookie in response with _Refresh Token_ as its value.
    1. Send response with _Access Token_ included in the body.

    We include authentication tokens in response because presumably after a successful sign up, the frontend would want to automatically login a user.

    In the <b>3<sup>rd</sup> part</b>(`3. LOGOUT USER`), Add the following code:

    ```javascript
    module.exports.logout = async (req, res, next) => {
    	try {
    		// Authenticated user ID attached on `req` by authentication middleware
    		const userId = req.userId;
    		const user = await User.findById(userId);

    		const cookies = req.cookies;
    		// const authHeader = req.header("Authorization");
    		const refreshToken = cookies[REFRESH_TOKEN.cookie.name];
    		// Create a access token hash
    		const rTknHash = crypto
    			.createHmac("sha256", REFRESH_TOKEN.secret)
    			.update(refreshToken)
    			.digest("hex");
    		user.tokens = user.tokens.filter(
    			(tokenObj) => tokenObj.token !== rTknHash
    		);
    		await user.save();

    		// Set cookie expiry to past date so it is destroyed
    		const expireCookieOptions = Object.assign(
    			{},
    			REFRESH_TOKEN.cookie.options,
    			{
    				expires: new Date(1),
    			}
    		);

    		// Destroy refresh token cookie
    		res.cookie(REFRESH_TOKEN.cookie.name, "", expireCookieOptions);
    		res.status(205).json({
    			success: true,
    		});
    	} catch (error) {
    		console.log(error);
    		next(error);
    	}
    };
    ```

    To logout a user, we delete the Access Token (attached in `req` object by _authentication middleware_) from the user's record in the database. _This will cause authentication middleware to invalidate requests that contain this access token_. Then we destroy the refresh token stored in a cookie in user's browser. We destroy this cookie by passing the same configurations used to create the cookie, but this time setting cookie `maxAge` to `0` and a value of none(`""`).

    In the <b>4<sup>th</sup> part</b>(`4. LOGOUT USER FROM ALL DEVICES`), Add the following code:

    ```javascript
    module.exports.logoutAllDevices = async (req, res, next) => {
    	try {
    		// Authenticated user ID attached on `req` by authentication middleware
    		const userId = req.userId;
    		const user = await User.findById(userId);

    		user.tokens = undefined;
    		await user.save();

    		// Set cookie expiry to past date to mark for destruction
    		const expireCookieOptions = Object.assign(
    			{},
    			REFRESH_TOKEN.cookie.options,
    			{
    				expires: new Date(1),
    			}
    		);

    		// Destroy refresh token cookie
    		res.cookie(REFRESH_TOKEN.cookie.name, "", expireCookieOptions);
    		res.status(205).json({
    			success: true,
    		});
    	} catch (error) {
    		console.log(error);
    		next(error);
    	}
    };
    ```

    It is the same process as `logout` controller. Only this time, we destroy all tokens in a user's record.

    In the <b>5<sup>th</sup> part</b>(`5. REGENERATE NEW ACCESS TOKEN`), Add the following code:

    ```javascript
    module.exports.refreshAccessToken = async (req, res, next) => {
    	try {
    		const cookies = req.cookies;
    		const authHeader = req.header("Authorization");

    		if (!cookies[REFRESH_TOKEN.cookie.name]) {
    			throw new AuthorizationError(
    				"Authentication error!",
    				"You are unauthenticated",
    				{
    					realm: "reauth",
    					error: "no_rft",
    					error_description: "Refresh Token is missing!",
    				}
    			);
    		}
    		if (!authHeader?.startsWith("Bearer ")) {
    			throw new AuthorizationError(
    				"Authentication Error",
    				"You are unauthenticated!",
    				{
    					realm: "reauth",
    					error: "invalid_access_token",
    					error_description: "access token error",
    				}
    			);
    		}

    		const accessTokenParts = authHeader.split(" ");
    		const staleAccessTkn = accessTokenParts[1];

    		const decodedExpiredAccessTkn = jwt.verify(
    			staleAccessTkn,
    			ACCESS_TOKEN.secret,
    			{
    				ignoreExpiration: true,
    			}
    		);

    		const rfTkn = cookies[REFRESH_TOKEN.cookie.name];
    		const decodedRefreshTkn = jwt.verify(rfTkn, REFRESH_TOKEN.secret);

    		const userWithRefreshTkn = await User.findOne({
    			_id: decodedRefreshTkn._id,
    			"tokens.token": staleAccessTkn,
    		});
    		if (!userWithRefreshTkn) {
    			throw new AuthorizationError(
    				"Authentication Error",
    				"You are unauthenticated!",
    				{
    					realm: "reauth",
    				}
    			);
    		}
    		// Delete the stale access token
    		console.log(
    			"Removing Stale access tkn from DB in refresh handler..."
    		);
    		userWithRefreshTkn.tokens = userWithRefreshTkn.tokens.filter(
    			(tokenObj) => tokenObj.token !== staleAccessTkn
    		);
    		await userWithRefreshTkn.save();
    		console.log("...Tkn removED!");

    		// GENERATE NEW ACCESSTOKEN
    		const accessToken = await userWithRefreshTkn.generateAcessToken();

    		// Send back new created accessToken
    		res.status(201);
    		res.set({ "Cache-Control": "no-store", Pragma: "no-cache" });
    		res.json({
    			success: true,
    			accessToken,
    		});
    	} catch (error) {
    		console.log(error);
    		if (error?.name === "JsonWebTokenError") {
    			return next(
    				new AuthorizationError(error, "You are unauthenticated", {
    					realm: "reauth",
    					error_description: "token error",
    				})
    			);
    		}
    		next(error);
    	}
    };
    ```

    The `refreshAccessToken` controller requires an Access Token in the authorization header regardless if it is expired. We throw an authorization error using `AuthorizationError()` custom error constructor.

    The snippet above refreshes an Access Token with a new one in the following steps:

    1. Find Refresh Token in cookie
    1. Find Access Token in Authorization header
    1. Verify Access Token. Expired jwt passes verification.
    1. Verify Refresh Token. Fails verification if expired.
    1. Check user in Refresh Token exists in our database and was assigned Access Token received here.
    1. Remove stale access token from user's record.
    1. Generate new Access token and return in response body.

    In the <b>6<sup>th</sup> part</b>(`6. FORGOT PASSWORD`), Add the following code:

    ```javascript
    module.exports.forgotPassword = async (req, res, next) => {
    	try {
    		const errors = validationResult(req);
    		if (!errors.isEmpty()) {
    			throw new CustomError(errors.array(), 422);
    		}

    		const email = req.body.email;

    		const user = await User.findOne({ email });
    		if (!user) throw new CustomError("Email not sent", 404);

    		let resetToken = await user.generateResetToken();
    		resetToken = encodeURIComponent(resetToken);

    		const resetPath = req.header("X-reset-base");
    		const origin = req.header("Origin");

    		const resetUrl = resetPath
    			? `${resetPath}/${resetToken}`
    			: `${origin}/resetpass/${resetToken}`;
    		console.log("Password reset URL: %s", resetUrl);

    		const message = `
                <h1>You have requested to change your password</h1>
                <p>You are receiving this because someone(hopefully you) has requested to reset password for your account.<br/>
                  Please click on the following link, or paste in your browser to complete the password reset.
                </p>
                <p>
                  <a href=${resetUrl} clicktracking=off>${resetUrl}</a>
                </p>
                <p>
                  <em>
                    If you did not request this, you can safely ignore this email and your password will remain unchanged.
                  </em>
                </p>
                <p>
                <strong>DO NOT share this link with anyone else!</strong><br />
                  <small>
                    <em>
                      This password reset link will <strong>expire after ${
    						RESET_PASSWORD_TOKEN.expiry || 5
    					} minutes.</strong>
                    </em>
                  <small/>
                </p>
            `;

    		try {
    			await sendEmail({
    				to: user.email,
    				html: message,
    				subject: "Reset password",
    			});

    			res.json({
    				message:
    					"An email has been sent to your email address. Check your email, and visit the link to reset your password",
    				success: true,
    			});
    		} catch (error) {
    			user.resetpasswordtoken = undefined;
    			user.resetpasswordtokenexpiry = undefined;
    			await user.save();

    			console.log(error.message);
    			throw new CustomError("Email not sent", 500);
    		}
    	} catch (err) {
    		next(err);
    	}
    };
    ```

    The code above emails a user with with a password reset link.

    This is a sample email sent to a user:

      <figure>
        <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/30csloqqfs0gqnm5zp5u.png" alt="sample reset email message">
        <figcaption>Password reset email</figcaption>
      </figure>

    In the <b>7<sup>th</sup> part</b>(`7. RESET PASSWORD`), Add the following code:

    ```javascript
    module.exports.resetPassword = async (req, res, next) => {
    	try {
    		console.log("req.params: ", req.params);
    		const errors = validationResult(req);
    		if (!errors.isEmpty()) {
    			throw new CustomError(errors.array(), 422);
    		}

    		const resetToken = new String(req.params.resetToken);

    		const [tokenValue, tokenSecret] =
    			decodeURIComponent(resetToken).split("+");

    		console.log({ tokenValue, tokenSecret });

    		// Recreate the reset Token hash
    		const resetTokenHash = crypto
    			.createHmac("sha256", tokenSecret)
    			.update(tokenValue)
    			.digest("hex");

    		const user = await User.findOne({
    			resetpasswordtoken: resetTokenHash,
    			resetpasswordtokenexpiry: { $gt: Date.now() },
    		});
    		if (!user) throw new CustomError("The reset link is invalid", 400);
    		console.log(user);

    		user.password = req.body.password;
    		user.resetpasswordtoken = undefined;
    		user.resetpasswordtokenexpiry = undefined;

    		await user.save();

    		// Email to notify owner of the account
    		const message = `<h3>This is a confirmation that you have changed Password for your account.</h3>`;
    		// No need to await
    		sendEmail({
    			to: user.email,
    			html: message,
    			subject: "Password changed",
    		});

    		res.json({
    			message: "Password reset successful",
    			success: true,
    		});
    	} catch (error) {
    		console.log(error);
    		next(error);
    	}
    };
    ```

    Snippet above is the logic for a password reset. This controller expects an incoming request to have a valid `resetToken`. A `resetTokenHash` is recreated from the parts that were used to create the `resetToken` _i.e `const [tokenValue, tokenSecret] = decodeURIComponent(resetToken).split("+");`_.<br />
    A user with the recreated `resetTokenHash` is retrieved and password is updated.<br />
    _**Remember**: We do not hash the password because we attached a mongoose middleware on `User` model that hashes the password field if it is modified._<br />
    After modifying the password successfully, we send an email to notify about a successful password change.

    So we have created the first set of controller functions in `auth.controller.js` file that alter user's authentication either directly or indirectly.<br />
    Next we need to create the **second set of controller functions** that act on user object in our application.

    Open/create **`user.controller.js`**. According to the directory structure for this tutorial, it should be at the path: `server/src/controllers/user/`(<i><a href="#directory-structure">refer above</a></i>). Create the constituent directories if you do not have.<br />
    This file will contain controllers that act on user object in our application.

    Paste in the below code inside this file:

    ```javascript
    const { validationResult } = require("express-validator");

    const CustomError = require("../../config/errors/CustomError");
    const User = require("../../models/User");

    /* 
      1. FETCH USER PROFILE BY ID
    */
    module.exports.fetchUserProfile = async (req, res, next) => {
    	try {
    		const errors = validationResult(req);
    		if (!errors.isEmpty()) {
    			throw new CustomError(
    				errors.array(),
    				422,
    				errors.array()[0]?.msg
    			);
    		}

    		const userId = req.params.id;
    		const retrievedUser = await User.findById(userId);

    		res.json({
    			success: true,
    			user: retrievedUser,
    		});
    	} catch (error) {
    		console.log(error);
    		next(error);
    	}
    };

    /* 
      2. FETCH PROFILE OF AUTHENTICATED USER
    */
    module.exports.fetchAuthUserProfile = async (req, res, next) => {
    	try {
    		const userId = req.userId;
    		const user = await User.findById(userId);

    		res.json({
    			success: true,
    			user,
    		});
    	} catch (error) {
    		console.log(error);
    		next(error);
    	}
    };
    ```

    This is straight forward. We have two functions that fetch user profile. The first one `fetchUserProfile()`, fetches a user by ID provided in URL. And the second one `fetchAuthUserProfile`, fetches a user by ID gotten from authentication middleware as `req.userId`.

    We have so far created and exported controller functions in separate files. Let's export all these controllers from a single file so that we could import them in other modules with one import statement.

    For this, open/create a third file **`index.js`**, at the location `server/src/controllers/user/`(<i><a href="#directory-structure">refer above</a></i>).<br />
    Add the code below in this file:

    ```javascript
    module.exports = {
    	...require("./auth.controller"),
    	...require("./user.controller"),
    };
    ```

    We export one object that includes all exports from the imported files.

1. ## Create application routes

    So far, we have written controllers. Now we need to create routes in our application. Basically, we will _specify route paths_ and the _controller_ to execute program logic at that path.

    For this, open/create **`user.routes.js`**, at the location `server/src/routes/`(<i><a href="#directory-structure">refer above</a></i>). Create the constituent directories if you do not have.<br />
    Add the code below in this file:

    ```javascript
    const express = require("express");

    const validators = require("../validators");
    const userControllers = require("../controllers/user");
    const { requireAuthentication } = require("../middlewares/authCheck");

    const router = express.Router();

    // User Login
    router.post("/login", validators.loginValidator, userControllers.login);

    // User Signup
    router.post("/signup", validators.signupValidator, userControllers.signup);

    // User Logout
    router.post("/logout", requireAuthentication, userControllers.logout);

    // User Logout from all devices
    router.post(
    	"/master-logout",
    	requireAuthentication,
    	userControllers.logoutAllDevices
    );

    // Refresh Access Token
    router.post("/reauth", userControllers.refreshAccessToken);

    // Forgot password
    router.post(
    	"/forgotpass",
    	validators.forgotPasswordValidator,
    	userControllers.forgotPassword
    );

    // Reset password
    router.patch(
    	"/resetpass/:resetToken",
    	validators.resetPasswordValidator,
    	userControllers.resetPassword
    );

    // Authenticated user profile
    router.get(
    	"/me",
    	requireAuthentication,
    	userControllers.fetchAuthUserProfile
    );

    // Get user by ID
    router.get(
    	"/:id",
    	requireAuthentication,
    	validators.fetchUserProfileValidator,
    	userControllers.fetchUserProfile
    );

    module.exports = router;
    ```

    The code above contains the routes our application will have. We used `express.Router()` to organize application routes into a separate file.

    Using the classic `router` object, we define routes in the order of _HTTP Method_, _route path_ and _controller_, _i.e router.`METHOD`(`"PATH"`, `CONTROLLER`)_. In between _route path_ and _controller_, we can add _middleware(s)_ that intercept a request and take it through a round of processing. So controllers are declared last in the chain.

    For instance, from the snippet above, Authentication middleware(`requireAuthentication`) and validators(`validators.x`) are middlewares that intercept a request before it is processed in controller(`userControllers.x`). Authentication middleware ensures request is authenticated and validators apply validation rules on data within a request.

    Since the routes we have defined are all concerned with the user object in our application, let's define a _base path for these routes_.<br />
    Open/create **`index.js`**, at the location `server/src/routes/`(<i><a href="#directory-structure">refer above</a></i>).<br />
    Add the code below in this file:

    ```javascript
    const express = require("express");
    const router = express.Router();

    const userRoutes = require("./user.routes");

    router.use("/users", userRoutes);

    module.exports = router;
    ```

    The snippet above makes `userRoutes` available on `/users` base path. With this pattern, if your application has another user type e.g admin, you can create another base path for it.

    We then export `router` object created here.

1. ## Register routes

    We need to register routes we have created so that traffic can be chanelled to them.

    Therefore we need to include these routes in the entry file of our application. Open this file, `index.js` located at `server` directory root, _i.e `server/`_(<i><a href="#directory-structure">refer above</a></i>).

    Edit the <b>3<sup>rd</sup> part</b> of this file like shown below:

    ```javascript
    const routes = require("./routes"); // import the modular routes at the top

    // ...
    /* 
    3. APPLICATION ROUTES 🛣️
    */
    app.use("/api", routes); // register modular routes
    // ...
    ```

    We have imported the routes and regisered them on a base path, `"/api"`. Therefore in the long run, user routes will be available on a path `/api/users/...`. For example to access login route, the path is `/api/users/login`.

## Conclusion

Cheers🥂, we have reached the end. Well, the article is an epic ride🏇 with valuable information 🤯. I suppose it is a divine touch🪄 to escalate your skills a level higher. Nonetheless, we have built a backend furnished with **full user registration** and **user authentication**. I have always felt that many beginners find it hard to connect the dots using JWTs for authentication, let alone implementing basic security practices when using them.

Security is an expansive topic. While that is true, in this article we have have implemented some basic security practices when using authentication tokens for authentication, _i.e access tokens and refresh tokens_. For example:

-   An access token is short-lived unlike the refresh token.
-   Access token is included in a response body and refresh token is added in httpOnly cookie in response.
-   When we receive access token from client, we check to ensure the token was actually issued to this user.
-   We store access token in database so that we can be able to invalidate it on demand.

It is a good thing we have built an authentication API. However for many, I know it can still be overwhelming how to consume such an API to stitch together an authentication that _"works"_ from the frontend. If this sounds like you, be sure to check the next parts where we build the front-end using react.

---

_Let's connect [@twitter](https://twitter.com/intent/user?screen_name=smitterhane). Till then...peace✌._