Firebase - organizing shareable data? - firebase

I am trying to implement sharing model like in Google Docs. That is:
"Documents" are private by default.
Some documents are accessible to everybody.
Some documents are shared with users.
Right now my database stores document metadata in the database in per-user collections (${userid}/docs/${docid}) and Firebase Storage is used to store assets under similar paths. I set up trivial security rules to manage that.
Now I am trying to figure out sharing:
Should I move all documents into a global docs collection and have security rules check ACLs that are in the docs metadata?
Should I try to replicate shared documents in each user collection? Seems brittle but cloud functions should help with synchronizing data.
Keep documents under owning users and have security rules check permissions.
Is it even possible to have Firebase Storage security rules consult Firebase to fetch metadata?
Obviously, I am trying not to go overboard with database access, money is tight.

Should I move all documents into a global docs collection and have security rules check ACLs that are in the docs metadata?
The simplest solution would be to create a global collection, where each document should have an array-type field, that should contain the UIDs of the users that are allowed to read. The absence of the field should mean that the document is private. You should always check in your application code if a particular UID exists in the array, but the most important thing is to check that in your security rules. To be able to do that, you need to have to sign the user into Firebase Authentication. As soon as the user is authenticated the request.auth variable (including request.auth.uid) becomes available, meaning that their token is automatically passed with any calls to Firestore.
Should I try to replicate shared documents in each user collection? Seems brittle but cloud functions should help with synchronizing data.
This practice is called denormalization, and it's quite common practice when it comes to NoSQL databases like Firestore. But I cannot see any reasons why you would do that. It's not necessary in this case.
Keep documents under owning users and have security rules check permissions.
It doesn't really matter where the documents exist, you always use security rules against malicious users.

Related

Firebase Security Open Access

My android wallpaper app is connected to Firebase Cloud Firestore. It doesn't have any user authentication because I want the user to be able to use it without fuss. To do this, it must be in open access, meaning, the user is allowed to read and write. This is dangerous as he can also edit and modify the data if he knows the project id. The project id is visible in the url of the app so this is not really a good choice. Closed access is also not an option for obvious reasons.
Is there anything I can do to protect my data without need of a user authentication? I would love to see the code needed for the Cloud Firestore and Storage to protect the data. I want the user to read only and I, as the owner, should be the only one who could write. Please refer to the images attached. Thanks a lot for taking time to answer my questions.
My data structure in Firebase Cloud Firestore:
Securing your data with Security Rules
Firebase Cloud Firestore, Firebase Realtime Database and Firebase Cloud Storage are secured by their own Security Rules. They provide access control and data validation in a simple yet expressive format and allow you to control access to documents and collections in your database.
To build user-based and role-based access systems that keep your users' data safe, you need to use Firebase Authentication with Cloud Firestore Security Rules.
Your specific use case
I assume that you store your data in Firebase Cloud Firestore and the wallpapers in Firebase Cloud Storage. The user then gets a document with a link to download a wallpaper and maybe also can upload their own wallpapers to the database.
The dangers of an open database
As you mentioned allowing all reads and writes to your database in a production app is very dangerous. Obviously, anyone with your database reference will be able to read or write data to your database. Unauthorized users could destroy your backend and there are also possibilities that costs could increase exponentially. Therefore this is not recommended. There always should be some mechanisms preventing these scenarios.
Recommendation: Using anonymous authentication first and connect later with existing Identity Providers
I recommend that you use Firebase Authentication to create and use temporary anonymous accounts to authenticate with Firebase. These temporary anonymous accounts can be used to allow users who haven't yet signed up to your app to work with data protected by security rules while not being in the way of your user experience. If an anonymous user later decides to sign up to your app, you can link their sign-in credentials to the anonymous account so that they can continue to work with their protected data in future sessions.
You could also enable Google-Sign-In, Facebook Login, Twitter, GitHub, Microsoft, Yahoo, etc. to let users authenticate in a very fast and easy way without compromising on a security standpoint if using regular password authentication is not what you want (from a UX standpoint). FirebaseUI makes it really easy to add these to your existing app. Check out the official documentation on this topic.
Implementing Cloud Firestore Security Rules
The official documentation on this is really great on how to get started with Cloud Firestore Security Rules.
However these security rules could work for you:
Allow read access to all users to the root (Not recommended because this can create huge costs in production). Users don't have write (create, update, delete) access. In this case you can edit your data via the Firebase Console. Choose either option 1 or option 2 for your project.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Option 1: Matches any document in the 'root' collection.
match /root/{rumiXYZ} {
allow read: if true;
allow write: if false;
}
// Option 2: Matches any document in the 'root' collection or subcollections.
match /root/{document=**} {
allow read: if true;
allow write: if false;
}
}
}
The {document=**} path in the rules above can be used to match any document in the collection/subcollections or in the entire database. This should however not be necessary for your use case. Continue on to the guide for structuring security rules to learn how to match specific data paths and work with hierarchical data.
Don't forget to secure your Firebase Cloud Storage too!

How to restrict firebase storage files only for the paid user?

I have the file stored in firebase cloud storage. This file will only available for the paid user download.
How to set up security rules to allow the paid user to have read access to that file?
[Updated]
I use the cloud firestore to store user collection
Each user doc contain
uid
email
name
photoUrl
provider
status
stripeCustomerId
purchasedProducts << this is the array of product name
I can verify paid user by looking if the product exist in purchasedProducts array.
However, inside the security rule from Firebase storage, it seem I can't access resource (user collection) in there. Or am I missing something?
Thanks
There is no way to access Cloud Firestore from within the security rules for Firebase Storage.
That means the only ways to currently implement your use-case is to:
include the necessary information in the ID token of the user, as a custom claim, which is then also available in security rules.
include the necessary information about the user (probably their UID) in your security rules
Since the second approach requires that you update your rules for every paying user, it's not very common.
Setting a custom claim can be done through the Firebase Admin SDK, for example from a Cloud Function that triggers when you write their payment information to Cloud Firestore.
Once you set the custom claim it may take up to an hour before it's available on the client, and from there in the security rules. The reason for that is that the claims are included in the ID token, which only auth-refreshes once an hour. If you want to get the updated claims sooner, you can force a refresh of the user's profile on the client.
Another approach you can try is to delete a file right after it was uploaded using the functions.storage.object().onFinalize webhook - this is wehere you can access the database and check if the user was allowed to upload the file.
Even though it may look a bit 'hacky' at the first glance, this is really a precaution measure in the first place - the UI itself would restrict the upload for the "good" users. And for those who messes up with the source code and tries to circumvent the system, onFinalize would do the job.
You can access cloud firestore through the firestorage service security rules:
https://firebase.google.com/docs/storage/security/rules-conditions#enhance_with_firestore

Firebase storage security rules: Is it possible to use firebase database collection fields value in security rules? If not how can I solve this issue?

I have migrated data to firebase storage, which have structure like:
<fileId>/<filename>
I can store fileIds associated with user in firebase database like:
<userId>/<fileids>
Is there anyway I can get something like auth.uid/fileids from firebase database in security rules of firestorage, to only allow to read file to auth user, if fileIds belong to him. If not, what all options are available to achieve the same and which one is best among them?
It's currently not possible to use data from databases in your Cloud Storage security rules. You can only use information about the object stored in the bucket.
You could instead write a backend API that performs all the checks and then operates on the object in storage, or store information about the file in its metadata for use in rules.

Firebase Storage Project Level Permissions

I store files in Firebase Storage as follows:
/{projectId}/{file}
Users have different permissions based on the projectId, and can easily have some level of permissions on tens (or even hundreds) of projects. For each project, he or she is assigned a role, which determines what operations are permitted.
I have these roles stored in Firestore by the user, but they are inaccessible in the Firebase Storage security rules.
Furthermore, setting custom claims with all project roles for each user will easily exceed the 1000 byte limit for custom claims. Is there any way to lookup permissions at the time of file access, i.e., update, delete, read/download?
There is currently no way to access documents in Firestore from security rules defined for Storage. Custom claims is the only common permissions system shared between the two products.
Consider routing access through Cloud Functions if you need more a more flexible logic, either before or after the fact.
Please feel free to file a feature request with Firebase support.

Firebase Storage allow write to folder to multiple users

I have created an app, that allows users to create a simple photo collection. All the data is stored in the Firebase Cloud Firestore. The images are uploaded to Firebase Storage.
The owner of a collection can invite any other user to access his collection and upload photos.
The sharing of data in Firestore works fine. But now I have problems with writing the security rules for Storage. Does anyone know, how I have to write the rules, so that any user added to the collection can access the images as well? The files for each collection are stored in separate folders.
Firebase security rules currently do not bridge between products. You can't use data in Cloud Firestore in security rules for Cloud Storage. The only things you have access to in security rules for Cloud Storage are object metadata and Firebase Authentication custom claims per user. You will have to figure out a way to make changes to either one of those things to implement your permissions.
Alternatively, you can direct all access through Cloud Functions to decide if the user should be able to access the content, but bear in mind that Cloud Functions responses can only be 10MB maximum.
You should look into creating a private group and authenticate the group with help of private token.
https://firebase.google.com/docs/storage/security/user-security#group_private

Resources