0
service cloud.firestore {
  match /databases/{database}/documents {
    match /pool/{poolId} {
      allow read: if request.auth != null;
      allow write: if request.auth != null && get(/databases/$(database)/documents/user/$(request.auth.uid)).data.admin == true;
    }
  }
}

I wrote the previous rules by following https://firebase.google.com/docs/firestore/security/rules-conditions#access_other_documents. I expected that, for a user to add a new document into the collection pool, a document of the ID request.auth.uid should exist in the collection user and have an entry admin: true .

But every request from Functions

    pool.post('/add', async (req, res) => {
        const added = await db.collection('pool').add({
            ...
        }); 
    });

is allowed to add a new document to the collection pool.

Even the following rules

service cloud.firestore {
  match /databases/{database}/documents {
    match /pool/{poolId} {
      allow read, write: if false;
    }
  }
}

do not disallow any requests from Functions...

What's the problem of the rules? Or, is there something in Functions which makes the rules not working? Or, in my project configuration...?

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
ghchoi
  • 4,812
  • 4
  • 30
  • 53
  • 1
    Firebase functions works as "super admin". You can literally change rules in your database if you want using functions. You need to write your own logic in functions to secure your functions. – Mises Feb 13 '23 at 16:29
  • What do you exactly mean by "request from Functions"? Where is the request executed? In a **Cloud Function**? I guess that with `pool.post` you pass an Express app to your HTTP Cloud Function. – Renaud Tarnec Feb 13 '23 at 16:41
  • @Mises, the last config he showed should prevent anything from writing, but functions can still write, so there is an issue. Yes, he could check `context.auth.admin` === `true` at the top of his function and reject the request, but the question was why is the `Firestore rules` way of securing `Firestore` against unauthed function calls does not work. I think your point is valid though and something that can be done *in addition* to what he's asking as an extra security measure. --- https://firebase.google.com/docs/firestore/security/rules-conditions#access_other_documents – Wesley LeMahieu Feb 13 '23 at 16:42
  • @Mises Then the rules work for what...? Aren't the rules for requests coming into the Firestore Database from anywhere...? – ghchoi Feb 13 '23 at 16:42
  • @RenaudTarnec I am using Firebase Functions to add a document to a Firestore Database. – ghchoi Feb 13 '23 at 16:43
  • 1
    @ghchoi Rules works for calls straight to a database. For example, using Firebase JavaScript SDK library. The user browser can make calls to the database if rules allow him to. – Mises Feb 13 '23 at 16:59
  • @Mises What should I do if I want to allow only requests from specific users? Should I use the condition like `if (db.collection('user').get(request.auth.uid))`...? – ghchoi Feb 13 '23 at 17:03
  • @ghchoi There are many possibilities to do that. One way is like you have in your question. But you have to restrict access to "user" collection so only "super admin" can change documents there (Well not only, but it's just an example). Second one is to check user token: `request.auth.token.admin == true` but you will need to write a firebase function to assign this field to a user token. – Mises Feb 13 '23 at 17:09
  • Your question is very common. Please read the duplicates to understand the behavior. The bottom line is that you can't control the queries coming from backends, only from frontends when using the web and mobile SDKs. – Doug Stevenson Feb 13 '23 at 19:21

1 Answers1

3

Cloud Functions for Firebase use the Node.js Admin SDK which totally bypasses the security rules since it is considered as a "privileged environment".

You'll find a note on this aspect in the Firestore doc:

Note: The server client libraries bypass all Cloud Firestore Security Rules


If you want to restrict your HTTPS Cloud Function to only the Firebase users of your app (and identify which user is calling it through the decoded ID token) you can follow the following Firebase official Cloud Function sample: Authorized HTTPS Endpoint.

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
  • What should I do if I want to allow only requests from specific users? Should I use the condition like `if (db.collection('user').get(request.auth.uid))`...? – ghchoi Feb 13 '23 at 16:51
  • I really don't understand where to use the rules then... Can't I make my Functions not bypass the security rules? – ghchoi Feb 13 '23 at 16:54
  • 1
    See the update for restricting your HTTPS Cloud Function to only the Firebase users of your app. For "I really don't understand where to use the rules then" => Rules are enforced when the **Client SDKs** calls the Firestore serverless backend. The **Server SDKs** bypass them. – Renaud Tarnec Feb 13 '23 at 17:06
  • Can you please help me a little bit more? I followed Authorized HTTPS Endpoint. What I additionally want to do is allowing only some of authorized users to write into the collection 'pool' while all authorized users can read the collection. – ghchoi Feb 13 '23 at 17:11
  • So I may change `const { initializeApp, applicationDefault } = require('firebase-admin/app');` to use `Client SDKs`? – ghchoi Feb 13 '23 at 17:12
  • 1
    No don't use the Clients SDKs and keep using the Admin SDK. As shown at lines [61 to 63](https://github.com/firebase/functions-samples/blob/539345b1d7b3af7a7fd4fadaa54255329bdff3e3/authorized-https-endpoint/functions/index.js#L61) in the sample, the Firebase ID token that was passed as a Bearer token in the Authorization header is decoded and you can get the user uid (req.user.uid). You must write a logic in your Cloud Function that authorizes (or not) a user identified by its uid to execute a specific action (read or write). – Renaud Tarnec Feb 13 '23 at 17:52
  • 1
    Note that the following sentence in my last comment "authorizes (or not) a user identified by its uid to execute a specific action" may be confusing. Actually the action (read or write from/to the `pool`collection) is no executed by the user but **by the Cloud Function**. And in the Cloud Function, you get the caller's uid (see above comment) and decide if you can write or read to/from the collection based on this uid. in other words the Cloud Function acts as an application server or a middleware. – Renaud Tarnec Feb 13 '23 at 18:19
  • 1
    Thanks master. I learned a lot. Now I can move lots of Function's code to Flutter. I did not even know Flutter can directly CRUD Firestore... I thought I need a backend to communicate with Firestore DB. Now it seems clearer to me! My question was closed but thanks again. – ghchoi Feb 14 '23 at 03:10