User to access only his/her document in Firestore - google-cloud-firestore

I'm struggling by setting the firestore rules.
As per the screenshot, I have collection for the user with dynamic document IDs.
I'm trying to set a rule for the user to access only his/her document.
FbId is facebook id (since it is the authentication way in my app)
userId is firebase id (not sure if it is important to save it or not)
Here is my current rule:
match /users/{document=**} {
allow read, write: if normalUser();
}
function normalUser() {
return request.auth.token.firebase.sign_in_provider == "facebook.com"
&& request.auth.uid != null;
}
This rule gives access to the authenticated user for the whole collection.
How can I set this rule? If there is anything I need to change in the structure?
Update: I don't want to change the documentid for the user collection to match userid because I have another collection where the user could have multiple documents; so this solution won't fit everything.
Thanks

My get method from the app was using facebook id in the query that is why it wasn't working.
The rule I'm using right now:
match /users/{userId} {
allow update: if request.auth.uid == resource.data.userId;
}
Thanks

It will be far easier for you to use the Firebase Authentication UID of the user as the ID of the document. The Facebook UID is not going to be available in security rules.
If you use the Firebase UID, you can then write rules that look like the ones in the documentation for user-based security. Click through and read where it says "Another common pattern is to make sure users can only read and write their own data". This is probably what you want to do.
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
}
}

Related

Firestore rules difficulty

I have the following firestore setup, how can I set the rules to let only owners of marketCompra, marketTroca and marketVenda documents to delete?
The other ones I will let everybody see.
Firestore does not have a built-in concept of a "document owner". For that, your app will need to include data indicating which user "owns" the document (for example having a ownerUid field set to the Firebase Authentication UID of the user that created/owns the document) and then in your Firestore Rules you can ensure that the allow delete: condition checks the current request.auth.uid to that field's value.
service cloud.firestore {
match /databases/{database}/documents {
match /marketCompra/{docId} {
allow delete: if isOwner();
}
function isOwner() {
return request.auth.uid == request.resource.data.ownerUid;
}
}
}
For "The other ones I will let everybody see", you can simply use:
allow read: if request.auth.uid != null
meaning that as long as the user is logged in to the app, they can issue a read request against this collection (or document).
By the way, you might find this article worthy of a review: https://fireship.io/snippets/firestore-rules-recipes/

How to only allow certain users to read Firestore database

I am developing a small webapp based on a firestore database and I just want certain users to access (read and write) the data. It may be like 15 or 20 UIDs. I have created a collection -in root level- named "whitelist" and inside it each document has the UID of the user and several fields with the userID, userName and userEmail.
Then I have written my rules as follow:
service cloud.firestore { match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null && data.child('whitelist').hasChild(auth.uid);
} } }
However, I have tried with my own user to see if it works and the user is not able to read anything at all even though there is a document within the whitelist collection. What am I doing wrong?
// This will check if the user is authenticated and his UID exists
// in "haveAccess" collection which must be placed in root. You can use the same for ".write"
".read":"(auth !== null && data.child('haveAccess').hasChild(auth.uid))"
You can check the documentation for rules here : Google Firebase Database Rules
It is well worth to ensure that the statement data.child('whitelist').hasChild(auth.uid) returns true.
Also according to Data validation you should use resource.data
The resource variable refers to the requested document, and resource.data is a map of all of the fields and values stored in the document. For more information on the resource variable, see the reference documentation.
You can try adding resource:
service cloud.firestore { match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null && resource.data.child('whitelist').hasChild(auth.uid);
} } }

Firebase UID vs document-id and Firestore Rules

Hi I am getting a little bit confused here with Firebase User UID and Firestore Document ID (userId???)... and looking for some help :-)
By creating a user I get a UID and I write it to the database
let db = Firestore.firestore()
db.collection("user").addDocument(data: [
"name": "confused",
"uid": result!.uid ])
by doing so I get a unique document-id (marked green) which I thought is the userId as well:
The thing I wanted to achieve is that the user can only read and write his document (green) and not the other documents (red)
Therefore I used the following rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /user/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
}
}
So the UID and the document ID (userId???) should have a connection do they? But I don't really get it?! In my app I want to retrieve the document id of the user, to use it later on a http-trigger but I can only get the UID
print(Auth.auth().currentUser!.uid)
any ideas or do I get it completely wrong?
It's normal to use the UID of the user as the ID of their own document. Right now, you are using addDocument, which tells Firestore to assign a random ID to the document. With that, the security rule will not work as expected (because the ID assigned by Firebase Auth will never match the document ID assigned by Firestore. What you should do instead is use setDocument and specify the UID from Firebase Auth as the document ID to write.

Firestore security - Allow read if user's uid equals document's field value uid

I am trying to only allow reads to some documents if the user's uid matches the document's uid.
resource.data.uid == request.auth.uid
The security simulator gives me this errror for this line.
Error: simulator.rules line [20], column [16]. Null value error.
Is this a functionality that is just not supported by Firestore security rules?
If I understood well, I think you want something like this:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow list, create: if request.auth.uid != null;
allow get, update, delete: if request.auth.uid != null && resource.id == request.auth.token.email;
}
}
}
This code should allow users to create their own documents and access/edit only them.
Also, list allowance is required to check if the document exists before creating it, so apparently it needs to be allowed to all authenticated users.
Note that resource.id is actually the document's name. So it means that when you create a document, its name must be the user uid. And of course, you won't be able to have more than one document per collection for the same user.

Firestore rule on condition from a different collection

I have a Firestore db with two collections: users and tournaments. The users have a 'participant' role and an 'admin' role and are indicated by 'isParticipant' and 'isAdmin' booleans in the user document:
/users/{userId}:
isParticipant: true
isAdmin: true
I have access rules set up for these collections like so:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow create, read;
allow update: if request.auth.uid == userId;
}
match /tournaments/{tournamentId} {
allow create, read, update: if request.auth.uid != null;
}
}
}
However, what I really want to do is restrict the creation of a tournament to a user that is of an 'admin' role. I am already doing this in code, but would like the added security of a db rule to prevent anyone but an admin user from creating a tournament.
Is there a way to reference the data element of a different collection within the rules syntax? Something like:
match /tournaments/{tournamentId} {
allow create: if resources.users.userId.isAdmin == true;
}
?
Thanks in advance.
You can access the data in a different collection by using the get function:
// Allow the user to delete cities if their user document has the
// 'admin' field set to 'true'
allow delete: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
For more information on this, see access other documents in the Firebase documentation, which is where I got the above example from.
One thing to note is that this requires an additional document read. If you'd store the fact that a user is an admin inside their profile as a custom claim, you could access it from request.auth without needing an extra read. E.g.
allow delete: if request.auth.token.admin == true
For more on this, see control access with custom claims and accessing the user token in the documentation.