Hello.
I am just starting with Feathers. We are going to create a multi tenant app, so we are thinking that in every request user has to pass the company ID.
This company ID will separate data in the database.
I read a similar issue https://github.com/feathersjs/authentication-local/issues/14 but could not understand what is the recoomended way to do it.
What is the correct way to accomplish this in Feathers? Would be nice that the company parameter are pre set in the service, so, we dont have to write that paramter in every service
Thank you!
There are different ways to approach this the most common being to limit the access to the authenticated users company in hooks. When requesting multiple entries, limit the query to context.params.user.companyId, when requesting a single one, check first if the companyId matches the user's company id:
app.service('myservice').hooks({
before: async context => {
const { companyId } = context.params.user;
if(!context.id) { // When requesting multiple entries
// limit query to users company id
context.params.query.companyId = companyId;
} else if(context.params.caller !== context.path) {
// Get the individual entry first and check if access is allowed
const result = await context.service.get(context.id, Object.assign(context.params, {
caller: context.path
}));
if(result.companyId !== companyId) {
throw new Error('You are not allowed to access this');
}
}
}
});
@daffl Why wouldn't you just use context.params.query.companyId = companyId for both cases? Seems like it would be better to limit the results returned from the data source rather than filtering them out in the application logic.
There is no filtering in the application logic. In the case of single entries queried by their id the adapters do not use the query so you'll have to check first if a user is even allowed to access it.
the code is going in the infinite loop because of context.service.get in get service
basically i want user to retrieve it's own resource
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue with a link to this issue for related bugs.
In Feathers 4 and later this can be done in an easier way via:
app.service('myservice').hooks({
before: async context => {
const { companyId } = context.params.user;
// limit query to users company id
context.params.query.companyId = companyId;
}
});
Hi, in our case we have the following structure:
context.params.query.accountId = accountId
we need to set the accountId only if the user sent it in the request headers. If the user sent it as a url parameter we can just ignore it and let feathers framework to do its magic.
to summarize it:
I think that account or company under the user object needs to be an array. this will make your services more flexible and will allow you to introduce a concept of Organization & Accounts where one Organization can have multiple Accounts that each one of them is a tenant
I think we should use the restirctToAccount approach and return 403 if the user try to access to some account that he/she not allowed to access to. In this way you can also use some kind of analytics that will help you to understand if someone tries to "hack" to your services
In case you use multi-tenant approach (and not single-tenant) you need to store the accountId in each and every record in your backend (of course that this is true only to resources which private to specific account. Sometimes you have shared resources which not require it)
@daffl what do you think about this approach?
Thanks!
Most helpful comment
In Feathers 4 and later this can be done in an easier way via: