Firestore rules to allow write on two separate Uids - firebase

I am writing a chat application and am done apart from the security rules section. I am currently creating two documents for each message (one each for each user) I am okay with writing a document to my user Id but the database isn't allowing for a write in the other paired user Id.
I have tried by allowing the write if the userId is in the resource.data of the other file
match /message/{user}/{chatRoomID}/{messageId} {
allow read, write: if request.auth.uid == user || request.auth.uid in resource.data;
}
How can I make it so whenever a message is sent to the database it is only read and can be written by the specific user Ids??
Each message object has reference to who sent it (each user's object Id). Thanks in advance !!

While in is indeed an operator in security rules, this won't work:
request.auth.uid in resource.data
The in operator checks if a key exists in a map, where it is much more likely that you store the UID of the other user in the value of a field.
To check whether a certain field has a specific value, use something like this:
request.auth.uid == resource.data.senderID

Related

Firestore Rules | Insufficient permission when using resource.data on getting collection

My firestore rules
match /fruits/{fruit} {
allow read: if request.auth.uid == resource.data.user;
}
Now on reading a single document, there is no problem
db.collection("fruits").doc('apple').get()
but when I try to get the collection, it gives me permission-denied
db.collection("fruits").get()
When you try to query all documents in 'fruits' collection, the security rules check if user field in those documents is equal to UID of user requesting data. If any one document doesn't match the condition, it'll throw a permission error. Instead you should add a where() clause in your query as shown below:
db.collection("fruits").where("user", ==, currentUserUID).get()
This query will find documents where user field matches user's UID and all matched docs will pass the security rule if request.auth.uid == resource.data.user;. Security rules are not filters and that means they won't filter out documents from collection based on the rule. You must write a query that way.

Only allow document to be read if ID is known [duplicate]

This question already has an answer here:
Firebase firestore only allow read if user has exact document ID
(1 answer)
Closed 1 year ago.
I have a collection named proposals, which I want to disable listing on it and allow only get if user know the ID.
Is it possible with Firebase?
My currently fails because "read" is allowed, but without this I can't read the document.
match /proposals/{uid} {
allow list, update, delete: if false;
allow create, read: if true;
}
It's pretty common to do this kind of authorization, and yes, it's possible.
Firestore Security Rules provide to us request.auth.uid which contains the UID of the user making the request or null if it's unauthenticated.
So, you could use that information with an equality operator:
match /proposals/{uid} {
allow list, update, delete: if false;
// Allow getting Documents if the Document ID is equal to the currently uid of the authenticated user who is making the request.
allow get: if request.auth.uid != null && request.auth.uid == uid;
allow create;
}
You should use get to define rules that will apply when any user is trying to get a document. Note that read is for any type of read request, which includes get and list.
More about Security Rules and Authentication: https://firebase.google.com/docs/rules/rules-and-auth

How to set Firebase security rule for read operation when user reads his/her own profile data

I am developing a Flutter app and using Firebase as backend. In my app, each user needs o sign up to be able to use the app and the user's profile data is saved in a user_profile collection.
Each user's profile data is stored in a separate document in the collection.
The Document ID for each document is equal to the User ID, created by the Firebase Authentication when the user signs up for the first time.
And I also save the User ID in a field (named uid) in each document as well for the corresponding user.
For the security part, I want that each user may only read his/her own profile data. I set the following rule:
// Rules for User Profile data
match /user_profile/{any} {
allow read: if (request.auth != null) &&
(resource.data.uid == request.auth.uid) &&
exists(/databases/$(database)/documents/users/$(request.auth.uid));
Is it correct if I set my rules as given in the above example?
(1) The user needs to be authenticated
(2) The uid in the incoming request needs to be equal to the uid field in the corresponding document that the user wants to read.
(3) The document with the uid available in the request must exist in the corresponding document
I cannot make it clear to me if I am making the whole thing unnecessarily complicated. For instance, does the rule (1) do the same thing as rule (2)? While I have rules (1) and (2), does it add anything to have rule (3) as well?
Based on the following elements in your question:
The user's profile data is saved in a user_profile collection
The document ID for each (user's profile) document is equal to the User ID
You want that each user may only read his/her own profile data
the following read security rule should do the trick:
service cloud.firestore {
match /databases/{database}/documents {
// Match any document in the 'user_profile' collection
match /user_profile/{userId} {
allow read: if request.auth != null && request.auth.uid == userId;
// ...
}
}
}
In other words, since the document ID for each (user's profile) document is equal to the userId you don't need to use the field containing the userId in the security rule: The wildcard expression {userId} makes the userId variable available in rules, see the doc.

Firestore rule on uid doesn't work for unknown reason

I have an 'owner' field in my documents to entitle only the owner to read the document, and only a new document that its 'owner' field is the uid of the user, can be written:
allow read: if request.auth.uid == resource.data.owner;
allow create, write : if request.auth.uid == request.resource.data.owner;
The creation and update rule works as expected! I tested and saw that if the 'owner' field of the record in the new data is not the user's UID then it doesn't work!
The problem is the 'read' section. I wasn't able to read records. Only when I changed to allow read: request.auth.uid != null, I was able to read.
I triple checked that the records has an 'owner' field that is exactly the same as the UID, also in debug.
I'm have experience with Firebase, and I have no idea what is the problem here.
Since you indicate that "[You weren't] able to read records" (with an S at records), your problem most probably comes from the fact that security rules are not filters, as explained in the documentation:
Once you secure your data and begin to write queries, keep in mind
that security rules are not filters. You cannot write a query for all
the documents in a collection and expect Cloud Firestore to return
only the documents that the current client has permission to access.
You don't show the query used to read the Firestore documents, but let's imagine your documents are in a collection named collection.
With a query like
query = db.collection("collection")
query.get().then(....);
you are querying independently of the User ID, hence the problem.
You need to adapt your query with the where() method, as follows:
//get the value of uid
query = db.collection("cities").where("owner", "==", uid)
query.get().then(....)

Collection Group permissions for Firestore

I have these rules:
match /suuntoAppAccessTokens/{userName} {
allow create: if request.auth.uid != null && request.auth.token.firebase.sign_in_provider != 'anonymous';
match /tokens/{userID} {
allow read, write, create, update, delete: if request.auth.uid == userID && request.auth.token.firebase.sign_in_provider != 'anonymous';
}
}
match /{path=**}/tokens/{userID} {
allow read, write, create, update, delete: if request.auth.uid == userID;
}
That means that for the path /suuntoAppAccessTokens/dimitrioskanellopoulos/tokens/{userID} the current user should have access.
However, when I query the collection group like so:
return this.afs.collectionGroup('tokens').snapshotChanges();
I get a permission error.
Getting directly the document under tokes/{userID} works as expected.
What can I do so that the current user can run a collectionGroup query and get the items he is permitted to get based on my rules?
Your rule is expecting that the security rule will filter all the documents from all of the tokens collection so that only the current user's documents will be read. This is not possible with security rules. Security rules are not filters. From the documentation:
When writing queries to retrieve documents, keep in mind that security
rules are not filters—queries are all or nothing. To save you time and
resources, Cloud Firestore evaluates a query against its potential
result set instead of the actual field values for all of your
documents. If a query could potentially return documents that the
client does not have permission to read, the entire request fails.
You will need to change your query to that the client is only requesting documents that are fully expected to be readable by the current user. Unfortunately, it's not possible for me tell if this is possible with your current schema. The ID of the document {userId} can't be used in a collection group query to filter the documents. So, both you must ensure that both of the following criteria are met:
You will need some field in the document that you can filter on to get this job done.
You will need to adjust your security rule to match exactly what the client is asking for.
I suggest storing the uid of the user in the document with the token, the same as {userId} in the rule. You can query it like this:
collectionGroup('tokens').where("uid", "==", uid)
Be sure that the client passes in the uid correctly
Also, you will need to make sure that the rule is granting access by the exact same criteria:
match /{path=**}/tokens/{userID} {
allow read, write, create, update, delete:
if request.auth.uid == resource.data.uid;
}
This will only allow access to the document if its uid field is the same as the auth uid, which is exactly what the client is asking for.

Resources