25

Firestore has a DocumentReference type, which is a "pointer" to another firestore document. Using the firebase JavaScript client, you can access properties (e.g. document "id"), directly on the reference.

For example, if there is a document with a docRef property that is a firestore DocumentReference:

const retrievedDoc = await getFirestoreDocument();
console.log(retrievedDoc.docRef.id); // "jRmSeMYDMKiOPGsmkdaZ"

I am trying to accomplish the same thing within firestore rules. There is a custom function named isOwner. It uses the firestore rules get call on a document path, and then attempts to access the docRef.id just as if it were the JavaScript client above.

get(/databases/$(database)/documents/path/to/$(id)).data.docRef.id

The value of the document's id is compared against the current user's. But when I test this using the simulator and in real code, it fails. I feel like this should work, but it doesn't.

What does work is to store and use the id value directly as a string (e.g. get(/path/id).docId) instead of a DocumentReference.

Should I be able to access the id value of a DocumentReference within the firestore rules? Am I doing something wrong?

I want to avoid doing a second document get within the rule as described in this SO answer. That's a second "read" for each trigger of this rule. And I don't think the document id (which is what I need) will be available on the get call anyway.

wtk
  • 1,431
  • 15
  • 15
  • Isn't `get(/databases/$(database)/documents/path/to/$(id)).docRef.id` just the same `$(id)`? – Frank van Puffelen Oct 11 '18 at 03:30
  • @FrankvanPuffelen No, the `docRef` property is a DocumentReference to a document elsewhere in Firestore. – wtk Oct 11 '18 at 16:46
  • @FrankvanPuffelen The intent is: "If the document reference _here_ points back to _you_, then access granted" – wtk Oct 11 '18 at 16:48
  • In that case you're missing a `.data` in there, which is needed to access the fields of a document. Something like `get(/databases/$(database)/documents/path/to/$(id)).data.docRef.id`. See https://firebase.google.com/docs/firestore/security/rules-conditions#access_other_documents – Frank van Puffelen Oct 11 '18 at 17:11
  • I'm sorry @FrankvanPuffelen, that's an error in my question (fixing now). I _do_ have the `.data` in the actual rules file. – wtk Oct 11 '18 at 17:13
  • @FrankvanPuffelen, I answered below (https://stackoverflow.com/a/53601030/5183171) based on documentation, yet I am not able to make it work, thinking of an API issue. Do you work on the API & what could be wrong ? I let you "dive into my brain", thanks – Akabab Dec 03 '18 at 20:08
  • I've never used references myself, so can't help. Maybe somebody else spots the problem. Btw: since your problem isn't solved, you should probably add the information to the question, and not post it as an answer. – Frank van Puffelen Dec 03 '18 at 20:44

2 Answers2

13

Based on documentation:

get() method is supposed to returns a Resource object which is supposed to contains a .id property (as well as .data).

For example, to restrict write access to an authenticated user which is the author of a book document (authors documents are identified with the user uid), you would do:

service cloud.firestore {
  match /databases/{database}/documents {
    match /books/{document=**} {
            allow write: if get(resource.data.authorReference).id == request.auth.uid;
    }
  }

}

Yet I'm always having the error property id is undefined on object on trying. .data is accessible so I suppose there is an issue in the api.

Akabab
  • 255
  • 2
  • 10
1

Update

Actually, a reference is a Path object in Firestore rules as documented here. So you access the id by the index of the part of the path you need.

In this example I use the incoming document's data which has a reference object to lookup a property on another document from a get()

match /databases{database}/documents {
   match /contacts/{contact} {

     allow create: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.relatedRules[request.resource.data.relation.path[4]].canBeRelated

     // the [4] assumes the path to be `databases/$(database)/documents/contacts/contactId`
     // your exact index would vary for your data structure
   }
}

First Answer

This only works in the Firestore dashboard rules simulator, it is not a working example for either the local emulation or production Firestore.

This is a year old but I encountered this same puzzling issue, but not on the data from a get(), just on the data of the request.resource.data. I'm not sure what ought to be available (not even __name__ is available) in the rules but if you're accessing a resource reference on the data and you have a predictable id size (say, 20 characters) you could simply get the range of the path on the resource to check against

match /databases{database}/documents {
   match /contacts/{contact} {

     allow create: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.relatedRules[request.resource.data.relation.path[9:29]].canBeRelated

     // the [9:29] assumes the path to be `/contacts/20characterLongIdStr`
     // your exact range would vary for your data structure
   }
}

Feels like a resource reference object should have at least the id since the path is there. It appears Firestore won't support this for whatever reason.

Charles Harris
  • 304
  • 1
  • 2
  • 18