I've been able to set permissions to my Firestore database, the logic behind the rule is restrict users to be authenticated and belong to a specific domain.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if isUserAndSignedIn();
}
}
function isUserAndSignedIn(){
return request.auth != null && request.auth.token.email.matches('.*#domain[.]com')
}
}
The rule works fine for CRUD operations as expected but it doesn't work for triggers, in my case I'm getting the following error when the trigger is executed (cloud function):
FirebaseError: Missing or insufficient permissions.
at new FirestoreError (/srv/node_modules/#firebase/firestore/dist/index.node.cjs.js:348:28)
at JsonProtoSerializer.fromRpcStatus (/srv/node_modules/#firebase/firestore/dist/index.node.cjs.js:5385:16)
at JsonProtoSerializer.fromWatchChange (/srv/node_modules/#firebase/firestore/dist/index.node.cjs.js:5883:44)
at PersistentListenStream.onMessage (/srv/node_modules/#firebase/firestore/dist/index.node.cjs.js:14779:43)
at /srv/node_modules/#firebase/firestore/dist/index.node.cjs.js:14708:30
at /srv/node_modules/#firebase/firestore/dist/index.node.cjs.js:14748:28
at /srv/node_modules/#firebase/firestore/dist/index.node.cjs.js:10612:20
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
Any Idea how can I solve this in the rule?
how can I bypass a trigger in the rules execution for the database?
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 12 months ago.
Improve this question
I am using firestore and firebase auth and I am trying to add custom usernames to my app (I am following this courses, Next.js Firebase Full Course) that connect to the freebase emulator because it is still in development. I have this piece of code that runs in a useEffect that gets the current user uid and then fetch a document from firestore, then checks if it exits and if i does set the username state to the username in the do. Here is the code:
useEffect(() => {
let unsubscribe;
if (user) {
console.log(user.uid);
const ref = doc(db, "users", user.uid);
unsubscribe = onSnapshot(ref, (doc) => {
if (doc.exists()) {
setUsername(doc.data()?.username);
} else {
console.log("No such document!");
}
});
} else {
setUsername(null);
}
return unsubscribe;
}, [user]);
But I always get the error "next-dev.js?3515:32 Uncaught Error in snapshot listener: FirebaseError: false for 'get' # L5". I have tried changing the on snapshot to a get doc but that also gets the same error. I tried making it so it didn't connect to the emulator that fixed it. I'm not sure why but I like to use the firebase emulator. Is there anyway to fix this
Thanks in advance!
The error message “FirebaseError : false for ‘get’ #L5” is saying that security rules are denying the request at line 5 of your security rules. If you have curated your rules like below,
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
then line 5 has the error as you are not allowing read and write access to anyone.
To fix that you can either change your firestore rules to :
match /{document=**} { allow read: if true; allow write: if true; }
but this allows setting your rules to true to both read and write operations, which means that you allow anybody who knows your project ID to read from, and write to your database which is obviously bad, since malicious users can take advantage of it. It’s true that you can use these settings for a small amount of time for testing purposes, but never in a production environment.
So there is another option in which you can limit access to your database to a fixed amount of time. Today is 1.03.2021, we are limiting the access for exactly one day by:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.time < timestamp.date(2021, 3, 2);
}
}
}
The most important part when it comes to security rules is the Firebase Authentication, meaning that you can allow access only to the users that are authenticated to perform operations in your database. The rules should look like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
If you need a more granular set of rules, for instance, to allow only the authenticated users, who have the value of the UID equal to the value of UID that comes from to authentication process, to be able to write to their own document, then you should consider using the following rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{uid} {
allow create: if request.auth != null;
allow read, update, delete: if request.auth != null && request.auth.uid == uid;
}
}
}
A feature request is filed for this error message to be more precise like “FirebaseError: PERMISSION_DENIED because security rules returned false for 'get' # L5”
Im trying to figure out how the rules in Firestore work, and have stumbled upon a problem I cant quite figure why is failing.
When using the Firestore Rules Playground and do a get request for a document in /apps, I get the following error: "Error running simulation — Error: simulator.rules line [10], column [14]. Null value error."
And from the app im getting: FirebaseError: Missing or insufficient permissions
The location in trying to get: "/apps/0aKQw0MiRzEbrLDvsnI3"
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if isUserAuthenticated();
}
/* Functions */
function isUserAuthenticated() {
return request.auth.uid != null;
}
}
}
The thing im trying achieve is to allow read access for all document, and only write access if a user is signed in
I have no idea why it's failing on a get request?
If I'm not mistaken the way you added the read condition seems to be incorrect. Perhaps try this and see if it works for you.
match /databases/{database}/documents { match /{document=**} { allow read: if true, write: if request.auth != null && request.auth.uid!=null }
The following rules produce an error on "request" in the nested (**) match:
Error running simulation — Error: simulator.rules line [10], column [30]. Null value error.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Allow users to only edit their records
match /users/{userId}{
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
match /{documents=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
}
The simulator test that is failing is:
GET: /users/MyUserId/items/MyItemId
This is using "password" authentication, but even running this as "unauthenticated" produces the same error, as if the rule is not being compiled correctly by the simulator.
Further troubleshooting shows it works live in the database, just not in the simulator. So must be a simulator bug.
Question:
For the different top-level firestore collections below, how to restrict access to all but one of the paths?
We are building a data schema in Firestore to support a chat app for teachers across multiple schools.
The top-level firestore collections include:
/siteAdminUsers
/schools
/schools/{schoolId}/teachers
/schools/{schoolId}/chats
Below is the security rules setup we are trying now - where we check for:
valid user auth
expected value exists in userClaim variable request.auth.token.chatFlatList
However, the read listener for /messages is being blocked.
Error message:
FirebaseError: [code=permission-denied]: Missing or insufficient permissions
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /schools/{schoolId}/chats/{discussionId}/messages {
allow write: if false;
allow read: if request.auth != null
&& request.auth.token != null
&& request.auth.token.chatFlatList.val().contains($discussionId);
}
}
Details
We are using cloud functions for all data read/write, so for almost every case we can just block all client access.
The one exception is for the chat discussions, where we need to set a snapshot listener in the mobile client to know when there are new messages.
Sub-collection notes:
At a school, there are discussion sessions for school staff (teachers, admins, etc)
/schools/{schoolId}/chats/{discussionId}
Where each discussion-document contains:
list of participant teacher ids
subcollection for actual messages where each document is an indivual posted message:
/schools/{schoolId}/chats/{discussionId}/messages
User Claim code from Cloud Function
Looking at the cloud function logs, we have verified that the userClaim is being set.
return firebaseAdmin
.auth()
.setCustomUserClaims(
uid, {
chatFlatList: 'id1 id2 id3'
}
);
UPDATE #1
Tried the following variation where rules skip/omit the check on userClaim and auth.token.
However, still same permission error.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /schools/{schoolId}/chats/{discussionId}/messages {
allow write: if false;
allow read: if request.auth != null;
}
}
}
I think the issue here is that you are writing a rule on the collection called messages.
All match statements should point to documents, not collections.
https://firebase.google.com/docs/firestore/security/rules-structure
You should try adding /{document=**} after your path to messages, something like:
match /schools/{schoolId}/chats/{discussionId}/messages/{document=**} {
allow write: if false;
allow read: if request.auth != null;
}
This worked for me if I wanted to read and write all collection but not one collection named "backStage";
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{collection}/{document} {
allow read: if true
allow write: if (collection != "backStage");
}
}
}
Here's a solution (seems to be working), which includes the check on chatFlatList user claim variable (from original question) for a substring :
match /schools/{schoolId}/chats/{discussionId}/messages {
allow write: if false;
allow read: if request.auth != null
&& request.auth.token.chatFlatList.matches(discussionId);
}
Figured this out thanks to:
Firebase storage rules based on custom parameters
Here the post shows there is not any $ notation to access the path var. I recall seeing this in a security rules example code example - maybe it's specific to database tiers?
https://firebase.google.com/docs/reference/security/storage/#string
https://regex-golang.appspot.com/assets/html/index.html
Trying some example inputs here, to get an understanding for how to create the regex's.
I have tasks collection on root level and here is my rules (yeah, super global for now):
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if resource.data.private == false;
allow write: if request.auth.uid != null;
}
}
}
Single task has field private: Boolean and I want to give read permission to all tasks that has private: false and writing is allowed only for logged in users.
firestore.collection('/tasks').onSnapshot(snapshot => {})
At least with onSnapshot this read rule doesn't work. I give an error:
Uncaught Error in onSnapshot: Error: Missing or insufficient permissions
So am I doing something wrong or what is happening here?
Apparently "you can't query data you have no access to". Because only private: false tasks are readable the query should be changed from this:
firestore.collection('/tasks').onSnapshot(snapshot => {})
to following:
firestore.collection('/tasks').where('private', '==', false).onSnapshot(snapshot => {})
after requesting only data I have access to, it started working!