Firestore rules not allowing read given valid UID - javascript

I have written the following firestore rule to limit reads on the users collection by only allowing the request if the caller's UID matches the document ID. There is parity between the users collections' document ID and the UID in Firebase Authentication.
function isAuthenticated() {
return request.auth.uid != null;
}
// Users collection
match /users/{userId} {
allow read: if isAuthenticated()
&& request.auth.uid == userId;
}
I have a document in the collection with ID Eli90wRvWkfKcOfn1C4DBDqxQTz1. When hitting firestore with the same authentication user, I get the following error:
If I remove the check for the request.auth.uid == userId, it results in this rule and a successful read:
// Users collection
match /users/{userId} {
allow read: if isAuthenticated();
}
The query that is being called is:
export const getUser = (uid: string) => {
return db.collection('users').where('id', '==', uid).limit(1).get();
};
I've seen many people use the UID check in their rules but why isn't it working in my case?

Your security rules match this code:
db.collection('users').doc(uid).get();
So here we access a single specific documents, with the document ID being the UID of the user.
If you instead want to query for the UID in a field in the document(s), you can secure that with these rules:
match /users/{doc} {
allow read: if resource.data.id === request.auth.uid;
}
So now the rules match your original code, and only allow reading documents where the value of the uid field matches the uid of the current user.

Related

Firestore security rules - trouble with permission to write to a collection

I've attempted to set up a security rule where a person writing a document to a particular collection may only do so if their uid matches the uid contained in the document, but can't get it to work. In this case I am writing a document to a collection called 'embedUsers' and the document being written contains a uid field that was acquired when the user account was created.
The rules are set up as follows:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /embedUsers/{documents=**} {
allow read: if true;
allow write: if resource.data.uid == request.auth.uid;
}
}
}
The document is written by an Angular client, using the following code:
const userRef: AngularFirestoreDocument<UserModel> = this.afs.doc(`embedUsers/${user.accountAddress}`);
console.log("current logged in user", this.fireUser.uid);
try {
console.log(user.uid);
await userRef.set(user, { merge: true });
} catch (e) {
console.error(`FireAuthService.updateUserData unexpected failure with error ${e.message}`)
throw new Error("updateUserData failed");
}
console.log("wrote embed user successfully")
Although I have checked that the uid of the signed in user matches the uid field in the data object being written, this call to userRef.set fails with FireAuthService.updateUserData unexpected failure with error Missing or insufficient permissions. It seems like something must be wrong with my rules but I'm not sure what (if I change the rule to simply say allow write: if true; the document gets written as expected.
As explained in the doc, "When writing data ... the request.resource variable contains the future state of the document". So you should adapt your rule to:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /embedUsers/{documents=**} {
allow read: if true;
allow write: if request.resource.data.uid == request.auth.uid;
}
}
}

How to access my data field in Firebase Security Rules

Security Rules
I am trying to grant permission based at a document field:
match /users/{user}/{documents=**} {
allow read, write: if resource.data.uid == request.auth.uid
}
Firebase query
Here is how my query looks:
query(collection(db, "users", match.params.uid, "promotors"));
Error message
But I keep geting this message:
FirebaseError: Missing or insufficient permissions.
Your query is not in any way checking the data in a field in the documents, so it will never meet this part of your rules: resource.data.uid.
Instead what you seem to have is a case where the document ID matches the UID of the user, which you can check with:
match /users/{user}/{documents=**} {
allow read, write: if user == request.auth.uid
}
Also see the documentation on content owner only access.

Firestore Rule That Checks Single Document Not Authenticating?

I'm having what I think maybe a simple issue with Firebase Rules, but I can't seem to get this to work. I have a document 'Companies', with multiple subcollections inside. I want to set up a rule that checks for admins in an array (with each array item being a string of the firebase userId) inside a 'company' document and allows them to read/write all subcollections of that document.
Here's the data structure in a single company document:
company1 {
admins: ["userid1", "userid2", "userid3"],
}
Here's my firebase rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /companies/{company}/{document=**} {
allow read, write: if request.auth.uid in get(/databases/$(database)/documents/companies/$(company)).data.admins
}
}
}
Here's an example of a query that's not working when it should:
let ref = db.collection("companies");
//get current users company
ref
.where("admins", "array-contains", firebase.auth().currentUser.uid)
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
this.company = doc.data();
this.company.id = doc.id;
});
});
I hope my question makes sense :)
I found an answer, I hope it helps anyone who might come upon this.
I ended up adjusting the data structure to include the company document ID as a field in the user doc. Then, I created these rules to allow users to read/write their own user doc based on firebase authentication, as well as read/write their companies based on a field in the user doc:
service cloud.firestore {
match /databases/{database}/documents {
// Allow users to create and edit the document for themselves in the users collection
match /users/{user} {
allow read, write: if request.auth.uid == user;
}
// Allow users to create a company for themselves upon signup
match /companies/{company} {
allow create: if request.auth.uid != null
}
match /companies/{company}/{document=**} {
// Allow users to read/write data on thier own companies
allow read, write: if company in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.companies
}
}
}

Keep getting missing permission when trying to add to user only

I am trying to allow read/write to only a users UID, however script keeps saying missing permission please help.
Many thanks`
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid == userId;
}
}
}
db.collection('users').doc(userId).collection('details').add(...
The rules you've written only match documents that are immediately within the collection called "users". However, you're trying to access a document within a subcollection of a document of under "users".
The documentation addresses this situation very specifically, so be sure to read and understand how hierarchical data works for security rules.
If you want to apply per-user rules to all documents in a collection, and all the documents in all nested subcollections under that document, you can simply say this:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth.uid == userId;
match /{document=**} {
allow read: if request.auth.uid == userId;
}
}
}
}
Note that the match has to be nested like this in order it to use the userId wildcard from the outer match.
Probably your forgot to enable sign-in provider in the firebase console.

Firestore security rules allow one document to be public

I'm working on an Angular app using Firestore here I have a bookings functionality. I want users to publicly read and write to bookings if there provided reference_no exists within one of the documents.
Here is how my document is structured:
These are my current security rules:
service cloud.firestore {
match /databases/{database}/documents {
match /jobs/{job} {
allow read, write: if request.resource.data.property.reference_no == resource.data.property.reference_no;
}
match /{document=**} {
allow read, write: if isSignedInUser();
}
//functions
function isSignedInUser(){
return request.auth.uid != null;
}
}
}
This is how I'm querying this:
findByReferenceNo(reference_no): Observable < any > {
return this.afs
.collection(this.jobsCollection, ref => ref.where('property.reference_no', '==', reference_no))
.snapshotChanges()
.pipe(
map((actions) => {
return actions.map((snapshot) => {
const data = snapshot.payload.doc.data();
const id = snapshot.payload.doc.id;
return {
id,
...data
};
});
})
);
}
But not sure why I'm getting: Error: Missing or insufficient permissions.
Note: I'm not signed in while accessing this.
As far as I know, in order to guarantee query performance, Firestore security rules validate your query and not each individual document. This means that a query is only allowed if the rules can validate that the query won't retrieve more data than is allowed.
In your current model that is simply not possible, because it would require that the security rules check each individual document. For some examples of queries that can be secured, have a look at the Firestore documentation on securing queries.

Categories