Setup Firestore User Security Rule - firebase

I am new to Firestore and I'm trying to setup simple security rules so that only someone who is signed in can create a new database entry and that users can only read and write their own entries.
service cloud.firestore {
match /databases/{database}/documents {
match /Users/{userID} {
// Can only create a new entry if signed in with a uid.
allow create: if request.auth.uid != null;
// Can only update an entry if signed in with uid and changing own information (saved under uid)
allow update: if request.auth.uid != null &&
userID == request.auth.uid;
// Can only read (get/list) an entry if signed in with uid and reading own information (saved under uid)
allow read: if request.auth.uid != null &&
resource.data.userID == request.auth.uid;
}
}
}
The create new entry case works fine, but I wonder if this is secure enough.
For updating and reading, I also want to check that the user is updating/reading their own entry. The document name is the uid (in other words UserID) from Firebase so simply checking that request.auth.uid is the same should do the trick, but something is off in the way I'm writing it. The call gets blocked and when I run it in the simulator I get the error: Missing or insufficient permissions. I can't figure out after reviewing the documentation and this tutorial video.

Something like below should be sufficient for your situation:
// True if the user is signed in or the requested data is 'public'
function signedInOrPublic() {
return request.auth.uid != null || resource.data.visibility == 'public';
}
// 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 signedInOrPublic();
}

Related

How to write security rules for users setting documents that don't exist?

When a user registers, a document should be set in Firestore (database/users/${uid}). However, I keep getting a "Missing or insufficient permissions." error.
This is the most relevant security rule
match /users/{documents=**} {
allow read, create, update: if request.auth != null && request.auth.uid == resource.id
}
Another rules I tried implementing was
match /users/{uid=**} {
allow read, create, update: if request.auth != null && request.auth.uid == uid
}
and this is the code that registers the user and sets the document
createUserWithEmailAndPassword(auth, emailInput, passwordInput).then(
(UserCredential) => {
console.log(UserCredential);
uid = UserCredential.user.uid;
sendEmailVerification(auth.currentUser).then(() => {
toast.success(
"Account created and email verification sent! Please check your inbox/spam folder",
{
duration: 10000,
}
);
setDoc(doc(db, "users", uid), {
userSettings: ["example", 0],
});
router.push("/verify");
});
}
);
Why can't I set a document as an authorized user accessing my own user document?
The problem is request.auth.uid == resource.id. From the documentation,
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.
But the document does not exists as user has just registered to your application.
Try the following rules instead:
match /users/{userId} {
allow read, create, update: if request.auth != null && request.auth.uid == userId;
}
This rule will ensure that a user can create/read/update a document with their user ID only.
Also do note that the match path is /users/{userId} and not match /users/{userId=**} as in your question. The value of userId would be /userID and not just userID if you use the recursive wilcard (=**) and rule will fail always.
If the rule must be applied for all nested collections, then use the recursive wildcard on the next path segment:
match /users/{userId}/{path=**} {
// ... can still read userId
}

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.

Cloud Firestore - still get "any user can write to your entire database" error

I've added rules to my Cloud Firestore database but still receive these issue messages:
any user can read your entire database
any user can write to your entire database
See the rules below.
Indeed, any user can read and write into "users" collection, otherwise I won't be able to log in/register a user. How to solve the issue?
Thanks
The problem with your current rules is that it allows any user to read/write any other user's user document. You need to restrict it a bit more to look at the userId of the documents. Something like this
service cloud.firestore {
match /databases/{database}/documents {
// Ensure the user is authenticated
function userIsAuthenticated() {
return request.auth != null && request.auth.uid != null
}
// Ensure the user owns the existing resource
function userOwnsResource() {
return userIsAuthenticated() && request.auth.uid == resource.data.userId
}
// Ensure the user owns the new resource
function userOwnsNewResource() {
return userIsAuthenticated() && request.auth.uid == request.resource.data.userId
}
match /users/{userId=**} {
allow read, update, delete: if userOwnsResource();
allow create: if userOwnsNewResource();
}
}
}
userOwnsResource checks that the user can access an existing resource. userOwnsNewResource checks that they can create a new document for their userId. You can obviously reuse these functions in other rules which is handy.

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."

Resources