constrain users access to only their own personal data
Regarding user access endpoints ( not related to admin/masterkey):
Correct me if I'm wrong, but it looks to me that the authentication and routing code currently has no built in mechanism for pŕeventing an authenticated user from accessing data related to other users by simply leveraging querymen to make queries that pass some other user's id ?
Hopefully I just failed to locate this, but in case it really doesn't exist, and because this is such a common requirement, it would be great to include a build-in mechanism for this so that this functionality could be activated by just adding an item or attribute ( could be named something like requireOwnId or ownerOnly ) to a route's middleware chain.
I don't know if I got it. Can you list some requests, the expected and the actual behavior? Something like this:
-
Request:
GET /users/123authenticated as user456 - Exptected: don't receive anything
-
Actual: received
id,pictureandname.
So I can understand better and see if this is a bug.
Ok, I eventually found it as a utility function inside of response/index.js called authorOrAdmin.
However I'd much rather have it as a pluggable middleware that I could apply at the route level like this:
router.put('/:id',
authorOrAdmin(),
token({ required: true }),
body({ name, image, favorites, history }),
update)
To have it as a configuration at the route level would provide much better separation of concerns between the permissions logic and the other parts of the code and that how I see it done in most security libraries such as: https://github.com/deitch/cansecurity
It would also be very nice to have the generator ask me if the 2 GET endpoints (listing and details) are available only to the author or to any authenticated user.
To give you example scenarios... a GET posts/ and GET post/:id endpoint would be usually available to any authenticated user, however a GET history/:id and GET preferences/:id endpoint would not be because they'd be part of a users own private data set.
authorOrAdmin needs two things:
- the current user who made the request;
- the fetched resource to compare the user with the author field.
The current user is passed by the passport middleware (token({ required: true })), so authorOrAdmin must be called after that.
router.put('/:id',
token({ required: true }),
authorOrAdmin(),
body({ name, image, favorites, history }),
update)
But the resource is only fetched inside the controller method update, thus authorOrAdmin is there.
To have this as a middleware we need to create another middleware just to fetch the resource, something like this:
router.put('/:id',
token({ required: true }),
body({ name, image, favorites, history }),
fetchResourceAndPassToReq(),
notFound(),
authorOrAdmin(),
update)
I don't like it very much, but if you change the code to something like this, please share with us. :)
Yes, I agree it may not be trivial to do. I expanded my comments and fixed the link to a library that implements it at routing level. See another example of it implemented at the route level here: http://stackoverflow.com/a/24302705/6771649
I'm not so sure that 2 additional middleware would really be needed in order to accomplish this. Why not have the route level middleware only set a flag on the request object and use the existing downstream middleware to execute the actual validation based on the provided flags?
Hi, @jay-jlm I still don't understand how you solved this. I am currently facing the same problem, please assist