I've been trying to get the Firestore rules to play nice for a while now and every time I think I get them right, another portion stops working for some reason.
I'm trying to have some simple rules, if you made the document that document and any child documents or collections, you can create, edit and delete them. I thought this was pretty simple but alas I keep getting permission denied errors.
Rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
match /users/{userId}/{document=**} {
allow read, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
When doing just the match /users/{userId} I was able at one time able to create user documents but I couldn't create child documents or colletions.
When doing just the match /users/{userId}/{document=**} I could no longer create users but any existing users I could add child documents and collections and do everything expected.
This combination of both rules doesn't seem to work either.
I keep getting [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation. when I try to create a user with this statement:
await FirebaseFirestore.instance.collection('users').doc(googleUser.uid).set(
{
'created': now,
'lastLogin': now,
'name': name,
'email': email,
},
);
Now nothing works. I deleted all my authentication accounts and my Firestore data and wanted to start over but it simply will not create the data in Firestore.
Any suggestions would be greatly appreciated as I'm going in circles and nothing is working anymore which is extremely frustrating as it did at one point but no longer does.
edit All of my testing is being done on a real Android phone.
After walking away from my computer and thinking more, I figured out what it was. My App Check debug token changed somehow.
Once I added the new value from the debug console everything started working again.
I'll leave this answer here in case this saves anyone else some headaches in the future!
Edit: Additionally, ones App Check debug token will change anytime you clear storage on your app on the device. Which is why mine was changing.
Related
I created a rule in Cloud Firestore to read/write based on wether the user is signed in through Firebase Auth or not.
From my understanding based on what I read in the official documentation, the following code should allow the signed in user the correspodent permissions to the userID document inside the data collection.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/document {
match/data/{userId}{
allow read, write: if isSignedIn();
}
}
function isSignedIn(){
return request.auth != null;
}
}
Database image:
The idea is that after the user logs in, the code I wrote should verify if there is a document called ReservedID in data/userID/ReservedID, and if there isn't, create one for him, however, this collection is never created.
It does work if I remove the security rules.
Image of the error that shows in Android Studio:
However, after signing in using mAuth.signInWithEmailAndPassword, the user still can't write or read from the database. The Android Studio Logcat provides this message:
PERMISSION_DENIED: Missing or insufficient permissions.
Did I misunderstood how to properly set these rules in my database? Or could it have something to do with the code itself?
It looks like there are some issues with the code.
The match statement should specify the path to the collection and whole documents, rather than just one document. i.e you are using the path for single document match /databases/{database}/document instead of below path:
match /databases/{database}/documents {
match/data/{userId}{
}
With the above changes, the code will look like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match/data/{userId}{
allow read, write: if isSignedIn();
}
}
function isSignedIn() {
return request.auth != null;
}
}
You can verify this on playground
I'm initializing FireStore in my Chrome extension like they explain in the docs:
import { initializeApp } from "firebase/app";
initializeApp({apiKey:'...', ...});
Then I call setDoc to store a document. Everything is fine when I set allow read, write: true in the access rules. But when I change it to allow read, write: if request.auth != null, all my Firestore requests start failing with Error adding document: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
I assumed that providing my Firestore API key and other data in the initialization code would automatically make it authenticated. However, Firestore thinks that my requests are anonymous for some reason. How do I fix this?
Here's my config:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null
}
}
}
The request.auth check if the request is coming from authenticated user in firebase authentication.
The Firestore API key is only to send the request to the correct firebase project, but it doesn't make the request.auth != null.
You need to authenticate against firebase authentication in order to get request.auth != null. But in your case I beleive you don't want the users to authenticate, so you need some other security rule to get what you really want to check.
I understand it's not good to set your read and writes to all users on Firebase;
But what if you leave your reads to all and your writes to only authenticated users?
What is the worst thing that could happen?
Is it easy for someone to gain access to the firebase project?
I'm currently using cloud firestore.
Sorry if this seems a little dumb, I'm new to this:
Thanks,
Jacob
EDIT: Current Rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if request.auth != null;
}
}
}
I've worked with Firebase for a long time and I've come to realize that the rules are a very good way to prevent unauthorized access to your data, by any third party - especially when used in tandem with rules set within your Application (be it Web, iOS or Android).
Personally, I usually set my rules to the following:
service cloud.firestore {
match /databases/{database}/documents { match /{document=**} {
allow read, write: if request.auth != null;
}
This means that only logged in users are allowed to access the data and write data as well.
If, instead, you want users to access the data without being logged in but only write data if they're logged in, then I would suggest these rules:
service cloud.firestore {
match /databases/{database}/documents { match /{document=**} {
allow read;
allow write: if request.auth != null;
}
In terms of how easy it is to access a Firestore Database when not authorized to:
Google has done a good job at keeping things secure.
Setting up these rules and some other checks within your Application is enough to keep things secure.
I need some help making my security rules for firestore work.
These are my firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
function isAdmin(uid) {
return (uid in get(/admin/administrators).data.uid)
}
allow read;
allow write: if request.auth.uid != null && isAdmin(request.auth.uid);
}
}
}
In document /admin/administrators there is a field named uid containing an array of UIDs of administrators which are allowed to write in the database.
After I logged in as one such administrator and tried to add a document to another collection. (Specifically, the call in my Angular application using Angularfire 2 is this.afStore.collection(collection).add({});) I received the error Error: Missing or insufficient permissions.
Any help appreciated (including "there's obviously a better way to do this")
You should enter the absolute path of the collection you're trying to reference. Change the (uid in get(/admin/administrators).data.uid) to get(/databases/$(database)/documents/admin/$(request.auth.uid)).data.uid
Can I trust the data that are coming in the Firestore, or do I have to check everything and absolutely not trust the incoming data, because client can fake everything?
For example, can client fake uid, email and display name?
firebase.firestore().collection("users").doc(state.user.uid).collection("friendRequests").doc(payload.uid).set({
uid: payload.uid,
email: payload.email,
displayName: payload.displayName
});
Firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
}
match /users/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
match /users/{userId}/friendRequests/{friendId} {
allow create: if userId == request.auth.uid
&& friendId != request.auth.uid
&& !exists(/databases/$(database)/documents/users/$(request.auth.uid)/friends/$(friendId))
&& !exists(/databases/$(database)/documents/users/$(request.auth.uid)/friendRequests/$(friendId))
&& !exists(/databases/$(database)/documents/users/$(friendId)/friends/$(request.auth.uid))
&& !exists(/databases/$(database)/documents/users/$(friendId)/friendRequests/$(request.auth.uid));
}
}
}
Set of rules is already quite big and I didn't even check yet if the data structure is valid and if friend uid exists.
The request.auth in your security rules is populated by Firebase Authentication. That means that the user triggering the rules must have signed in with Firebase Authentication, and that the profile in request.auth is belonging to the user that signed in.
The only way someone can hijack that process is if they get access to the administrative credentials for your project. With that they can generate any tokens they want. But if this happen, they can already access your database without restrictions, so the additional step of forging a token is rather useless.
Of course when using a social provider (Facebook, Github, Google, etc) users can set whatever display name they want. So you typically should not depend on that being valid, beyond using it to display a name for each user.