Michael Messerli
Michael Messerli

Reputation: 95

Node.js API - Allow users to only update and delete their own object

I am trying to build a RESTful API using Node.js w/ Express. I am fairly new to the MEAN stack, and want to use best practices. The concept I'm having trouble grasping and implementing is the following:

Restricting routes like PUT and DELETE on a user object, to only allow requests from users who 'own' this object.

One possibility I've thought of:

Creating secret token for users that matches token in DB

So when creating a user I assign them a token, store this in the DB and attach it to their session data.

Then my middleware would look something like:

router.put('/api/users/:user_id', function(req, res, next) {
  // already unclear how this token should be transfered 
  var token = req.headers['x-access-token'] || req.session.token;

  // update user (PUT /api/users/:user_id)
  User.findById(req.params.user_id, function(err, user) {
    if (err) {
      res.send(err);
    } else if (user.token != token) {
      res.json({ sucess: false, message: 'User not same as authenticated user.' });
    } else {

      // set new information only if present in request
      if (req.body.name) user.name         = req.body.name;
      if (req.body.username) user.username = req.body.username;
      ...

      // save user
      user.save(function(err) {
        if (err) res.send(err);

        // return message
        res.json({ message: 'User updated.' });
      });
    }
});

Questions I have regarding best practice

Sidenote

This is a learning project for me, and I am aware of libraries like Passport.js. I want to learn the fundamentals first.

I have a repo for this project if you need to see some of the surrounding code I'm using: https://github.com/messerli90/node-api-ownership

Edit

I would accept a good RESTful API book recommendation, where these points are covered, as an answer.

Edit 2

I actually found a lot of the answers I was looking for in this tutorial: http://scottksmith.com/blog/2014/05/29/beer-locker-building-a-restful-api-with-node-passport/

I was trying to do this without the use of passport.js but a lot of the concepts covered in the article made some of the mechanics of an authorized API clear to me.

Upvotes: 4

Views: 5249

Answers (3)

peyo
peyo

Reputation: 371

Reading material: Implementing Access Control in Node.JS

Found this super clear article on how to allow users to only delete replies they own. Hope it helps.

What worked for me:

.delete(requireAuth, async (req, res, next) => {
    const knexInstance = req.app.get("db");
    const comment = await CommentsService.getById(knexInstance, req.params.id);

    if (comment === undefined) {
      return res.status(404).json({
        error: {
          message: `Comment doesn't exist.`
        },
      });
    }

    if (comment.users_id !== req.users.id) {
      return res.status(401).json({
        error: {
          message: `You can only delete your own comments.`
        },
      });
    }

    CommentsService.deleteComment(knexInstance, req.params.id)
      .then((numRowsAffected) => {
        res.status(204).end();
      })
      .catch(next);
  })

Upvotes: 0

Evgenij
Evgenij

Reputation: 21

A 'bit' too late, but if someone is still looking for an answer, here is how i did it:

router.put('/', function(req, res) {
    var token = req.headers['x-access-token'];
    if (!token) return res.status(401).send({auth:false, message:'No token provided'});
    jwt.verify (token, process.env.SECRET, function (err, decoded) {
        if(err) return res.status(500).send({auth:false, message:'failed to auth token'});
        User.findByIdAndUpdate({_id: decoded.user_id}, req.body, function(err, user) {
            if (err)
                res.send(err);
            res.json({username: user.username, email: user.email});
        });
    });
});

Just pass the user id that is stored in the token to the mongoose function. This way the user who sent the request can only update or delete the model with his ID.

Upvotes: 2

ryanman
ryanman

Reputation: 3834

If I understand your question, this is an API, and the client (not a browser) is passing the secret token (api key) in the request, in a header. Seems reasonable. Of course, you must require https to protect the api key. And, you should have a way for users to revoke/regenerate their API key.

So far, I don't think you need to store anything in the session. It seems like storing the token in the session just complicates things. Presumably, if you are going to establish a session, the client has to include the token in the first request. So, why not just require it on each request and forget the session? I think this makes life simpler for the api client.

Upvotes: 2

Related Questions