Firebase database security for new users - javascript

Is there a way to check the values using rules that are initially set on user creation?
I have perused the firebase docs to no avail.
When the function createUserWithEmailAndPassword is called, I then create some values in the database, for example:
"users": {
"ht35resf435dwe3rfdw": {
"is_premium": false,
"display_name" "John",
"last_login": 15353723826
}
}
The problem I am facing is: is_premium: false is part of the front end code and I am worried that a user could somehow change this to is_premium: true.
I can't figure out a way to check that it is initially set to false on creation.
P.s I could be going about this all wrong, I am a junior so I would appreciate any and all pointers.

To only allow a value to be set to false, you can use a validation rule:
{
"rules": {
"users": {
"$uid": {
"is_premium": {
".validate": "newData.isBoolean() && newData.val() == false"
}
}
}
}
}
The above will simply only allow false to be written by any client. When you're writing using an Admin SDK however, those writes bypass these security rules. So you can use the Admin SDK to mark premium users.

you can just change the security rules as below so that the user won't be able to make changes.
{
"rules": {
"foo": {
".read": true,
".write": false
}
}
}

I just started using Firebase so I cannot provide much information. If you want to start learning it there is a great series you can watch. Also, I would suggest using Cloud Firestore instead of the Realtime Database because the ladder is older and has fewer features. Changing the security rules on your database will let you fiddle with which users can edit data. What you need to do is make the premium default to false or even not exist in the database and then create a javascript function to create or change it to true in the database.
EDIT:
Ajith Naruto's would work but it would also disallow all writing to the database from the web app.
EDIT 2:
Frank van Puffelen's should work I would go off of his answer.

Related

Firebase realtime database rules - Allow multiple user access to list all messages

