• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/21/22

Set Up Authentication Middleware

Implement Authentication Middleware

We will now create the middleware that will protect selected routes and ensure that a user is authenticated before allowing their requests to go through. Let's start!

Create a new  middleware  folder, and an  auth.js  file inside it:

const jwt = require('jsonwebtoken');

module.exports = (req, res, next) => {
  try {
    const token = req.headers.authorization.split(' ')[1];
    const decodedToken = jwt.verify(token, 'RANDOM_TOKEN_SECRET');
    const userId = decodedToken.userId;
    if (req.body.userId && req.body.userId !== userId) {
      throw 'Invalid user ID';
    } else {
      next();
    }
  } catch {
    res.status(401).json({
      error: new Error('Invalid request!')
    });
  }
};

In this middleware:

  • Because many things can go wrong, put everything inside a  try...catch  block.

  • Extract the token from the incoming request's  Authorization  header — remember that it will also contain the  Bearer  keyword, so use the split function to get everything after the space in the header. Any errors thrown here will wind up in the  catch  block.

  • Then use the  verify  function to decode your token. If the token is not valid, this will throw an error.

  • Extract the user ID from your token.

  • If the request contains a user ID, compare it to the one extracted from the token. If they are not the same, throw an error.

  • Otherwise, all is well, and the user is authenticated — pass execution along using the  next()  function.

You now need to apply this middleware to your  stuff  routes, which are the ones you want to protect.  In your  stuff  router:

const express = require('express');
const router = express.Router();

const auth = require('../middleware/auth');

const stuffCtrl = require('../controllers/stuff');

router.get('/', auth, stuffCtrl.getAllStuff);
router.post('/', auth, stuffCtrl.createThing);
router.get('/:id', auth, stuffCtrl.getOneThing);
router.put('/:id', auth, stuffCtrl.modifyThing);
router.delete('/:id', auth, stuffCtrl.deleteThing);

module.exports = router;

Import your middleware and pass it as an argument to the routes you want to protect.

Now, from the front end, you should be able to log in and use the app normally. To check that unauthorized requests do not work, you can use an app like Postman to pass a request without an  Authorization  header — the API will refuse access and send a 401 response.

Congratulations!  Your API now implements token-based authentication and is properly secure. Or is it?

Your Turn! Authorization Flaw

In this coming podcast, I present the challenge that awaits you, which is to find the authorization flaw in our API.

It turns out that there is a security vulnerability in the API. One of the routes allows for requests to potentially be made by the wrong person. Can you figure out what the problem is? How can you fix it?

Here is your mission:

  1. Find the route that has this problem: Which route has this security vulnerability?

  2. Fix this vulnerability and find out how to solve this security problem.

Don't hesitate to listen to the challenge again, which comes with a clue to guide you to the solution ;) . And if you can't do it, don't worry, I'll explain the solution right away below.

Solving the Problem

Ready to discover the solution? Let's check it out!

The route with the security issue is indeed the DELETE route. Why? Because the front end doesn't send a user ID when requesting to delete a  Thing  . Therefore, you cannot check if the user making the request is the owner of the thing they are trying to delete. This means that, in theory, anyone with a valid token could delete anyone's thing. Quite a glaring security issue!

So how do you fix it? Knowing that you can't change the front-end app, you need to compare the user ID from the token with the  userId  field of the  Thing  you get from the database. The challenge is that you currently don't have access to the extracted user ID in the DELETE controller. However, there is a simple solution:

  • Create an auth object on your request object and place the extracted  userId  inside that  auth  object in your authentication middleware: 

req.auth = { userId };
  • In your DELETE controller, retrieve the  Thing  from the database, then check its  userId  against the ID you extracted from the token — if they match, delete the  Thing  ; if not,  return an error.

exports.deleteThing = (req, res, next) => {
  Thing.findOne({ _id: req.params.id }).then(
    (thing) => {
      if (!thing) {
        return res.status(404).json({
          error: new Error('Objet non trouvé !')
        });
      }
      if (thing.userId !== req.auth.userId) {
        return res.status(401).json({
          error: new Error('Requête non autorisée !')
        });
      }
      Thing.deleteOne({_id: req.params.id}).then(
        () => {
          res.status(200).json({
            message: 'Deleted!'
          });
        }
      ).catch(
        (error) => {
          res.status(400).json({
            error: error
          });
        }
      );
    }
  );
};

Now you know for certain that only the owner of a  Thing  can delete it!

Let's Recap!

  • jsonwebtoken's  verify()  method lets you check the validity of a token (on an incoming request, for example).

  • Make sure you add authentication middleware in the right order on the right routes.

  • Keep an eye out for security flaws!

What You've Learned in This Part of the Course

  • You added a User data model to store user information in your database.

  • You implemented secure password encryption to safely store user passwords.

  • You created and sent JSON web tokens to the front end to authenticate requests.

  • You added authentication middleware to secure routes in your API, meaning that only authenticated requests would be handled.

In the final part of this course, you will learn:

  • How to capture files coming in from the front end.

  • How to save them to your server.

  • How to delete them when they are no longer needed. 

Example of certificate of achievement
Example of certificate of achievement