Firebase GET request blocked by simple firebase rules - javascript

I have the following collection group query:
const userInRooms = await firestore()
.collectionGroup('userRooms')
.where('uid', '==', authenticatedUser.uid)
.get();
And it works fine.
But since I added security rule:
match /rooms/{docId} {
allow read;
allow write;
match /userRooms/{docId} {
allow read;
allow write;
}
}
userRooms is subcollection in rooms.
It stopped working and I getting:
NativeFirebaseError: [firestore/permission-denied] The caller does not have permission to execute the specified operation.

Cascading the sub-collections rules that way doesn't work for collection group queries. A recursive wildcard must be present at the beginning of the path so it'll match any collections with that name. Try:
match /rooms/{docId} {
//...
}
match /{path=**}/userRooms/{docId} {
allow read, write: if true;
}
Do change the rules as required instead of allowing everyone to read the database (unless they are allowed to).

Related

Firestore rules 'Simulated read allowed' but fails in browser

My security rules are super simple. I have two collections - riders and races:
riders: can only be read from or written to when the user is signed in
races: can be read from by unauthenticated user; written to when user is signed in.
service cloud.firestore {
match /databases/{database}/documents {
// restrict read/write on all to authenticated
match /{document=**} {
allow read, write: if request.auth != null;
// then allow collection read if match
match /races/{id} {
allow read, list: if true;
}
}
}
}
These rules allow what appears to be the correct setup using the Firebase consoles Rules Playground but in the browser auth users behave as expected but the unauthed users are returned an error when making a call for a race:
core.js:6456 ERROR FirebaseError: Missing or insufficient permissions.
at new e (prebuilt-47338342-439a2133.js:188)
at prebuilt-47338342-439a2133.js:10415
at prebuilt-47338342-439a2133.js:10416
at e.onMessage (prebuilt-47338342-439a2133.js:10438)
at prebuilt-47338342-439a2133.js:10355
at prebuilt-47338342-439a2133.js:10386
at prebuilt-47338342-439a2133.js:15146
at ZoneDelegate.invoke (zone.js:372)
at Zone.run (zone.js:134)
at zone.js:1276
// service call
this.racesCollection = this.firestore.collection<Race>('races');
this.racesCollection
.doc(id)
.valueChanges()
.pipe(takeUntil(this.destroy$))
.subscribe((response: any) => {
console.log('=== APP SERVICE emits race ===', response);
this.race.next(response);
});
I've tried rewriting the rules but cannot seem to find my way around this one. Any help or ideas appreciated! Thanks.
You should write your security rules in such a way they overlap:
service cloud.firestore {
match /databases/{database}/documents {
// restrict read/write on all to authenticated
match /{document=**} {
allow read, write: if request.auth != null;
}
// then allow read for the races collection for all users
match /races/{id} {
allow read, list: if true;
}
}
}
In addition, note that list is a "sub-case" of read, so you might remove the list rule, i.e. just do allow read: if true;.
BTW, the simulator does correctly show that your rules do not allow reading a document in the races collection if you are not authenticated.

Can a user update the script in a console and access other users' data in my firebase code?

For context, I display my Firebase app's configuration in the app.js file (is this safe?) and in order to give a user their data I list the following code:
db.collection('whispers').where("sender", "array-contains",
userID).orderBy('time').onSnapshot(snapshot => {
let changes = snapshot.docChanges();
changes.forEach(change => {
renderSay(change.doc);
});
});
Could a user remove the 'where' condition and access all data in this collection? I only use anonymous sign in so nothing special required in accessing their own data besides using the same device and not clearing data.
I doubt that could be possible by reverse-engineering but the actual security lies in the security rules.
You can change them from the Firebase Console
Here is a video from Firebase and here is the documentation
service cloud.firestore {
match /databases/{database}/documents {
// Match any document in the 'users' collection
match /users/{userID} {
allow read: if request.auth.uid === userID;
allow write: if request.auth.uid === userID;
}
}
}
Using the above security rules will allow users to read and write their data ONLY.
So even if they reverse-engineer your code, it will harm their data only and other data will be safe.
For more details please check the links above and you will get familiar with wildcard [{userID} in this case]
PS: Only you or anyone with access to your Firebase Console can change the rules
EDIT: This Medium Article has many types of rules explained. Please have a read there too :D

Build private chats in Firebase and Javascript

