0

Let's say that I want to check if a logged in user has administrative privileges before doing a task. How can I achieve this asynchronously if I have to wait for the response from the mongoose function checking the database to know if the user has privileges or not?

Say I have a user model like such:

  const UserSchema = new Schema({
  username: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  },
  isadmin: {
    type: Boolean,
    default: false
  }
});

Then I create a function for checking if the user is an administrator

function isAdmin(id) {
  let is = false;
  User.findById(id)
    .then(user => {
      if (user) {
        if (user.isAdmin) {
          is = true;
        }
      }
    })
    .catch(err => console.log(err));

  //Now I want to wait for the findById function to resolve before returning this function
  return is;
};

Then let's say I want to check if a user is an admin before doing something like deleting a comment from a post

router.delete("/post/comment/:id"),
  (req, res) => {
    if (!isAdmin(req.user.id)) {
      return res.status(401).json({ notauthorized: "User is not admin" });
    }

    //Go ahead and remove content
  }
);
Darren
  • 1,774
  • 4
  • 21
  • 32
  • You could have `isAdmin` itself return a promise and chain it in your `router.delete`, or you could look into using [`async`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)/[`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await). – Alexander Nied Jun 14 '18 at 01:53

2 Answers2

2

You can just add the admin key inside the user object in passportjs or the package you use for doc. After that you can just create a middleware and use it as you want in any route. If not look at the second option. You can query the user in this middleware and check if admin is true. Hope that make sense :)

const requireAdmin = (req, res, next) {
  if (req.user.admin) {
    return next();
  }

  res.sendStatus(401);
}

// Or you can

const requireAdmin = (req, res, next) {
  User.findById(req.user.id)
    .then(user => {
      if (user.admin) {
        return next();
      }
      res.sendStatus(401);
    }).catch(() => res.sendStatus(401))
}


router.delete("/post/comment/:id"),
  requireAdmin,
  (req, res) => {
    // do logc
);
EQuimper
  • 5,811
  • 8
  • 29
  • 42
  • I am using JWT for authentication with passport and React on the frontend. Initially I was sending the isadmin value of the user within the JWT payload to React, and including that in the API request, however I didn't think it was very secure to rely on solely the request from the frontend to answer if the user is an administrator. Am I correct in assuming using the function as middleware in router.delete will wait for the result from that function before continuing? – Darren Jun 14 '18 at 01:58
  • The thing is if you dont want to request you gonna need to have this value in the jwt yes. If not you can maybe make use of in-memory stuff etc. You can for adding security make the token for the admin expire quickly if you want to add more security. This stuff gonna be more app related. But the way I show you it's the way you gonna see a lot around the web. I think this is kind of the standard. But I understand what you mean. – EQuimper Jun 14 '18 at 02:03
  • Yeah I just implemented Francisco's which is just a slightly different version of what you've got here. I was mostly not understanding how middleware worked in the router functions I think, but we're all squared away now. – Darren Jun 14 '18 at 02:06
  • Perfect if you need help understanding middleware this link should help you. This is really important to understand them when you work with express. https://expressjs.com/en/guide/using-middleware.html Hope that can help :) – EQuimper Jun 14 '18 at 02:07
1

Similar to EQuimper, but with https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

async function isAdmin(req, res, next) {
  const user = await User.findById(req.user.id).exec();
  if (!user) {
    next(new Error("User not found"));
    return;
  }
  if (!user.isAdmin) {
    res.status(401).json({ notauthorized: "User is not admin" })
    return;
  }
  next();
};

router.delete("/post/comment/:id", isAdmin, (req, res) => {
  // Do something
});

The isAdmin middleware could also be written as:

const isAdmin = async (req, res, next) => {
  const user = await User.findById(req.user.id).exec();
  if (!user) {
    next(new Error("User not found"));
    return;
  }
  if (!user.isAdmin) {
    res.status(401).json({ notauthorized: "User is not admin" })
    return;
  }
  next();
};
Cisco
  • 20,972
  • 5
  • 38
  • 60