Search code examples
feathersjs

Enforce access rights on Feathers.js resources


How restrict access access to a subset of a resource exposed via Feathers.js?

For example, how would you restrict CRUD operations to a user's tenantId or groupId?

So far, I have this somewhat working with a service hook. The code below works for "get/find" but not for "put/update". Tracing the resulting mongo query, only "_id" is being queried during "update" scenario. It seems my query is being ignored or overwritten.

// my-resource.hooks.js
module.exports = {
    before: {
        all: [
            function(hook) {
                const userGroupId = hook.params.userGroupId; 
                // NOTE: userGroupId gets extracted via an Express js hook from auth/header

                hook.params.query = {
                    ...hook.params.query,
                    userGroupId 
                }
                return hook;
            }
        ]
    }
}

UPDATE

So far, I've extended the feathers-mongoose service implementation and update this bit of code. Seems to do the trick, but I'm still wondering if I'm missing something.

_get(id, params = {}) {
    params.query = params.query || {};

    const discriminator = (params.query || {})[this.discriminatorKey] || this.discriminatorKey;
    const model = this.discriminators[discriminator] || this.Model;
    let modelQuery = model
        .findOne({
            [this.id]: id,
            ...params.query
        });

Solution

  • In order to know if the user is allowed to modify an individual resource the best way is to retrieve it first, check the permissions and throw a Feathers error if they are not allowed:

    const errors = require('feathers-errors');
    
    // my-resource.hooks.js
    module.exports = {
      before: {
        all: [
          function(hook) {
            // NOTE: userGroupId gets extracted via an Express js hook from auth/header
            const userGroupId = hook.params.userGroupId;
    
            // If there is an id, get the entry first to check the permission
            if(hook.id !== undefined && hook.id !== null) {
              return hook.service.get(hook.id).then(entry => {
                if(entry.userGroupId !== userGroupId) {
                  throw new errors.Forbidden('You are not allowed to access  this');
                }
    
                return hook;
              });
            }  
    
            // Otherwise just restrict the query
            hook.params.query = {
                ...hook.params.query,
                userGroupId 
            }
    
            return Promise.resolve(hook);
          }
        ]
      }
    }