Auth, Auth, Auth?

rallofield

Raoul Vandevelde

Posted on June 18, 2023

Auth, Auth, Auth?
Written by Raoul Vandevelde

Middlewares in Node.JS

During my training at BeCode and a dive into the world of building API's with Express, it was time to build an API ourselves.
The exercise consisted of making a webshop API of which we could choose the subject.
Ofcourse, this came with a lot of questions like, how would a user experience this in their browser? Would they be able to add, change and delete things from the database at will?

That's something an e-shop owner would not want, right?
Our goal, after having made all the necessary controllers and database connections:

  • Make authentication and authorization middlewares so only those who are authorized by the admin (in this case, the shop owner), have access to the specified functions in the controllers.

Setup and context

A bike-shop owner has asked you to create an API in which he can add his collection of bikes, to sell on his website.

Let's just asume you have created your file-structure and are ready to create some middlewares!

Hells yea!

hold_your_horses gif

Hold your middle-horses - some developer

Let's first go into something called the JWT or JSON Web Tokens, which we will need to securely transmit our information to authenticate. Be sure to read up on it before continuing.

We define our JWT_ACCESS_TOKEN in our .env file where we will assign to it a digital key.

For Example:

JWT_ACCESS_TOKEN = YRskPK2K5CoW0lOp4Fl9
Enter fullscreen mode Exit fullscreen mode

Having done this we will install the package needed to work with JWT.

npm install jsonwebtoken
Enter fullscreen mode Exit fullscreen mode

Ready? Authenticate, Authorize, Set. Go!

Let's get to work!
Make a folder in your structure which will contain all your middlewares.
No need to think deeply about the name, just call it 'Middlewares'.

Create a file named admin_authentication.middleware.js in the new folder you just created, and require at the top the following packages:

const jwt = require('jsonwebtoken');
const dotenv = require('dotenv');
dotenv.config();
Enter fullscreen mode Exit fullscreen mode

For the admin authentication we will check two things:

  1. Is the user logged in?
  2. If so, is the logged in user an admin?

Let's get into the first point.

Logged in?

We will check if the user accessing the page is logged in or not.
This first part will look something like this:

const authenticateAdmin = (req, res, next) => {
    // Extract the access token from the request cookie
    const accessToken = req.cookies['webshop.process'];
    console.log(accessToken);

    if (!accessToken) {
        // If access token is not present, user is not authenticated
        return res.status(401).json({ message: 'Unauthorized' });
    }
Enter fullscreen mode Exit fullscreen mode

Let's break down the code shall we?

break_it_down

  • The function defined takes three parameters into account: req , res and next. Next is used to call the next middleware or route handles when the middleware has passed.

  • We extract the access token from the cookie with req.cookies['webshop.process'].

Note: the ['webshop.process'] is specific to what we are doing here and should be updated to the name you used in your login- or sessionscontroller when signing the cookie.

  • In the if statement we check if the access token is present or not.

Not, would mean they are not logged in and as such not authenticated to utilize the functionality being called in the routes.

  • We respond with a 401 Unauthorized message.

If they are indeed logged in, we will continue with the next part of the function.

Are they admin?

After having checked if they are logged in, let's check if they are an admin or not.

We consider the following code:

try {
// Verify the access token
 const decoded = jwt.verify(accessToken, process.env.JWT_ACCESS_TOKEN);
  console.log(decoded);
  console.log(decoded.is_admin);
// Check if the user is an admin
  if (decoded.is_admin !== 1) {
     return res.status(403).json({ message: 'Forbidden' });}
        req.user = {
            is_admin: decoded.is_admin};
// Attach the decoded user data to the request object
next(); 
// Proceed to the next middleware or route handler
Enter fullscreen mode Exit fullscreen mode
  • If the access token is indeed present, the function will verify it with jwt.verify() -> this will pass the token and the JWT digital secret (from the .env file) with process.env.JWT_ACCESS_TOKEN.

  • If this verification succeeds, it checks if the decoded payload has is_admin equal to 1 (make sure your is_admin column is a boolean type, where 1 makes sure it is an admin).

If this is not the case, the function will respond with a 403 Forbidden status and a JSON message if needed.

  • Lastly, the is_admin property is being attached to the req.user if the user is indeed an admin and the next() function is called to proceed to the next middleware or route handler.
} catch (err) {
        // If access token verification fails, user is not authenticated
        return res.status(401).json({ message: 'Unauthorized' });
    }
Enter fullscreen mode Exit fullscreen mode
  • Because we used try in our function we should also use a catch(err) to make sure something is returned when the verification fails

Final touches before testing

  • Make sure that you export the function by putting this line below on your page:

module.exports = authenticateAdmin

  • Access your routes file where you want to use the middleware and require it at the top:

const authenticateAdmin = require('../../MIDDLEWARES/admin_authentication.middleware');

Depending on your filestructure this path will vary.

  • Add the middleware(s) in the chronological order:
router
.route('/admins')
.get(authenticateAdmin,adminController.getAdmins)
Enter fullscreen mode Exit fullscreen mode

This route will first check the authenticateAdmin middleware, before it executes the function specified behind it coming from the controller. In this case the admin must first be logged in and authorized before it can see a list of all the admins in the database.


The Test

To test our middleware but also to test your routes and methods in your controller, we will use a program called Postman. Postman is a handy little tool that will help us check if the middleware we just made really works.

Pro-Tip: To read more about how to use Postman in your project, this article will get you a long way!

  • Test route without being logged in:

Forbidden_access

  • Log in with an admin account:

login

  • Test it again

authenticated

As you can see, the middleware works for this route. But you can now apply it also for other routes where only an admin would have access to.


Thanks for reading!

I sincerely hope this has helped you out. I will keep updating this article if I find more during my middlewaring during my journey in web-development.

Cheers!

youshallnotpass

💖 💪 🙅 🚩
rallofield
Raoul Vandevelde

Posted on June 18, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Auth, Auth, Auth?
webdev Auth, Auth, Auth?

June 18, 2023