1
votes

I'd like to query a Firestore collection based on a string field, returning all documents where that field starts with a custom claim on the user's auth token. I was able to use this answer to create a query using >= and < operators with a successor key, and this works well for me when my I relax my Firestore rules.

I'd like to lock down my ruleset so that a user only has access to documents that start with their custom claim.

I've read the rules are not filters literature, and from my understanding of it, I just need to write rules such that my query can never return data that would violate the rules.

So that's what I'm attempting to do without much success:

I have a Firestore collection with documents that look something like this:

{
   "id": 1,
   "namespace": "foo.bar"
}

Each user has a custom claim on their auth token, let's say it's my_namespace.

I wrote a Firestore rule like so:

function hasNamespaceAccess(request, resource){
        //allow if data.namespace starts with request.token.my_namespace
        return resource.data.namespace.matches(request.auth.token.my_namespace + ".*");
    }


      match /path_to_my_objects/my_collection/{my_obj} {
        allow read, write: if hasNamespaceAccess(request, resource);
    }

My query, after simplifying to make this post as concise as I can, looks like this:

return db
        .collection('my_collection')
        .where('namespace', '>=', 'foo.') //the namespace "query values" are hard coded for clarity here
        .where('namespace', '<', 'foo.c')

The token which is used when making a call to Firestore does, for sure, have "my_namespace": "foo"

What I Expect

My Firestore rules says that a user has access to any document where namespace starts with "foo" -- the doc with "namespace": "foo.bar" conforms to this.

My query should only return documents where namespace is between "foo." and "foo.c". Again, my document conforms to this. This query, I believe, can never return a document that does not conform to the regex string in my Firestore rule.

As such, I'd expect to get a result set with my document.

What actually happens

index.cjs.js:13448 Uncaught Error in snapshot listener: FirebaseError: Missing or insufficient permissions. at new n (index.cjs.js:129) at index.cjs.js:10175 at index.cjs.js:10176 at n.onMessage (index.cjs.js:10209) at index.cjs.js:10115 at index.cjs.js:10146 at index.cjs.js:5542

I've tried modifying my query to not use a range, and to only have where("namespace", "==" "foo.bar"), and this works as expected, so it seems like the rest of the system is working fine, but there is a mismatch between the rules and the filter clause.

1

1 Answers

1
votes

The problem here is not that your code is trying to access data that the rules don't allow, but that the rules engine isn't smart enough to be able to prove that for all cases of a match without having to check the actual data.

An interesting alternative would be to perform the same check with >= and <= operators. I didn't have a chance to try that though, so let me know if that works (or doesn't work) for you.