Skip to content

Gotchas

Here is the place for common mistakes for defined rules and unexpected behavior.

General

Mind the order of your rules

The following example results in an error, because all rules will be considered in the given order. Rules overwrite/specify the rules before.

ts
const ability = defineAbility((can, cannot) => {
  can("read", "users");
  cannot("read", "users");
});

Fields

Conditional subset

ts
const user = { id: 1 };

const ability = defineAbility((can, cannot) => {
  can("read", "users", ["id", "name", "email"]);
  can("read", "users", ["password"], { id: user.id });
});

What do you expect?

Do you think it results in { $select: ["id", "name", "email", "password"] } for the current user and { $select: ["id", "name", "email"] } for all other users?

Actual behavior:

  • current user: { $select: [] }
  • all other users: { $select: ["id", "name", "email", "password"] }

correct definition:

With the following configuration you only get ["id", "name", "email"] for all other users and the complete user item for the current user.

ts
const user = { id: 1 };

const ability = defineAbility((can, cannot) => {
  can("read", "users", ["id", "name", "email"], { id: { $ne: 1 } });
  can("read", "users", { id: user.id });
});

You're not allowed to get on 'users'

To prevent the error You're not allowed to get on 'users', you need to define the abilities right after your authenticate() hook and before the authorize() hook for the get method of the user service.

ts
// src/services/users/users.hooks.js

export default {
  before: {
    get: [
      authenticate('jwt'),

      // Add this to set abilities, if a user exists
      context => {
        if (context.params.ability) { return context; }
        const { user } = context.params
        if (user) context.params.ability = defineAbilitiesFor(user)
        return context
      }

      authorize({ adapter: '@feathersjs/mongodb' }),
    ]

    // ...
  },

  // ...
};

Released under the MIT License.