Cloud Firestore Security Rules documentation example - firebase

This documentation page: Writing conditions for Cloud Firestore Security Rules, says the following:
Another common pattern is to make sure users can only read and write their own data
And provides this example:
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;
}
}
}
I don't understand why the create rule is not defined with the same condition as the rest, if request.auth.uid == userId, but instead is defined with if request.auth.uid != null. As I understand it, with this rule any user can create any document inside users, but cannot do anything with it unless it matches his uid. So why allow it at all?

Let's talk about the very basic security rule that could be implemented (with user authentication):
allow read, update, delete: if request.auth.uid != null;
allow create: if request.auth.uid != null;
where any user can delete the documents of another people creation. So to restrict/control it, we implement as shown in the code snippet provided.
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;
}
}
}
The code snippet is just an example use case that uses different conditions for reference purposes as this is a tutorial/guide, so the Firebase team try to fit as many possible conditions to the code snippet.
You can, of course, do allow create: if request.auth.uid == userId; to strictly restricted to that particular user.
I hope it gives you some idea!

Related

Firestore rules : User access to a collection that contain userId

I have 2 collections in my Firestore
users : uid, email
periods : periodId, name, owner_id
I need rules for users access only to it's 'users' collection and another that allow read and write to 'periods' collection only if ownerId uid is equal to authentified user id.
I do that
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write : if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
match /periods/{periodId} {
allow read, write : if request.auth.uid == request.resource.data. owner_id;
}
}
}
But it doesn't work.
:(
You don't share the queries corresponding to these security rules, but we can already identify several problems in your Security rules:
1. For /users/{userId} you have some overlapping between create and write.
The following shall solve this problem:
match /users/{userId} {
allow read, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
See the doc: "In the case where multiple allow expressions match a request, the access is allowed if any of the conditions is true".
2. For /periods/{periodId} you need to split between read and write.
For read access rights, 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 write access rights the request.resource variable contains the
future state of the document.
(source)
So the following should do the trick (untested):
match /periods/{periodId} {
allow read : if request.auth.uid == resource.data.owner_id;
allow write : if request.auth.uid == request.resource.data.owner_id;
}
I would suggest you watch the following official video on Security Rules. Actually the entire "Get to know Cloud Firestore" video series is a must...

Firebase Security Rules always false on get() or exists() operation PERMISSION_DENIED

I finished making my app and I'm implementing the security rules.
I am currently trying to validate whether or not a user exists in the bank, if he exists, he will be able to read and write to a specific path. I'm using Firestore:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents{
match /Usuarios/{documentID} {
allow read, update, delete: if request.auth.uid != null && request.auth.uid == documentID;
allow create: if request.auth.uid != null;
match /Manejo/{manejoID} {
allow write, read: if exists(/databases/$(database)/documents/$(request.auth.uid)); // The id is never exist... even when it does exist in my database, this operation simply DOES NOT WORK despite the document with the Uid existing in the database
}
}
}
Edit1:
match /Manejo/{manejoID} {
allow write, read: if true;
}
This condition not work too...
You need to declare in which collection you want to check the existence of the doc with docId == userId
With
allow write, read: if exists(/databases/$(database)/documents/$(request.auth.uid));
you don't declare the collection.
Look at the following example in the doc, which checks for the existence of the doc with docId == userId in the users collection. See how /users/ is added right after $(database)/documents.
allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid))
I understand that want to check the existence of the doc in the Usuarios collection. If this assumption is correct, do as follows:
allow write, read: if exists(/databases/$(database)/documents/Usuarios/$(request.auth.uid));
Note that you may be interested by this Firebase blog post.

How to manage Firebase database security rules for Sign In for the first time when there is no user registered yet?

