How to only allow certain users to read Firestore database - google-cloud-firestore

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);
} } }

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 can I make cloud firestore more secure without authentication?

I have an application that pulls data from cloud firestore. this app does not receive user login but firestore sent me an email saying it is not secure. mail of firestore security
There seems to be a security problem in authentication, but I don't want user login.
Here are my current "Cloud firestore Rules" settings:
`
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if request.auth != null && request.auth.uid == request.resource.data.author_uid
}
}
}`
How can I make this more secure?
Can I limit the number of reads as another solution?
The problem is, you're allowing not authenticated users to read your whole database. Another one is your authenticated users can create in your database what ever they want.
You need to restrict more access to your database. Someone will make his own database out of your database. He can create a document in a collection you expect should exist. This document will have nested collection with his database and everyone can read it. The best part is, if you have thousands of documents in your collection, you won't find that one document with nested collection easy.
To prevent that, restrict access to the whole database:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /articles/{articleID} {
allow read: if true;
allow write: if request.auth != null && request.auth.uid == request.resource.data.author_uid
}
}
}`

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.

multi location updates security rules in firestore

how can I ensure atomic writes of multiple document writes on client side only?
for example, when creating a record, a record document is generated and concurrently, a record log document is also generated (two different locations).
I wouldn't want the user to override by creating only the record document without the record log document. is this possible with the current firestore security rules?
Use the getAfter() function to look at what the state of a document would be after a write or set of writes. You can use this to make sure a value in another document is updated, for example:
service cloud.firestore {
match /databases/{database}/documents {
// Allow a user to update a record or log only if they keep the timestamps equal for the same ID
match /records/{record} {
allow write: if request.auth.uid != null &&
getAfter(/databases/$(database)/documents/logs/$(record)).data.timestamp == request.resource.data.timestamp;
}
match /logs/{log} {
allow write: if request.auth.uid != null &&
getAfter(/databases/$(database)/documents/records/$(log)).data.timestamp == request.resource.data.timestamp;
}
}
Docs: https://firebase.google.com/docs/reference/rules/rules.firestore.html#.getAfter