A meteor method is a remote procedure call (RPC) and is - so to speak - an API definition of what functionality can be called on your server from some remote client.
It is thus very important that we secure our methods with all necessary checks and make sure that only legitimate requests are accepted.
We use meteor for our SaaS product and thus most of our meteor methods require at least a minimal set of sanity checks such as checking if a user is signed in.
This is easily achieved with something like this:
Meteor.methods({
'some.method'() {
const { userId } = this;
if (!userId) throw new Meteor.Error(403, 'Permission Denied');
// actual method body
}
});
Now, because our users need a paid subscription to be able to perform certain actions (or to perform certain actions a certain number of times) combined with the possibility to create projects and grant access to other users with specific permissions, our sanity checks have grown to something like this:
Meteor.methods({
'some.method.you.pay.for'(someDocumentId) {
check(someDocumentId, String);
const { userId } = this;
if (!userId) throw new Meteor.Error(403, 'Permission Denied');
const document = Documents.findOne(someDocumentId);
if (!document) throw new Meteor.Error(400, 'Document not found');
if (document.owner !== userId) throw new Meteor.Error(403, 'Permission Denied');
const hasSufficientCreditsFromSubscription = Meteor.call('getCreditsForUser');
if (!hasSufficientCreditsFromSubscription) throw new Meteor.Error(400, 'Insufficient Credits');
// etc. etc.
}
});
Now imagine you have tens or hundreds of meteor methods that all need checks like these. We’re not yet there but it’s only a matter of time .
This is extremely hard to maintain. In the case that requirements change, new features are added or some checks are changed, maintaining and updating all methods becomes a nightmare. It also requires us to write a huge amount of boilerplate for something that could be solved more elegantly.
We have thought about adding some kind of hooks for meteor methods or try if we can apply decorators on meteor methods. Another approach would be to define a set of meteor methods that do the checks and call them instead.
The actual checks in our case can be much more complicated and usually include a subset of simple checks like if a user is signed in and if a resource exists themselves. That is, some checks are performed in order to perform more complicated checks. Thus, moving some of these checks to meteor methods requires us to perform some checks multiple times (because those methods are exposed again).
Do you know any guidelines or best practices from your experience that have helped you secure a large set of meteor methods for a defined but changing and growing set of checks?