I have an Angular web-app with Firebase as the serverless backend technology. The current purpose of the web-app is to let registered users create documents for offered products. While business users can create unlimited product-docs, free users should only be able to create one product-doc.
Collections:
{id: string, isBusiness: boolean, name: string, email: string, productIds: string[]}
{id: string, title: string, description: string, userId: string}
Whenever a user creates a new product-doc with
public createProduct(id: string, product: Product) {
return this.afStore.collection('products').doc(id).set(product, { merge: true });
}
I run a cloud function with the trigger onCreate
to add the id
of the created product-doc to the productIds
field of the according user-doc. This allows me to check frontend-side if(!user.isBusiness && productIds.length > 1)
to limit the creation of multiple product-docs for free user accounts.
However, I recently did a pen-test and the testers accidentality found out that if you open the website on multiple browser tabs and simultaneously run the createProduct()
function on each tab at the same time, all of them will execute allowing a non business user to have several product-docs.
Has anybody dealt with a similar situation? I don´t know if there is a way to avoid this with Firestore Security Rules, or if there is something to wait until active transactions are finished.
The current implementation suffers from a race condition: A 'non-business' user may create an arbitrary number of products until the first productId is written to the user doc.
In my eyes, the most straightforward way to solve this, is to pre-populate the non-business users with a productId, and restrict their ability to assign new ones.
public createProduct(id: string, product: Product, user: User) {
if (user.isBusiness) {
return this.afStore.collection('products').doc(id).set(product, { merge: true });
} else {
return this.afStore.collection('products').doc(user.reservedProductId).set(product, { merge: true });
}
}
Caveat: As with any frontend code, this will not prevent malicious actors from manipulating it to their liking. If this behaviour is a security concern, you will want to restrict write access to the products collection by non-business users. Consider implementing an appropriate security rule / cloud function. You can only trust the backend.