I made a game with JavaScript in which the user takes a specific amount of time to complete it. Then I want to create a document in a collection named scores on Firestone with that time. My problem is that the user can create an infinite amount of documents with a random time by simply copy & pasting the code and entering their own values. Is there a way that the user can only create once a document with the correct time at the end of the game? Here is the code that I used:
db.collection('score').add({
time: 122,
});
You have two requirements I think:
The user should only be able to create one document.
The time in the document must be the current time at the end of the game.
Allow only one document per user
The first one is easiest to do by using Firebase Authentication to sign the user in, so that they have an identity. If you don't want the user to enter any credentials, you can sign them in using anonymous authentication.
No matter how you sign them in, this gives them a unique UID (user identifier), which you can then use as the document ID:
let uid = firebase.auth().currentUser.uid;
db.collection('score').doc(uid).set({
time: 122,
});
So instead of calling add() (which generates a new document each time you call it), we create a document with the user's UID as its name. That way the user will always be writing to the same document.
You can then enforce that the user can only write their own document with Firebase's server-side security rules. See for an example of this, the documentation on content-owner only access.
Ensure the time can't be spoofed
To prevent the user from writing just any value they wish into the database, we can use Firestore's built-in FieldValue.serverTimestamp marker value. When you use this in your code, the Firestore server automatically writes in the current value from the server:
let uid = firebase.auth().currentUser.uid;
db.collection('score').doc(uid).set({
time: firebase.firestore.FieldValue.serverTimestamp(),
});
Here too, you can then use Firebase's server-side security rules to ensure that a malicious user can't pass their own value. To do that, compare the value that is being written (request.data.time) with the built-in variable for the current time (request.time).
Related
I'm building an an app in Firebase with a user feature and I need to implement a system to allow:
A user to follow another user
A user to see a list of the users they're following
A user to set their profile as private so that some of their data is only visible to the people following them
A user to be able to send a follow request to a user with a private profile
A user with a private profile to be able to accept/reject follow requests
So far I've made a Firestore collection at the root called users. When a user signs up with Firebase Auth, a document is made in users with the following structure:
user (document)
username: stringaccountIsPrivate: boolean
userData (collection)
userData (document)
where all the data that would be hidden if the account were private is in the userData document.
I'm not sure how I could implement the system to fulfill my requirements from here so that I could use Firestore rules to only allow followers of a private account to view that account's userData. I would appreciate it if anyone could suggest an appropriate data structure and an outline of how to write rules for this.
For this kind of situation, you must maintain two sources of truth, one for the creator and one for the user. this is done with an array of strings in both that have the user_uid and any additional information concatenated.
The goal is to have an array of CSV-like values of which you can split and render within your app.
create a concat string: entry = [user.uid, user.name, user.url].join(';');
return string to object: entry.split(';');
Doing the following ensures that only a unique entry exists
db.doc("user/user_id/userData/followers").set({ followers: Firestore.FieldValue.ArrayUnion(entry)}, {merge: true});
This is only a rough example and some backend logic will be needed to scale large - but with this, you have a theoretical limit of 200k entries depending on how much data you want to store in the string.
Additional logic would involve cloud functions reading and writing when a request to follow has been created which handles a counter that creates new documents as needed and ensure's that the counter is updated to prevent overflow since Security Rules can't do any recursive logic or manipulate the request directly.
I am using firebase as a backend for my Android App. And this app is a social media app where users can post, comment & like. so I am storing user data in user_collection and this collection is secured by security rules where if uid == auth.uid (any user can only access data of himself).
Now in this app when a user post something every user can see this post. And in post_collection I am saving userId in post_doc.
So the problem is I need to show name of user to other users and I have only userId but the problem is a user can't get name of other user by uid beacuse of security rules. now I have to solutions for this please tell me which one is better or you can also suggest any other solutions also?
I can use cloud functions getUserNameById() (Problem : I need to call this function very frequently in feed when user scroll)
I can store name also in post_doc (problem : when user changes his name then It will show old name in old post)
Thanks for you kind help
In a scenario like the one you describe, I would typically store the user name in each post doc. I would also ignore updates to the name, as I think of the user name in the post doc as a historical value: this is the name the user had when they posted this. Now you may want different behavior of course, in which case I recommend reading: How to write denormalized data in Firebase
Your approach with Cloud Functions is fine too, and quite common in some situations. But I tend to only use Cloud Functions for reading data, it the read operation itself is particularly complex, which isn't the case here. In a case like this, I'd recommend coming up with a data model that allows the use-case and security you want.
For example: if you create a collection usernames where each document has the UID as its document ID, and then contains a single field with the username for that UID, you could implement the lookup of the user name through Firestore.
So you could have:
Store the full user profile in /users/$uid.
Store the user name in /usernames/$uid.
Have a Cloud Function that triggers when /users/$uid is written and that updates /usernames/$uid.
The client then has read access to each /usernames/$uid document, or even to the entire /usernames collection in one go if needed.
This way the names can be cached on the client, and continue to work when the app is offline, unlike in your approach with a Cloud Function that looks up the user name.
Consider the solution: whatever public data you need (author name, author userpic link etc) just save it with the post at the time it had created.
So your Message Pojo will looks like:
id
authorName
text
etc..
and just display this name (authorName).
It will be the bad way to go any time to User_collection folder to take the name even if there are would be not strict security (becouse it takes time and document reads)
I want to assign a unique but incremental document/record(a data entry, not Firebase document) number when user generates his/her document in the app.
The document number should be unique integer/long and will be visible on the generated PDF document of the user as Your document number : 1100xxxxxx. This last generate document's number will be stored separately so when a new user generated his/her document, this number can be easily picked, incremented and assigned to user.
This way I won't have to query the database again for the last generated number using sorting as
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query query = rootRef.collection("Users")
.orderBy("DocumentNo", Query.Direction.DESCENDING)
.limit(1);
Right now, I generate a user by assigning the user.uid to the Firebase document. The reason I want to ensure the uniqueness of generated certificate number is that it will be visible to user and multiple users will be hitting the server at the same time(same millisecond even).
Although, I've seen almost every similar answer but the answer I've found similar to what I was thinking is this. Also, this unanswered question is what I should do but it has problems too.
So, is there a way by which I can generate a unique document/record number to the user? Answer need not be in Flutter, I want the logic mainly.
If you want to increment a number every time something happens do:
document.ref.update({unique_value: FieldValue.increment(1)});
That number will be unique. It will work with multiple users hitting the server at the same time.
I am doing the user authentication where I have this case:
Read from vendor_type document and if it returns null(doesn't exist) then continue the transaction,
Create new user using .auth().createUserWithEmailAndPassword(email,password),
Read the new users ID,
Write to vendor_type document some of the new user's detail such as name, surname, userId -->> userId is the problem, how can I create a user and get the ID within a single transaction, can I even do that? ,
Take the newly created ID of the user, and create a new vendor document with that ID.
So far I don't have any code to post because I don't know if this is even gonna work so I didn't start. If you have any idea how to implement this, please let me know. The main issue is getting the user ID while still in the transaction.
At the time of writing, it is not possible to combine in one transaction the creation of a user through the createUserWithEmailAndPassword() method from the Auth service AND a write to the Firestore service.
They are two different services offered by Firestore and therefore you cannot combined calls to these two different services in one transaction.
I have a question regarding authentication using Google Firebase.
For an app, I want to build an authentication similar to the one Slack uses: first, the user provides the input as to which group they want to log in to. If there exists a group with the same name as provided in the input, the user is then taken to a login/signup screen.
I've thought about storing users in the realtime database as follows, but I think there must be a better way to do this (since I don't think I can use the firebase authentication in this case):
groups: {
"some_group_name": {
"users": [
"user1": {
.. user 1 information
},
"user2": {
.. user 2 information
}
],
"group_details": {
"name": ..,
"someGroupDetail": ..
}
},
"some_other_group_name": {
...
}
}
I haven't realized if there is an obvious answer yet, so I'm open to suggestions. How would you suggest I tackle this?
Thanks
PS: I'm building the application using Nativescript and Angular, and (so far) there is no server or database involved other than Firebase.
Another suggestion that might work, is by using Firebase Auth Custom Claims. That way, you only need to store the group ID and group name in your realtime database, without worrying to keep changing the database each time user is added or removed.
This is one way you can do it:
Store database exactly like you have it, with it's group ID and name.
In your backend script (I recommend Cloud Function), each time a User is registering themselves, add custom claims in your user: Specifying what group is the User belong to.
Every time user authenticate, retrieve the group ID from custom claims. And there you get it!
Note: be careful not to put too much information in your custom claims as it cannot exceed 1000 bytes.
Read more about it here: https://firebase.google.com/docs/auth/admin/custom-claims
I would suggest you to implement Root-level collections.
Which is to create collections at the root level of your database to organize disparate data sets(as shown in the image below).
Advantages: As your lists grow, the size of the parent document doesn't change. You also get full query capabilities on
subcollections.
Possible use case: In the same chat app, for example, you
might create collections of users or messages within chat room
documents
Based on the reference from the firebase cloud firestore
Choose a data structure tutorial (I know you are using Realtime database but structuring the database is the same since both are using the NoSQL Schema)
For your case:
Make 2 Collections: Users, Groups
Users: User info is stored in the form of document
Groups: In the Groups Collection, here comes the tricky part, you can either store all groups subcollection under 1 document or split into multiple documents (based on your preference)
In the group-subcollection, you can now store your group info as well as the user assigned where you can store user assigned in the form of array, therefore whenever a user access the group, query the user assigned first, if yes, then allow (assuming users can view all group)
You do the thinking now