Node.js – Group/rule-based authorization approach in node.js and express.js

authorizationexpressnode.js

What are good strategies for role-based authorization in express.js? Especially with express-resource?

With Express-resource there are no handlers, so I think there are three options:

  1. Use a middleware
  2. Pass the authorization function to the resource and check each resource request separately
  3. Check authorization with every request right after authentication

Are there any other solutions?

Group/Role-based authorization is a pretty antique approach. Are there newer methods of access control? If not, how can role-based authorization be applied to node.js? Where to store group-rule relationships (with NoSQL/CouchDB/Redis)?

As an example, the structure:

/
  /forums
    /forums/threads

Each resource with index, new, create, show, edit update and destroy. Some people can edit/delete etc. threads and forums, some people shouldn't.

Best Answer

I would say that it's hard to solve this in a clean manner using express-resource, since it doesn't allow for route-specific middleware (at least not in a clean way).

I would opt for a similar layout as an express-resource module, but route it with plain old express. Something like this:

// Resource
var forum = {
  index: // ...
  show: // ...
  create: // ...
  update: // ...
  destroy: // ...
};

// Middleware
var requireRole = function(role) {
  return function(req, res, next) {
    if('user' in req.session && req.session.user.role === role)
      next();
    else
      res.send(403);
  }
};

// Routing
app.get('/forums', forum.index);
app.get('/forums/:id', forum.show);
app.post('/forums', requireRole('moderator'), forum.create); // Only moderators can create forums
app.delete('/forums/:id', requireRole('admin'), forum.destroy); // Only admins can delete forums

UPDATE: There have been ongoing discussions regarding route-specific middleware in express-resource, e.g. here. The prevailing view seems to be to have an array per action, e.g.:

var forums = {
  index: [ requireRole('foo'), function(req, res, next) { ... } ]
};

You could take a look through the pull requests and see if there is anything you could use. I totally understand it, of course, if you don't feel comfortable with that. I'm pretty sure we will see something like this in express-resource in the future.

The only other solution I can think of is along the lines of Jan Jongboom's answer, which would be to mount the resources with express-resource, but have middleware attached "outside" of that, something like:

app.delete('*', requireRole('admin')); // Only admins are allowed to delete anything
app.put('/forums/*', requireRole('moderator')); // Only moderators are allowed to update forums

But I regret that this leaks URLs all over the place.