Should REST API URIs be authorized based on the parameters, not just the resource?

Viewed 79

I am trying to create a backend for my web app. The app should have users which should be able to create posts about different things and edit them if needed, and also admins which can edit or even delete posts of different users if they deem them bad. I am having trouble managing user permissions and authorizations, especially regarding which resource a user is allowed to access but I have come to two possible solutions and I am not sure which one is the best.

  1. Use a specific route (such as /users/me) that allows one to modify his own data and leave the /users/{id} resource only for usage by admins.
  2. Handle the authorization based on whether the given id matches the user in the authentication (/users/{id} only allows you to DELETE or PUT if you are the same user as the one in the URI based on auth token).

I want to abstract the authentication and authorization layer from the database interaction layer, but I am finding it pretty hard to do using the second method because checking if a user "owns" a resource before he accesses it requires you to retrieve that resource, but I also think there are some merits to the second method aswell.

Which one would be the best architectural choice regarding code scaling and quality, and which one would you choose for a project (maybe even a mixture of the two)?

I mention that my backend uses node.js and express which interact with a MongoDB database through Mongoose.

2 Answers

I've done plenty of stuff in your second model, where there's a lot of logic in the endpoints of the form "can this user do that operation on such-and-such an object"?

That experience leads me to recommend your first model, if these conditions hold true.

  1. The stuff users can do to their own data is less complex than the stuff privileged users can do to any user.

  2. You can come up with models (in MVC lingo) that allow some level of code reuse.

  3. The lion's share of API operations will be users manipulating their own data.

Why do I suggest your first option?

  • Testability. A straightforward set of API operations for end-users to handle their own data can be tested, and regression tested, more cheaply than a more elaborate set of APIs dependent on more intricate privilege stuff.

  • Scalability. If your app scales out, you can move your administrative web endpoints to their own server or servers, and leave the simpler but heavily-trafficed user endpoints on another batch of servers. For this last one to work, you don't want endpoints like /users/me/whatever and /users/{id}/whatever but rather /me/whatever and /users/{id}/whatever. Don't give the /me endpoints any way for a user (possibly a hostile cybercreep) to specify a user other than the current user's session token or credentials.

  • Security. A less capable set of endpoints is easier to secure than a more capable set of endpoints. Endpoints that accept fewer data items are inherently more secure. (You still have to secure the administrative endpoints, of course.)

You should go with the second option if you want your API to be RESTful and consistent.

In REST, resources are identified by URLs. A URL like /users/me implies a globally unique resource, which turns out to not actually be the case.

The alternative is better aligned with REST. A URL like /users/1234 indicates a different resource than /users/5678. That's actually also the behaviour you're after, as far as I can tell.

From a code maintainability view, it usually pays to align the model with the desired behaviour.

This enables you to decouple the identity of the resource from the identity of the client.

Related