I'm having trouble using firebase [duplicate] - firebase

I've created a new project on Firebase, and created a Realtime Database in there. When asked about the security rules for my database, I selected to Start in test mode.
Now the security rules of my database in the Firebase console show up as:
{
"rules": {
".read": "now < 1622790000000", // 2021-6-4
".write": "now < 1622790000000", // 2021-6-4
}
}
What do these rules mean? And how can I change them to be more secure?
It's been a month since I created my Firebase Realtime Database, and I now got a message:
Your project's Realtime Database '' will start denying client requests unless you update your security rules

These default test mode rules are a simple catch-all that allows everyone in the world to read from and write to your database until a given date.
Let's break the rules down to see exactly how they work:
The ".read" and ".write" nodes immediately under "rules" determine who can read/write the data in the entire database.
The now variable is automatically set by Firebase to be the current time on the server. This value is in milliseconds since the epoch, which is the recommended value to also store timestamps in Firebase.
The 1622790000000 value in the rules is the timestamp of some point in the future. Let's see what this value is in a more readable date format:
console.log(new Date(1622790000000))
"2021-06-04T07:00:00.000Z"
So anyone can read of write all data in our database until June 4th, 2021. After that date nobody can access the data anymore with the client-side SDKs. The Firebase Admin SDKs bypass these rules altogether, so they are not affected.
Can I extend the time period?
You may have gotten a message like this from Firebase:
You chose to start developing in Test Mode, which leaves your Realtime Database instance completely open to the Internet. Because this choice makes your app vulnerable to attackers, your database security rules were configured to stop allowing requests after the first 30 days. In 5 day(s), all client requests to your Realtime Database instance will be denied.
This message means that access to your data is about to expire, due to timestamp that is in your security rules.
It's actually pretty easy to extend the test mode to another deadline. All you need to do is change that 1622790000000 value. For example, for extend it to July 4th, I can set the value to 1625382000000.
To determine the value to use, I run this tiny JavaScript snippet:
console.log(new Date("2021-07-04T07:00:00.000Z").getTime())
Run this snippet to get the timestamp exactly one month from now:
console.log(new Date(Date.now()+30*24*60*60*1000).getTime())
Here's another tool to calculate these values.
By using 1625382000000 we've extended test mode for a month and everyone can read/write the entire database until July 4, 2021.
How can I better protect the data?
At some point you should come up with a better way to protect your (user's) data than just opening it until a specific date. I typically do this right when I start a project, but it's also fine if you start it a bit later.
The important thing is that you should treat the server-side security rules the same as the client-side source code of your app.
I develop my code and rules in tandem. So:
I start with a fully closed off database, since there is no code yet that needs access to any data.
I add some data manually to the database, and write code to read it. At this point, I write security rules that only allow read-access to that specific data. So it may be ".read": true, but it'll be much deeper in my JSON structure. Even such simple rules will already block many bad actors.
The first time I want the app to write to the database is also when I add authentication. Typically I start with anonymous auth, since it does not require me to enter any credentials.
I then include the hard-coded UID in my security rules, to ensure only I can write data. You'll often still find this top-level ".write": "auth.uid === 'hardcodedUidOfPufsAnonymousUser'" in my rules much later, after I added proper data ownership.
When using Firestore I sometimes evolve that as explained here: User conflict when using same Auth method for Admin and Normal users | Firebase Auth
At any point when I add (typically lists of) data, I think through who "owns" this data, and who can read it. I then expand my rules to allow exactly that access, and nothing more.
This need to update my security rules as I write code slows down the pace at which I code, but I'll gladly do it anyway. Keeping the data in my database secure at every step, allows me to give people access to the app/database with confidence. I recommend you do the same.
For more information, I recommend reading:
The Firebase documentation on security rules, which contains examples of these common use-cases:
Content-owner only access
Public read, private write access
Attribute and role based access
All authenticated users can read/write all datsa

Related

Does Firebase Realtime Database guarantees FCFS order when serving requests?

This is rather just a straight forward question.
Does Firebase Realtime Database guarantees to follow 'First come first serve' rule when handling requests?
But when there is a write-request, and then instantaneously followed by a read-request, is the read-request will fetch updated data?
When there is a write-read-write sequence of requests, does for sure read-request fetch the data written by first write?
Suppose there is a write-request, which was unable to perform (due to some connection issues). As firebase can work even in offline, that change will be saved locally. Now from somewhere else another write-request was made and it completed successfully. After this, if the first device comes online, does it modify the values(since it arrived latest)? or not(since it was initiated prior to the latest changes)?
There are a lot of questions in your post, and many of them depend on how you implement the functionality. So it's not nearly as straightforward as you may think.
The best I can do is explain a bit of how the database works in the scenarios you mention. If you run into more questions from there, I recommend implementing the use-case and posting back with an MCVE for each specific question.
Writes from a single client are handled in the order in which that client makes them.
But writes from different clients are handled with a last-write-wins logic. If your use-case requires something else, include a client-side timestamp in the write and use security rules to reject writes that are older than the current state.
Firebase synchronizes state to the listeners, and not necessarily all (write) events that led to this state. So it is possible (and fairly common) for listeners to not get all state changes that happened, for example if multiple changes to the same state happened while they were offline.
A read of data on a client that this client itself has changed, will always see the state including its own changes.

Only download changed/updated documents from Firestore

being new to firestore, I am trying to keep the number of downloads of documents as small as possible. I figured that I could download documents only once and store them offline. If something is changed in the cloud, download a new copy and replace the offline version of that document (give relevant documents a timestamp of last-change and download when the local version is older). I haven't started with this yet but I am going to assume something like this must already exist, right?
I'm not sure where to start and google isn't giving me many answers with the exception of enablePersistence() from the FirebaseFirestore instance. I have a feeling that this is not the thing I am looking for since it would be weird to artificially turn the network on and off for every time I want to check for changes.
Am I missing something or am I about to discover an optimisation solution to this problem?
What you're describing isn't something that's built in to Firestore. You're going to have to design and build it using Firestore capabilities that you read in the documentation. Persistence is enabled by default, but that's not going to solve your problem, which is rather broadly stated here.
The bottom line is that neither Firestore nor its local cache have an understanding of optimizing the download of only documents that have changed, by whatever definition of "change" that you choose. When you query Firestore, it will, by default, always go to the server and download the full set of documents that match your query. If you want to query only the local cache, you can do that as well, but it will not consult the server or be aware of any changes. These capabilities are not sufficient for the optimizations you describe.
If you want to get only document changes since the last time you queried, you're going to have to design your data so that you can make such a query, perhaps by adding a timestamp field to each document, and use that to query for documents that have changed since the last time you made a query. You might also need to manage your own local cache, since the SDK's local cache might not be flexible enough for what you want.
I recommend that you read this blog post that describes in more detail how the local cache actually works, and how you might take advantage of it (or not).

Firebase Realtime Database Cache behaviour on Flutter

I'm trying to understand how Firebase Realtime Database uses cache. The documentation doesn't clarify some cases about cache handling. Especially for Flutter, there is no documentation and online sources are not enough. There are two different scenarios that I'm confused.
First of all, I start with setting the cache for both scenarios:
await FirebaseDatabase.instance.setPersistenceEnabled(true);
await FirebaseDatabase.instance.setPersistenceCacheSizeBytes(10000000);
Scenario 1: I listen to the value of a specific user. I want to donwload user data for once. Then, always use cache and download only updates if there is any:
final stream = FirebaseDatabase().reference().child("users").child("some_id").onValue();
It's my understanding that Firebase will download the node first and use the cache later if there is no update. This won't change even if the app restarts.
Scenario 2: I want to query the posts that are created only after the date:
final date = DateTime(2020,6,20);
final data = await FirebaseDatabase().reference().child("posts").orderByChild("createdAt").startAt(date).once();
Here for Scenario 2, I'm not sure how cache will be done. If Firebase Realtime Database caches the query, will it download everything when a new post created after the date? Or it will download only the new post and get others from the cache?
If there is a change to a location/query that you have a listener on, Firebase performs a so-called delta-sync on that data. In this delta-sync, the client calculates hashes on subtrees of its internal version of the data, and sends those to the server. The server compares those hashes with those of its own subtrees and only sends back the subtrees where the hashes are different. This is usually quite a bit smaller than the full data, but not necessarily the minimal delta.
Note that Firebase will always perform a delta sync between the data it has in memory already for the query/location and the data on the server, regardless of whether you enable disk persistence. Having disk persistence enabled just means the in-memory copy will initially be populated from disk, but after that the delta-sync works the same for both cases.

Firebase security rules difference between get() and getAfter()

In the doc, it says :
Using the get() and exists() functions, your security rules can
evaluate incoming requests against other documents in the database.
Thats all right to me, and the example makes sense to me:
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city} {
// Make sure a 'users' document exists for the requesting user before allowing any writes to the 'cities' collection
allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid))
// Allow the user to delete cities if their user document has the
// 'admin' field set to 'true'
allow delete: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
}
}
}
but an then it says
For writes, you can use the getAfter() function to access the state of
a document after a transaction or batch of writes completes but before
the transaction or batch commits.
I might still don't fully understand the concept. My questions are:
Why is it specifically have to use getAfter() for the transaction or batch write, Can we just use get()?
If you have to use getAfter() for transaction or batch write, does that mean you still need get() for normal write? how do they exist at the same time?
Thanks.
First, bear in mind that that security rules for writes kick in before anything in the database has been changed by that write. That's how the security rules are able to safely and efficiently reject access, without having to roll back any writes that already happened.
The documentation you're citing suggests that getAfter is useful to examine the contents of the database after the entire transaction's state would be recorded (in a sort of "staging" environment in memory), but before the transaction actually changes the database, visible to everyone. This is different than get, because get only looks at the actual contents of the database, before the transaction is finally committed. In short, getAfter uses then entire staged write of the entire transaction or batch, while get uses the actual existing contents of the database.
You are by no means obliged to use getAfter, if get works just fine for your case.
getAfter is useful when you need to examine other documents that may have been changed in the transaction or batch, and still have a chance to reject the entire transaction or batch by failing the rule. So, for example, if two documents being written in a single transaction must have some field value in common in order to be consistent, you need to use getAfter to verify the equality between the two. get wouldn't be helpful here, because it doesn't know anything about the other document in the transaction that hadn't been written yet.
On the other hand, if your rule needs to check if a document in the transaction has not changed an a field in an existing document (that is not the current document being checked), get would be necessary to fetch that value instead, before it's written by the transaction.

Permission denied still gets added to .on() listener

I'm using firebase's .on() event listener to listen for entries inserted into my database in real time. I've noticed that even when a data insertion is denied, a user subscribed to the ref the data was supposed to be inserted into still gets that piece of data, even though in the database the data is never inserted.
I noticed this while developing the chat module of my web app. Here is a gif of the bug: https://gfycat.com/VariableFrailBasenji
I've set a validation rule on new messages that their length has to be under 200:
"$msg": { ".validate": "newData.val().length < 200"}
So when you see me paste in a bunch of letters, the console says the write gets denied, but the user who had the .on() subscription to that part of the database still got the message, even though it didn't get added to the database.
Anyways, this isn't really a post with a question, just wanted to share this strange bug that could potentially lead to data leakage.
The Firebase SDK uses advanced techniques like latency compensation when you write to the database. This means that before the server has acknowledged a write it goes into an in-memory database cache and optimistically assumes the write will be allowed.
When the server denies the write, it will remove the incorrectly cached data and you should see a corresponding child_removed event.

Resources