I am building an app with Firebase that requires private messaging between users.
What I need is to build single chats for 1-1 chats, storing messages in Firestore.
My idea: I guess the best way is to build a single collection for each chat, with the right security rules. Let's say an user with tokenID 1234 wants to talk with user with tokenID 1111, a new collection called 1234_1111 will be created (if not existing), and the security will allow only these two users to read and write.
My question: Is it the right way? And how to do that in Javascript? I'm not sure how to define security rules directly in the JS code, neither how to create a collection with the two users ID.
Security Rules are not defined in your JavaScript code, they are defined separately. What you suggest would be a reasonable approach to take although I'd use a subcollection for it, and a simplified version of your security rules might look something like:
service cloud.firestore {
match /databases/{database}/documents {
match /dms/{idPair}/messages/{msgId} {
allow read, write: if
idPair.split('_')[0] == request.auth.uid ||
idPair.split('_')[1] == request.auth.uid;
}
}
}
Then in your JS code you could do something along the lines of:
// generate idPair
function dmCollection(uid) {
const idPair = [firebase.auth().currentUser.uid, toUid].join('_').sort();
return firebase.firestore().collection('dms').doc(idPair).collection('messages');
}
// send a DM
function sendDM(toUid, messageText) {
return dmCollection(toUid).add({
from: firebase.auth().currentUser.uid,
text: messageText,
sent: firebase.firestore.FieldValue.serverTimestamp(),
});
}
// retrieve DMs
function messagesWith(uid) {
return dmCollection(uid).orderBy('sent', 'desc').get();
}
Note that the idPair is constructed by joining a sorted pair of UIDs so that it will be stable no matter which user sends.

Firestore returns insufficient permissions, even tough it shouldn't

I have the following rules set up for my Firestore database:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /collections/{document=**} {
allow read;
allow write: if isAdmin();
}
match /general/{document=**} {
allow read;
allow write: if isAdmin();
}
match /inquiries/{document=**} {
allow write;
allow read: if isAdmin();
}
match /orders/{document=**} {
allow write;
allow read: if isAdmin() || resource.data.userID == request.auth.uid;
}
match /products/{document=**} {
allow read;
allow write: if isAdmin();
}
match /users/{userId} {
allow write, read: if belongsTo(userId);
}
function belongsTo(userId) {
return request.auth.uid == userId
}
function isAdmin() {
return resource.data.admin == true;
}
}
}
As you can see, everybody is allowed to read /products and its documents plus subcollections. Which works for the products, but somehow the product's subcollection (every product has one called collection-colors) can't be read.
FirebaseError: Missing or insufficient permissions.
Code causing the error:
retrieveCollectionColors(name) {
this.db.collectionGroup('collection-colors', ref => ref.where('product', '==', name))
.valueChanges().subscribe( (val: []) => {
this.collectionColors.next(val);
}, error => {
console.log(error);
});
}
The rules you have right now don't apply at all to collection group queries. You'll need to write a special rule for that. From the documentation:
Secure and query documents based on collection groups
In your security rules, you must explicitly allow collection group
queries by writing a rule for the collection group:
Make sure rules_version = '2'; is the first line of your ruleset. Collection group queries require the new recursive wildcard {name=**}
behavior of security rules version 2.
Write a rule for you collection group using match /{path=**}/[COLLECTION_ID]/{doc}.
So, if you want to allow collection group queries for "collection-colors", it will look something like this:
match /{path=**}/collection-colors/{doc} {
allow read: ...
}
This will apply to all subcollections with the given name. You can't selectively allow or disallow subcollections based on the name of the parent collection.

firestore rules matching parent data, only work in simulator

I'm trying to make a simple direct message part. I have a document with some meta informations and a subcollection with the single messages.
When I try to get the messages the rules are valid in the simulator, but in js I get everytime: Uncaught (in promise) FirebaseError: Missing or insufficient permissions.
The mongodb structure:
chatRooms
roomId
metaInformation (senderId/Name, recieverId/Name, timestamp, etc.)
messages
messageId
name
text
timestamp
The rules:
match /chatRooms/{chatRoom} {
allow create: if request.auth.uid != null;
allow update, delete: if resource.data.uid == request.auth.uid;
function isChatPartner() {
return parentDoc().receiverId == request.auth.uid || parentDoc().senderId == request.auth.uid;
}
function parentDoc() {
return get(/databases/$(database)/documents/chatRooms/$(chatroom)).data;
}
match /messages/{message} {
allow read: if isChatPartner();
}
}
The js-request:
db.collection("chatRoom").doc(_roomId).collection("messages").get().then(msg => {
console.log(msg);
})
Has anyone an idea what maybe could be wrong?
Your database and rules don't match. In the rule, the top level collection is called "chatRooms", but in your code, it's called "chatRoom". The collection names need to match exactly.
There's another problem. Your code is trying to get all of the documents in the subcollection, but the rules don't allow that. The rules are checking certain fields for access. This won't work in security rules, since rules are not filters (be sure to read and understand those docs). The query must only request documents that will definitely pass the rules - the rules will not check each document and exclude the ones that don't match.

Categories