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

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).

Related

firebase - check if an user is already registered in database

I have a firebase app that is running on mobile devices and now I need to create the web version of it.
At the moment I have integrated firebase ui to give to users the ability to login. Since I need to show a wizard if the user isn't already registered, how I can check if user exists in the database so I can decide if show the wizard or not and then add it to the database?
It sounds like you're looking for the fetchSignInMethodsForEmail method, which returns the list of providers with which a specified email address has previously been signed in to Firebase.
The typical flow is:
Get the email address of the user
Call fetchSignInMethodsForEmail to get a list of providers
If the list is empty, go to your account creation flow
If the list has a single value, show the sign in screen for that provider
If the list has multiple values, show a screen where the user can pick from those providers
I consider you are using firebase auth and firestore, but this is the logic
firebase.auth().onAuthStateChanged(async (user)=> {
if (user == null){
//the user is not signed in, navigate to login screen
}else{
//the user is signed in, try to check if he exists in the database or register him
//let say you are using firestore roo
let user_ = await firebase.app().auth().currentUser
firebase.firestore().collection("collections_of_users").doc(user_.uid).get().then(snap =>{
if(snap.exists){
//the user exists
}else{
//register him, and before make sure his email is verified by checking if emailVerified === true
firebase.firestore().collection("collections_of_users").doc(user_.uid).set({user_mail: user_.email, user_name: "xxx"})
}
})
}
});

Validate Firebase realtime URL's are from Firebase storage

So I have a function in my website which allows users to upload a profile photo. The photo is stored in a folder in my Firebase storage under their UID and then once successfully uploaded, the downloadURL is stored under their UID in my Firebase realtime database like so:
database.ref('users/'+firebase.auth().currentUser.uid).update({
profilePhoto: downloadURL
});
Now I have some rules already in place so that only the auth UID can write data to themselves.
{
"rules": {
"users": {
".read": "true",
"$user": {
".read": "true",
".write": "auth.uid == $user",
"profilePhoto": {
".validate": "newData.isString()
},
}
}
}
}
Work great but how can I make it so only URL's coming directly from my Firebase storage are written?
For example, a user with knowledge could simply run the script above and change the data to something outside of the storage or just some random text which breaks the profile image when I fetch it.
One thing I thought of doing is adding the Firebase storage 'base path' as a validation like so:
"profilePhoto": {
".validate": "newData.isString()
&& newData.val().contains('mystorage-12345.appspot.com/')
}
But is there a better way of doing it? Will the 'base path' ever change down the line?
There are no combinations of rules that you could use to prevent a user from linking to a malicious file or entering gibberish. Since you're open to better methods, here's my standard pattern for handling uploads like this.
First, decide on a URL structure for your profile photos where the photo will be stored in your bucket with an easily guessable format. My preference is:
/u/<uid>/profile_<width>x<height>.jpg
Second, provide a signed URL for your user to upload their image to. This might be in a separate bucket that, when a new object appears, triggers a function resize the photo and then moves the resized photos to /u/<uid>/profile_<width>x<height>.jpg with the relevant widths and heights.
Finally, there's no need to store the URL of the profile photo because any user's profile can be displayed like this:
<img src="https://<bucketUrl>/u/<uid>/profile_512x512.jpg" onerror="this.src=' + "/img/default-profile.jpg" + '" />
What's happening here is it first attempts to load the user's profile photo using the guessable /u/<uid>/profile_<width>x<height>.jpg pattern and if it fails to load, for any reason, display a default photo in its place.
Advantages
Lower costs due to fewer database reads, writes and storage.
Simple, fast and reliable.
Built-in failover in case the bucket can't be reached for any reason.

Firebase database security for new users

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.

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.

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>: { ... }

Categories