Help needed in debugging firebase rules

148 views
Skip to first unread message

Viggo Navarsete

unread,
Mar 9, 2019, 10:56:02 AM3/9/19
to Firebase Google Group
When trying this in the simulator with the user with UID marked with green in the screenshot, it works, but when I try this in my app it fails with FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

How can I debug this to narrow down what is wrong? Can I get some information from the network tab about the request that can help me pinpoint the problem? Is there an access log in firebase which can show me more details about the incoming request which was denied?

service cloud.firestore {
  match
/databases/{database}/documents {    
    match
/tenants/{tenantId} {


       
function isSignedIn() {
         
return request.auth != null;
       
}  
   
       
function isUserInTenant(rsc) {
         
return rsc.data.users[request.auth.uid] != null;
       
}
       
       
function isUserTenantAdministrator(rsc) {
         
return rsc.data.users[request.auth.uid] != null && "TENANT_ADMINISTRATOR" == rsc.data.users[request.auth.uid];
       
}
       
        allow read
: if isSignedIn() && isUserInTenant(resource);
        allow write
: if isSignedIn() && isUserInTenant(resource) && isUserTenantAdministrator(resource);
   
}
 
}
}

Screenshot from 2019-03-03 12-31-14.png


Ryan Brewster

unread,
Mar 10, 2019, 4:39:22 AM3/10/19
to Firebase Google Group
Viggo,

Try using the Firestore emulator to test this behavior. If you can reproduce the issue there, you may find the test report functionality helpful, as it will let you see how each expression is being evaluated.

~Ryan

Viggo Navarsete

unread,
Mar 10, 2019, 12:58:13 PM3/10/19
to Firebase Google Group
I've tried the emulator, using the same user as I'm logging I with, that the reason I don't understand why it doesn't work in my app. Probably there is something different, but I can't spot it 🤔

Viggo Navarsete

unread,
Mar 10, 2019, 12:58:13 PM3/10/19
to Firebase Google Group
The code I use to retrieve data is 

getOrganization(organizationNumber) {
const instance = getInstance();
var db = instance.firestore();
return db.collection("tenants").where("organizationNumber", "==", organizationNumber).get();
}

where organizationNumber is "888888888".

When I test it in the emulator I set the following:
Path to resource: /tenants/JuKWBUDm8vefJ1r0qKSQ
Provider: google.com
Firebase UID: I copy the User UID corresponding to the identifier from the user I'm logging in which I find in the Authentication view view.

Screenshot from 2019-03-10 13-43-06.png



Is this correct?

When I run it in the app, I have the following info from the request which may be interesting:
Query String Parameters:
database:
projects/*masked out*/databases/(default)


Form data:
req0___data__:
{"database":"projects/*masked out*/databases/(default)","addTarget":{"query":{"structuredQuery":{"from":[{"collectionId":"tenants"}],"where":{"fieldFilter":{"field":{"fieldPath":"organizationNumber"},"op":"EQUAL","value":{"stringValue":"888888888"}}},"orderBy":[{"field":{"fieldPath":"__name__"},"direction":"ASCENDING"}]},"parent":"projects/*masked out*/databases/(default)/documents"},"targetId":2}}

Ryan Brewster

unread,
Mar 11, 2019, 1:24:47 PM3/11/19
to Firebase Google Group
Viggo,

I believe you're currently using the Firestore rules simulator. The emulator is more like a local version of Firestore that you can run on your development machine. Documentation here.

~Ryan

Viggo Navarsete

unread,
Apr 16, 2019, 9:58:40 AM4/16/19
to Firebase Google Group
Hi Ryan,

I've made som tests using the emulator now, an all tests are green, but still it fails in my app. I wonder if I'm structuring this wrong?

In my code I do the following: 

return db.collection("tenants").where("organizationNumber", "==", organizationNumber).get();

Am I not structuring the mockData equally as in my firestore database, could that be the problem?

Or is it the code that I run that will not match any rules?

Hope to get some input before Easter so that I can get further on this :)

Thanks in advance:)

Regards
Viggo
simulator_example.png
tenants_example.png
users_example.png
firestore.rules

Ryan Brewster

unread,
Apr 17, 2019, 1:01:44 AM4/17/19
to fireba...@googlegroups.com
Viggo,

I think I see what the issue is.

In the console, when you simulate a "read", it's checking when a document lookup would be allowed. In this case, your rules definitely allow the user to look up that document, so it is all green.

However, you're trying to issue a query, and that query is not allowed by your rules. Writing security rules for Firestore queries can be tricky; our documentation here will be useful to you. The key insight is that rules are not filters. In this case you're issuing a query for all documents with a given organizationNumber, but your rules are checking for whether the user is in the document. What if one of the documents with that organizationNumber didn't have the user?

You'll need to modify your query so that the rules engine can guarantee that it's allowed. In this case, you will probably need to add an additional where clause to ensure the user is in the document:
  db.collection("tenants").where("organizationNumber", "==", 10).where("users", "array-contains", "viggo").get()
This query is definitely safe, because we're explicitly asking for documents that contain your user id.

One additional note: there is no way to use the console to simulate a query. If you want to test your queries, I would recommend looking into the local Firestore emulator, which is a tool you can download and run on your local machine. It does support queries, and will also give you very direct information about how your rules are being evaluated.

Here's a gist that tests your specific query: https://gist.github.com/ryanpbrewster/8158898d22be2ed463e27428116cbc36

~Ryan

P.S.: 

--
You received this message because you are subscribed to a topic in the Google Groups "Firebase Google Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/firebase-talk/z_j9Z2HNjb4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to firebase-tal...@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/41b959c4-8e73-4993-b40e-b19ba45c38b9%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Viggo Navarsete

unread,
Apr 17, 2019, 10:58:53 AM4/17/19
to Firebase Google Group
Hi Ryan,

and thanks for looking into this for me! I really appreciate it :)

My initial question here on this forum was how to check if a user belong to a specific tenant in a multi-tenant app. The thread is here: https://groups.google.com/forum/#!topic/firebase-talk/0POxgDoQgVA

I've tried to model it according to the input from there, and I want to make sure that a specific user belong to a tenant by querying the specific document for the tentant (can only be one per tenant), and if the user is authenticated, has a user in /users and is inside users array in the tenant document, THEN I can be sure the user belongs to the tenant, and hence the user can login to the app. If the query fails, I don't want the user to be logged in.

Am I thinking wrong about this? Please, if you have time, look at the input I got in the initial thread as well:)

Thanks A LOT in advance Ryan! :)

Regards,
Viggo
To unsubscribe from this group and all its topics, send an email to fireba...@googlegroups.com.

Ryan Brewster

unread,
Apr 21, 2019, 5:56:43 PM4/21/19
to Firebase Google Group
Viggo,

I think your approach will work. You'll just have to add the extra condition in your query.

I don't see any information about organizationNumber in your explanation. Is there some reason you can't run a query like
  db.collection("tenants").where("users", "array-contains", userId).get()

~Ryan
Reply all
Reply to author
Forward
0 new messages