I would like to give multiple users access to fetch a collection of "messages" they have access to in Firebase Realtime database. The database fetch would read "/messages" and return a collection of all messages the user has access to. Database structure looks like this:
"messages" : {
"-L123456789": {
"access": {
"author": "user-one-id-987654"
"assigned-user-id-1234": "assigned-user-id-1234"
}
"data" : {
"theData": "Hello world!"
}
}
}
I have created the following rule:
{
"rules": {
"messages": {
"$message_id": {
"data": {
".read": "
//if author or assigned user
data.parent().child('access').child('author').val() === auth.uid ||
data.parent().child('access').child(auth.uid).exists()
",
".write": "false"
}
}
}
}
However, I am not able to get a collection of all messages where I am listed as author or assigned user.
What rule would allow a user listed as "author" (user-one-id-987654) or "assigned user" (assigned-user-id-1234) to get a collection of all messages they have access to by simply reading the "/messages/" database path?
I am guessing a rule in the root of "messages" might be the answer?
I have tried the below rule - it grants access to all authenticated users - but I wish to only return a collection where the user is listed as "author" or "assigned user".
{
"rules": {
"messages": {
".read": "auth.uid !== null"
}
}
}
Kind regards
/K
Firebase server-side security rules can not be used to filter data. When you attach a listener to the database, the server checks if that listener is guaranteed to always meet the rules (no matter what the data). If it doesn't meet the rules, the listener is rejected right away.
So if you attach a listener to /messages, the server checks if you have read permission to /messages. And since you don't, it rejects the listener.
If you want to allow the user to read messages of which they're the owner you'll need two things:
A query that only retrieves the messages that the user owns.
Security rules that ensure only that query is allowed.
For more on this, see the Firebase documentation on securely querying data and the blog post introducing this feature.

Firebase does "read: true" rule allow to see users emails?

I have a firebase realtime db with the following rules structure:
{
"rules": {
"users": {
".read" : true,
"$user_id": {
".write": "$user_id === auth.uid"
}
}
}
}
does it allow anyone to see the db users' emails even if they only sign up using socials (google, fb)?
how can i avoid it in case?
I've set read : true, cause i create dynamic links which on open through routing they show certain data of certain users to an unregistered user and should remain like that.
EDIT: to let you better understand the process, what i'd need to set are rules so that users can sign up, only signed in users can write data, but if they create a link (with one of their items id contained in the link) anyone can open that link and read the item.
Here is the tricky part, at the same time non registered users shouldn't be allowed to read users emails (the email address saved by firebase upon social login, i do not programmatically save email address in the db).

restricting access to firebase database to users within a set

I wrote a simple chat application a while back to help me learn node and socket.io, I have recently been looking into firebase as a platform for an app/website, and I am running into an interesting problem I can't solve. Within the app there will be a chat app, there will be channels and all that good stuff, the hard part is there will be private chat, where 2 users can talk privately. The plan is to store the chat in a firebase database. Now I can easily restrict access to firebase databases based on if a user is authenticated, and even restrict user profile access to authenticated users and the user that owns that profile, but I am trying to figure out how to restrict access to the "private chat" children of the "chat" database, to only the 2 users that are in that conversation.
I am thinking the database would look something like this...
{
"chat": {
"channels": ['topics', 'current', 'blah', 'blah', 'blah'],
"{PRIVATE_CHAT_UID_GOES_HERE}": {
"users": ["{USER_ID_1}", "{USER_ID_2}"],
"messages": [{"from": "{USER_ID}", "message": "Hi there"},{...}]
"createdOn": "DATE GOES HERE"
},
"{PRIVATE_CHAT_UID_GOES_HERE}": {
"users": ["{USER_ID}", "{USER_ID}"],
"messages": [{...}, {...}],
"createdOn": "DATE GOES HERE"
}
}
}
Then I would restrict access to the child(private chat id) to only the users that are in the "users" array. That way no one can read or write to that particular chat, unless they are in that particular chat. I just have no idea how to go about it.
I know you can do things like
".read": "auth !== null && auth.uid = $uid"
But I don't think that would be applicable since it limits usage to the owner of the uid, and the uid would be automatically generated when I add a child to "chat" to start a private chat between users.
Is something like this even possible, or is there some better way to structure the data that would allow an easy restriction of access to only the 2 users that are part of the conversation?
I am trying to avoid having a node.js server sitting around just verifying if a user is in a chat, it seems like a pointless overhead, when you can list the database and handle auth directly from the database. I am more than happy to provide code samples of what I have, though I don't know that they are relevant. Thank you in advance for any help.
Your first problem is that you're storing the users in an array. An array is an ordered collection that can have duplicate values. In your scenario you don't want duplicate values, and most likely the order doesn't matter. In such cases you should use a set data structure, which in Firebase is modeled as:
"users": {
"{USER_ID_1}": true,
"{USER_ID_2}": true
}
Now you can restrict read access to the chat room to its members by:
{
"rules": {
"chat": {
"$roomid": {
".read": "data.child('members').child(auth.uid).exists()
}
}
}
}
Be careful mixing different data types in the same node. It's often best to keep each entity in its own top-level node, relating the different types by their key. For example, I'd separate the room's messages, members, and other metadata:
chat
rooms
<roomid1>
createdOn: ....
.... other metadata for the room
roomUsers
<roomid1>
user_id1: true
user_id2: true
roomMessages
<roomid1>
<message1>: { ... }
<message2>: { ... }
<message3>: { ... }

Firebase Database Rules for multi-update transactions

I’m trying to create security rules for the “Save data as transactions” blogging app example from the Firebase guide.
The user can increase or decrease the star count for a post, having his own UID being included or removed from the node at the same time.
I’ve written the following rules:
(I removed the rules for the counter increase/decrease since they are out of the scope of the question)
“stars”: {
".read": true,
"$postId”: {
".write": "auth != null && (newData.child('users').child(auth.uid).exists() || data.child('users').child(auth.uid).exists())",
"users": {
"$userId": {
".validate": "$userId === auth.uid"
}
}
}
}
And an exemple of a stars node:
“stars”: {
“postId1”: {
starCount: 2,
"users": {
“userId1”: true,
“userId2”: true
}
}
}
The rules work fine for adding an user to the “users” node, but a problem arises when removing.
It’s possible for a mean-spirited user to remove any other user from the “users” node, just update it with a empty node. Or a node with all the users from before, minus one he chose to remove.
The “.validate” rule ("$userId === auth.uid") does not work for a empty node being submited and I can't write a rule that checks if all the users that were in the database before the update are still there after.
The way I’d solve the problem if I wasn’t using transactions was to to move the “.write” rule to under “$userId”, limiting the uptate for only one user at a time and only with the same UID as the logged user.
Something like:
“stars”: {
".read": true,
"$postId”: {
"users": {
"$userId": {
".write": "auth != null && $userId === auth.uid"
}
}
}
"starCount": {
".write": true
}
But since I’m doing the database update using transactions I need the “.write” rule under the "$postId”, permitting the update of the “users” node and the “starCount” node at the same time. Something that would not be possible in my last exemple (no “.write” rule under "$postId”).
So it seem like a Catch-22. Or I use transactions but I’m not able to secure the starCount with rules, or I do it as a normal multi-update but loose the concurrency benefits for increasing the counter.
How can I correctly secure the “Save data as transactions” blogging app exemple?

Firebase should my user id structure include the words "simplelogin"?

When I login with simple login and use authData.uid I get the return of simplelogin:1 (with 1 being the id of my user.
My user structure is set up like this:
{
"users":
{
"1": { ... },
"2": { ... }
}
}
And my rules are set up like this:
{
"rules": {
"users": {
"$userid": {
".read": "auth.uid == $userid",
".write": "auth.uid == $userid"
}
}
}
}
So I am a little confused on how to make it so /users/1 would work. Does everyone normally make there user structure "simplelogin:1" instead of "1"?
It's saying I don't have permission to view my user since my $userid is "simplelogin:1" and not "1"
Does anyone know how I can fix this?
In your example above, the unique, cross-provider user id is simplelogin:1, not just 1. Using only the integer is problematic, because these user ids are not unique across all providers (Twitter, Facebook, Google, GitHub, etc.) and may have collisions.
It is expected and by-design that the user ids include the prefix for how the user was authenticated. Also note that the format of these user ids may change in the future, so it is not recommended that you attempt to parse them or treat them as human-readable. They are only intended to be unique alphanumeric ids for your Firebase.

Categories