3

I have doubts about the security rules at Firestore.

My scenario is this: An Android application where users will only read data. They will not save, they will not modify anything. They are not going to register. It's just data reading.

I, for my part, need to write in that collection. I plan to do it via cURL using API KEY.

The rule that I am using is the following:

  match /mycol/{document=**} {
  allow read: if request.auth.uid != null; allow  write: if true;
}

I want to prevent my collection from being accessed, for example by URL from anywhere:

https://firestore.googleapis.com/v1/projects/myproject/databases/(default)/documents/mydoc/mycol/

Avoiding for example that bots launch indiscriminate requests that would inflate my bill ...

I'm implementing the good rules for this case (only read from Android app without authentification and write via cURL using API KEY)?

A. Cedano
  • 369
  • 4
  • 25
  • You can't use security rules to limit the origin of traffic to Firestore. It could come from an Android app, or iOS, or web, or REST API. What you can do is limit access based on the authenticated user who is accessing the data, which is being discussed in answers below. – Doug Stevenson Feb 25 '19 at 17:20

2 Answers2

2

Since you're trying trying to read only and write only specific to some conditions.Try using this rules. Here inside the condition specify the api key if any:

   service cloud.firestore {
      match /databases/{database}/documents {
        match /<some_path>/ {
          allow read: if <some_condition>;
         allow write: if <some_condition>;
}
      }
    } 

To target your application only you should get your application token for that simply do this:

FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(this, instanceIdResult -> {
    String newToken = instanceIdResult.getToken();
    Log.e("newToken", newToken);
});

After running this code go to your log and then copy the token and then simply specify that token inside your condition for that try creating an auth id with your application token and then compare that auth id with your application token in your database rules

Mr. Patel
  • 1,350
  • 6
  • 20
  • 1
    Thanks. My question is precisely about the conditions. 1. What condition can be read only from the application without the need for the user to register? 2. What condition would allow me to write and access only using a API KEY? – A. Cedano Feb 25 '19 at 11:13
  • Check my updated answer and if you still didn't get it plz hit me back i'll give you the steps to do the code – Mr. Patel Feb 25 '19 at 11:30
  • I'm sorry, but I do not understand much what you tell me about the token. Is this an extra request every time to Firebase? This is not going to inflate my bill? Is not there an easier way to do it? – A. Cedano Feb 25 '19 at 11:32
  • A token is a unique identification which is generated by firebase to recognize a specific application so here when you get the generated token of your application and specify it inside your rules it will by default allow only your application to access the database – Mr. Patel Feb 25 '19 at 11:35
  • @A.Cedano this is the simplest solution for your problem if you want your application only to access the database and moreover i don't think so that there is any other solution apart from this which will be able to implement what you want – Mr. Patel Feb 25 '19 at 11:37
  • I understand best. But, the token does not expire? Are they permanent in this case? In my confusion I understood that adding Firebase correctly to my application via Android Studio should be recognized by Firebase, hence my confusion, I do not understand why I also need a token... – A. Cedano Feb 25 '19 at 11:42
  • Every time you launch an app you'll get the same token and regarding the token expiration you should use @Override public void onTokenRefresh() method to solve any expiration issues – Mr. Patel Feb 25 '19 at 11:48
  • to be more clear you need a token because that token is an identification of your application for example your application token generated is "xy23abc45" hence this token is an identification of your android application and when you specify to allow read if auth==yourapplicationtoken it will only allow your android application to access the database – Mr. Patel Feb 25 '19 at 11:53
  • I'm very confused. What is the utility of the `google-services.json` file, which is in my app and that contains the API KEY of firebase and other values such as `oauth_client`,`client_id`, etc? I thought that through this file my app would be recognized as authentic in Firebase. – A. Cedano Feb 25 '19 at 12:23
  • google-services.json is just a config file which is used to allow your application to communicate with firebase to recognize your android application you need to generate token – Mr. Patel Feb 25 '19 at 12:28
  • Visit this link for more info on google-service.json https://stackoverflow.com/questions/31597953/what-does-google-services-json-really-do – Mr. Patel Feb 25 '19 at 12:32
2

You aren't really closing your collection, on the contrary, you are allowing anyone to write to it.

To allow only yourself to write to the collection there are several options. You can use the Admin SDK and write a small program that will only run from your machine.

The Admin SDK igores security, so you can remove the write part and it will only work for you.

match /mycol/{document=**} {
  allow read: if request.auth.uid != null;
}

That's probably the easiest option and it's secure, as long as you keep your credentials safe and only do this in a trusted machine/network.

Another option would be creating a user for yourself and authenticating with that user. That can be a mobile app, web app or Node app for example. By doing that you can create a custom claim for your user, and only allow users with that claim to write, like this:

match /mycol/{document=**} {
  allow read: if request.auth.uid != null; allow write: if request.auth.token.admin;
}

Then you could set the claims to the user like this (from Node):

await admin.auth().setCustomUserClaims("user_user_id", { admin: true });

Another option yet would be creating a Cloud Function to write to that collection. Cloud Functions use the Admin SDK, so security rules also don't apply unless you want them to by using the Javascript client SDK instead. Then in the Cloud Function you could validate your API key, and you would always call the URL of the Cloud Function instead of the database directly.

I would go with the first option so you can get yourself familiarized with the Admin SDK, and then if you need you can escalate to more structured solutions.

Ricardo Smania
  • 2,111
  • 20
  • 31
  • Thanks Ricardo. The security rules are confusing to me. Leaving only this: `match /mycol/{document=**} { allow read: if request.auth.uid != null; }`, I see this message in my logcat: `com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions` I do not understand what rule to apply to allow reading from Android only. – A. Cedano Feb 25 '19 at 11:27
  • Your rules are restricted to authenticated users. One way to solve that would be creating an anonymous login on Android: https://firebase.google.com/docs/auth/android/anonymous-auth, or you could also let anyone read by setting the rule to only `allow read;` and only protect the writes. – Ricardo Smania Feb 25 '19 at 11:46
  • If I leave `allow read;` what happens in the case that a bot sends indiscriminate requests to the URLs of my collection / documents in that route? I want precisely to avoid surprises in my bill – A. Cedano Feb 25 '19 at 11:51
  • Yes, that would be a problem. Maybe in this case it would be more interesting to create a single user with a fixed password and authenticate with it. It would only be a problem if your password leaks, but there are ways to mitigate that, like for example creating a hash of the current date and using that as a password. – Ricardo Smania Feb 25 '19 at 11:56
  • 1
    Seeing at Google Console i have credentials for Android client, i can't use this for authorize my App permanently at Firestore? In fact, I thought that my app should be recognized by Firestore in an easy way, what I did not know was what rule should be applied to make it so. So, what good are those credentials that are in the console and that were created when I added Firestore to my Android app? – A. Cedano Feb 25 '19 at 12:10