This is my database Rules:
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, write: if request.auth.uid != null;
}
match /user/{userId} {
allow create, read, write, update, delete: if request.auth.uid == userId;
}
match /{document=**} {
allow create, read, write, update, delete: if request.auth.uid != null;
}
}
}
As you can see user must have to get an auth.uid to get access into the documents of my database.
match /{document=**} {
allow create, read, write, update, delete: if request.auth.uid != null;
}
But the problem is when I have no user registered on my Authentication (And in Document /user/{userId}) and somebody try to sign in, I can not get any response for the limitation of permission. How should I set my security rules where I can check if a user actually exists or not in Sign In process?
P.S: It's working perfectly if I have at least one user registered on my auth.
Security rules don't know or care if there are any users registered with your app. They just make requirements about how the data can be accessed. If your rules require a signed-in user, then that's the requirement - there's no getting around it.
If some of your data must be accessible without being signed in, then you can't have a requirement that request.auth be populated with user data.
With the suggestion of Doug Stevenson, I removed request.auth.uid from /user/{userId} and erase read from same node on the next check and it is working.
Thanks a lot.
Full answer is:
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;
}
match /user/{userId} {
allow create, write, update, delete: if request.auth.uid == userId;
}
match /{document=**} {
allow create, read, write, update, delete: if request.auth.uid != null;
}
}
}

Firestore security rules, get() and the allow create operation

I've made an app where users can post records to the firestore database. I'm now at the point where I'm implementing security rules but I'm struggling to find a solution.
My code looks like this
docRef.get().then(function(doc){
if(doc.exists){
docRef.set(//data to set here)
} else {
docRef.update(//data to update here)
}
.catch((error) => {
alert('Error' + error);
})
My rules are currently set to allow the user to create an object if they are authorised, and only allow an update if the user id on the record matches themselves.
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user}{
allow create: if request.auth.uid != null;
allow read, write: if request.auth.uid == resource.id;
}
match /equipment/{document} {
allow create: if request.auth.uid != null;
allow read, update: if request.auth.uid == resource.data.user;
}
I think the problem lies in that I'm trying to get the object prior to creating it. However I need to perform these checks prior to writing to the document.
Am I right in thinking this the problem? and if so is there a solution I can implement.
Thanks
I'm still working on my rules, but I hope this helps.
If you're trying to make sure the user is editing something they own and their uid is the same as the document id....
function isOwner(userId) {
return request.auth.uid == userId
}
match /users/{userId} { //this is the document
allow write: if isOwner(userId);
}
If you're trying to make sure they are the creator of a document:
match /equipment/{documentId} {
allow create: if request.auth.uid != null;
allow read, update: if get(/databases/$(database)/documents/equipment/documentId).data.userId == request.auth.uid;
}
https://firebase.google.com/docs/firestore/security/rules-conditions?authuser=0
You need to use the get function to retrieve a document you're interested in. That function returns data that has the related fields you can compare to, in this case, whatever field name you stored the user ID on so you can compare it to their auth.uid.
I think you should change the 'create' rule as following:
allow create: if request.auth.uid != null &&
request.auth.uid == request.resource.data.id;
This is what documentation says: "if your ruleset allows the pending write, the request.resource variable contains the future state of the document."

Firestore security rules allow user access to their data

I want to write a rule like this:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{document=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
That is, I want to allow all read and write operations to all of a user's data if that user is the authenticated user.
Unfortunately that does not work. I get an error specifying that I don't have permission to access the data.
This code solved the problem:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
match /{document=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
}
I think it's because you need to grant access to /users/{userId}, as well as /users/{userId}/{anyDoc=**}.
From the official documentation:
Another common pattern is to make sure users can only read and write
their own data:
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;
}
}
}
If your app uses Firebase Authentication, the request.auth variable
contains the authentication information for the client requesting
data.
Please note that this only works if you have made a 'users' table in your database and populated it with users that are known to your application (possibly copied from FireBase's users section Authentication/users in the webconsole).
AfaIcs you cannot refer to the Firestore authenticated users table this way. I found this lack of information very confusing since all examples and Firestore documentation make you believe that you can access the users created through the webconsole this way, invariably resulting in an 'access denied' messages when trying to read from a users table...

Resources