Online Food Ordering App (3)
Tags: express, node, react, reactnative
Published: 2020-12-31 13:43:09

*<span>Photo by <a href="https://unsplash.com/@abillion?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">abillion</a> on <a href="https://unsplash.com/s/photos/food-mobile-app?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>*


***

Welcome back!


Today we are going to finish implementing authentication for the backend of our app "Gourmet".


In this post we will implement the **login** and **logout** endpoints.


***


### Project steps
1. [Backend - Project Setup](https://dev.to/the22mastermind/online-food-ordering-app-1-594j)
2. Backend - Authentication
  1. [Backend - Authentication - Signup](https://dev.to/the22mastermind/online-food-ordering-app-2-28po)
  2. [Backend - Authentication - Login and Logout]() :pushpin:
3. [Backend - Place order]()
4. [Backend - View orders list and view a specific order]()
5. [Backend - Update order]()
6. [Frontend - Authentication]()
7. [Frontend - Place order, view orders list, and view order details]()


***


**Login**


- Create a new branch `ft-authentication-login` off our `main` branch

- Update `src/utils/messages.js` and add the following messages:

![carbon (1)](https://dev-to-uploads.s3.amazonaws.com/i/pjt9njhb7schjg2x0d8x.png)
 

- Create `tests/authentication_login.test.js` file and paste the following inside:

![carbon](https://dev-to-uploads.s3.amazonaws.com/i/hf0xuqojjllq1fu3vp2m.png)
 

If you run the tests, all Login tests should fail because we haven't yet implemented this functionality. Let's do it.

- Update `src/helpers/misc.js` like this:

![carbon (2)](https://dev-to-uploads.s3.amazonaws.com/i/dliwahtm207cxg6fjoi3.png)
 

The `isPasswordValid` function will help us in checking if the password submitted by the user equals to the user's password saved in the database by leveraging bcrypt's `compare` function.

- Update `src/validations/authentication.js` and the `login` function like this:

![carbon (5)](https://dev-to-uploads.s3.amazonaws.com/i/tvffyvbkqe3f285pj1iw.png)
 

- Update `src/middlewares/authentication.js` like this:

![carbon (3)](https://dev-to-uploads.s3.amazonaws.com/i/50jge06491160dewhwhg.png)
 

The `validateLogin` middleware function will help us to validate the login credentials by using our login validation function.


The `checkLogin` middleware function will help us to check if the user trying to login exists in our database and if the password provided is valid.

- Update `src/controllers/authentication.js` and add the `login` method like this:

![carbon (6)](https://dev-to-uploads.s3.amazonaws.com/i/t5qvuu6y9zrhxdzbslfl.png)
 

- Finally, update `src/routes/authRoutes.js` to create the login route and connect our middlewares and controller

![carbon (7)](https://dev-to-uploads.s3.amazonaws.com/i/of596dr4l4he1qwqse2j.png)
 

Now run the tests again and you should see that all our Login tests are passing. Nice!


***


**Logout**


What we want to achieve when a user logs out is to make sure that their JWT token becomes unusable. Since JWT doesn't have a functionality to force a token to expire, we will have to implement a custom solution.

If you have noticed, we provided a `expiresIn: '30d',` option in our `generateToken` function. This option controls the lifespan of our token, in our case it's 30 days. This is fine but imagine if a user logs in then logs out right away, this would mean that their token would still be valid for 30 days and if an attacker was to get hold of this token, they would be able to impersonate the original user. Now imagine if a user logs in then logs out again and they do this 5 consecutive times. We now have to deal with 5 unknown but valid tokens. Now imagine 1000 users doing this everyday - It could get out of hand very quickly.


Even though there's nothing we can do to force a token to expire before its `expiresIn` property, we can introduce a way to manage these tokens, especially for users who have logged out of our system.


Our solution is to store a user's token in a database when they logout. This database will be separate from our main database and ideally it should be very fast to make the writing and retrieving of data fast.

Redis is an ideal candidate for such a task because of its high performance and very low latency. Learn more about Redis [here](https://redis.io/) and [here](https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-20-04).


Let's now implement the logout functionality.


- Download and install Redis and test that it works well with the `ping/pong` command

- In our project root, run `yarn add redis` to install the Redis Node.js client

- Update `src/utils/messages` and add the following messages:

![carbon (9)](https://dev-to-uploads.s3.amazonaws.com/i/vn2rqz5l2h72fanhjdb3.png)
 

- Create a `tests/authentication_logout.js` file put the following code inside:

![carbon (8)](https://dev-to-uploads.s3.amazonaws.com/i/lt885m6d9roqsf8ta8x0.png)
 

- Create a `src/config/redisClient.js` configuration file like this:

![carbon (15)](https://dev-to-uploads.s3.amazonaws.com/i/pgxpuv6fim1r6owtme59.png)


- Update the `.env` file and a `REDIS_URL` variable with a default port like this: `REDIS_URL=redis://@127.0.0.1:6379`.

If you are using credentials to connect to your Redis server then your URL would be like this: `REDIS_URL=redis://USERNAME:PASSWORD@HOST_NAME:PORT_NUMBER`

- Update `src/middlewares/authentication.js` and refactor `checkUserToken` to this:

![carbon (11)](https://dev-to-uploads.s3.amazonaws.com/i/p4yt8xnimzjff3en6y6t.png)

Here we are using the `smembers` method of Redis to retrieve all the members/values in a set. This method takes a string key (`token`) and a callback which returns an error or an array of values found. Check out [this link](https://redis.io/commands) for a list of all the commands/methods.

We then check if our token is in the `tokensArray` and return an appropriate error. `tokensArray` contains tokens of logged out users that have not yet expired. So to make a user logout, we just have to store their token in this set of key `token`.

Let's now implement the controller where we will store the user's token in that set.

- Update `src/controllers/authentication.js` to add the `logout` method

![carbon (12)](https://dev-to-uploads.s3.amazonaws.com/i/cw3ekdzg91se1j8q6xx9.png)

Notice how we use the `sadd` method to add our token in a set of key token. When you use the `sadd` method, it appends your value to the set if the set exists. If the set doesn't exist it will first create it.


Cool!


Let's now create our logout route.

- Update `src/routes/authRoutes.js` like this:

![carbon (13)](https://dev-to-uploads.s3.amazonaws.com/i/f4ug560p5a6ynxn0x7x9.png)
 

Lastly, let's update our Travis config file to tell Travis to install Redis-server before running our tests.

- Update `.travis.yml` and `redis-server` in services like this:

![carbon (14)](https://dev-to-uploads.s3.amazonaws.com/i/366f83edlnr2smnn3ads.png)


And that's it!


If you run the tests again, you should see that all our authentication tests are passing.


Now we can commit our changes to GitHub and create a PR which will trigger a build on Travis.


The last step is to provision a Redis database for our production env on heroku. The process is similar to how we added the Postgres database.

- Open the `Resources` tab on heroku and type `Heroku Redis` in the Add-ons search bar then select it. If "Heroku Redis" doesn't show up click [here](https://elements.heroku.com/addons/heroku-redis) to find it in the market place then click on the install button and confirm.

Note: You might be asked to add a credit card but make sure sure to select the `Hobby-dev` plan so that they don't charge you for usage. You can always upgrade to a paid plan after you have tested that everything is working well.


If the provision of Heroku Redis is successful, it will automatically add a `REDIS_URL` env variable.

Now you can head back to GitHub and merge our PR.

After Heroku has finished building, you can open POSTMAN and test our new endpoints and everything should be working well.


That's all for today, our Authentication endpoints are finished.


*Note: There are a few things we can do to improve our API. For instance, you might have noticed that the tokens of logged out users saved in our Redis database will stay there even after 30 days (after they expire). Since there's no reason to keep storing an expired token, we can set up a CRON job that will run maybe every day at midnight or every end of week or end of month to delete these expired tokens. But this is out of scope for this series now. I might write a post on how to implement such a functionality at the end of this series.*


In the next post, we are going to look at user roles, how to create an admin account, how to create a menu of dishes, ...etc. At the end of the post a customer will be able to place an order.


I want to thank you who is reading this post right now. If you have a question, comment, suggestion or any other feedback, please feel free to drop it in the comment box below.


***


See you in the next post! Happy New Year! :tada:


***


The code in this post can be found [here](https://github.com/the22mastermind/gourmet-api)