I have created a small app which uses Firebase as its database for my portfolio. A lot of the website renders a different view whether an authenticated user is logged and I would like to have one account that has the ability to log in and would be authenticated but would not have permission to write. My current rules are as follows:
"rules": {
".read": true,
".write": "auth != null", }
I know this allows anyone to read the data (which I want) but I am wondering if there is a way to "trick" my react app into thinking a user, with an example uid of "Example123", is authenticated but if that user tries to write it fails?
I have tried something like:
"rules": {
".read": true,
".write": "auth != Example123", }
but firebase did not like that. Is it possible to do what I am wanting using firebase rules or would I need to figure out a different approach? Thanks in advance.
You could do as follow:
Create a node users in your database, under which you will store the specific user(s) with its (their) User UID, like:
- users
- Lt7kif73eL58dFCYcmeEfOzUl2n1 <- User UID
- restrictedRights: true
- name: xxxxxx
and write your rule as follow
".write": "auth != null && !root.child('users').hasChild(auth.uid)"
Of course you should add this rule to your database:
"users": {
".read": ....,
".write": false
}
Related
I am trying to read all events node as user that have admin node,
and able to read only related event if not admin.
The issue that I cant get all events with this security rule.
"Event":{
"$uid": {
".read": "auth.uid == $uid || root.child('Users').child(auth.uid).child('admin').val() == 'admin'",
".write": true
}
where my requests looks like:
var starCountRef = firebase.database().ref("Event/"); //trying to read all events as admin
starCountRef.on("value", (snapshot) => {
const data = snapshot.val();
The problem we have here is that the RTDB rules work from top down. That means if one rule in the top denies the access it doesn't matter what the one down says. In your case giving access to the whole list to the admin would be no problem but then also to each owner of the event would be. Because those are probably no admins. And you can access the whole list only when you put the .read above the uid.
There is one way I could imagine to make it work. There are query-based rules. More about them here.
You could write your rules like this:
"Event":{
".read": "query.equalTo == auth.uid ||
root.child('Users').child(auth.uid).child('admin').val() == 'admin'"
}
You would then need to access the data with a query to get it:
db.ref("Event").orderByKey()
.equalTo(auth.currentUser.uid)
.on("value", cb)
i am working on a react.js project which authenticate from firebase but i have 3 multiple type of user's in it (super Admin,vendor admin,vendor staff) with different privileges. how can i authenticate them from firebase and get to know this is my venor admin or vendor staff etc ???? because firebase just authenticate single type of user!
You can control access via custom claims using the Firebase Admin SDK on your own server of through Cloud Functions for Firebase.
Set claims server side for a specific bid with a method like this:
admin.auth().setCustomUserClaims(uid, {admin: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
Then set up your database with a structure that separates admin and vendor admin content:
/
/admin
/data for admins here
/vendorAdmin
/ data for vendor admins here
/staff
// data for staff here, or perhaps this data is accessible to all since the admins may need access to it.
In the Firebase Console, customize the rules to restrict these locations to those who include the proper claim:
{
"rules": {
"admin": {
".read": "auth.token.admin === true",
".write": "auth.token.admin === true",
}
"vendorAdmin": {
".read": "auth.token.vendoradmin === true",
".write": "auth.token.vendoradmin === true",
}
"staff": {
".read": "auth.token.staff === true",
".write": "auth.token.staff === true",
}
}
}
This is a simplified example, so you'll have to customize it further to meet the needs of your app.
You can maintain a users table in your database, and every time you sign up a user just add them there as well, using the uid.
I've been using Firebase for quite some time, but I only now decided to really look into the security rules.
My question is, how safe is "auth !== null"? Yes, I realize that this means that only an authenticated user can access the data, but how easy is it to become authenticated? Can someone sign up for the app, and then use those credentials to GET request right into my database?
Like I said, I'm new to Security rules, so I'm sorry if this is an obvious question.
Here's my security rules:
{
"rules": {
"Users": {
"$user_id": {
".write": "$user_id === auth.uid",
".read" : "auth !== null",
"shoofers" : {
".write" : "auth != null"
}
}
}
}
}
Thanks!
Neil
You can give users access to the database either after sign in authentication or with out authentication.
But it is good and safe to allow users to access your database with authentication
with authentication security rules are
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
and without authentication
{
"rules": {
".read": "auth == null",
".write": "auth == null"
}
}
A user can authenticate via anonymous, email/password, various OAuth providers and phone number sign in. You can enable/disable either of them. Your root rule above allows read only access to any authenticated user via any of the mechanisms stated and write access for a specified user to their own data. It is very difficult to fake sign in. The database will always check that the request has an ID token (when auth is not null). ID tokens use public-key cryptography and are hard to fake without possession of the private key.
{
"rules": {
".read" : "auth.uid != null",
".write" : "auth.uid != null",
}
}
or without authentication
{
"rules": {
".read" : "auth.uid == null",
".write" : "auth.uid == null",
}
}
The thing i have seen is, when you use 'simulator' from firebase console and fake authentication by giving some random string as uid, firebase doesn't check it and allows access. So auth!=null is not enough because it authenticates even if uid=1.
I've recently starting exploring firebase as a authentication solution for my angular JS single-page website, and it seems perfect. However from a security perspective I'm not very sure about keeping the logic on client-side in my application.
Suppose I have a check 'isProfileCompleted' for a customer who signs-up on my website, and is supposed to complete his profile. I'm keeping the data in a JSON keyed by the UID with exclusive write access to the customer only.
The problem is, now that the client has write access to his data, he can easily bypass client side validation checks by simply modifying javascript in his browser. Also, the client can easily update his account_type to author/moderator, as it's his data. Does firebase provide a solution to this problem?
Let me know if it's not clear, so I will try to elaborate further.
Thanks.
You can secure your data with Security Rules.
Firebase Security Rules are an expression (does the true evaluate to true/false) based rules language that live on a Firebase server and validate whether the current user can access your data.
Take the following data structure:
{
// the users of the app
"users": {
"1": {
"name": "Sanjay",
"isProfileCompleted": true
},
"2": {
"name": "David",
"isProfileCompleted": false
}
}
}
By default anyone can read or write data to your Firebase database. To fix this you can write security rules.
Security Rules are essentially an annotation on your data structure:
{
"rules": {
"users": { // /users is read only
".read": true,
".write": false
}
}
}
Security Rules give you access to a set of server variables to check your rules against. The most commonly used one is the auth variable which lets you check against the currently authenticated user. You can also create wildcard variables with the $, which acts a route parameter creating.
{
"rules": {
"users": {
// users can read and write their own data, but no one else.
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid"
}
}
}
}
You can even write rules to validate the structure of your data.
{
"rules": {
"users": {
// users can read and write their own data, but no one else.
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid",
".validate": "newData.hasChildren(['name', 'isProfileCompleted']),
"name": {
".validate": "newData.isString()"
},
"isProfileCompleted": {
".validate": "newData.isBoolean()"
}
}
}
}
}
But the Bolt compiler is a better solution for this, as it allows you to create Types to define schema.
You can write your Security Rules in the Firebase App Dashboard or you can upload them via the Firebase CLI.
In my app I want to have a "can read"- and a "can write" view. When the app starts without existing parameters ("first time user"), then 2 random hashes are created, together with the firebase app secret they are POSTed to the PHP FirebaseTokenGenerator to receive my token.
Then I want to do this:
The first hash ("read hash") represents an "anonymous user" entry
The second hash ("write hash") represents a "key" which is a child entry of the user entry
Which looks like this:
<appname>
users
<read_hash>
key
<write_hash>
[other user related data]
...
The hashes can be used as URL parameters, e.g. "myapp.com/#read_hash/write_hash"
What I want to achieve is:
when the user only has his read_hash (calling just "myapp.com/#read_hash"), he should be able to SEE all his entries he entered the first time (having the app create the write_hash / "key" for him). But he is not allowed to modify them.
When he provides his write_hash (calling "myapp.com/#read_hash/write_hash") writing to Firebase is allowed.
My security rules:
{
"rules": {
"users": {
"$user": {
".read": "$user == auth.read_hash",
".write": "$user == auth.read_hash && root.child('users').child($user).child('key').val() == auth.write_hash"
}
}
}
}
My problem is: How do I store the write_hash the first time without my security rule ".write" preventing writing ??
Any other idea how to achieve this? Any architectural / inconsistencies?
I'm using these libs:
Backbone JS with backbone-firebase.js
FirebaseTokenGenerator PHP
Thanks in advance.
First, I should note that you should never put the Firebase Secret in your app. It should always be stored safely on a secure server. Your original question suggested you were sending the token down to a server from the client.
I would suggest having the read_hash be the same as a user id, and then storing a "key" at //key when the user is first created.
Then I'd suggest a rules structure as follows:
{
"rules": {
"$userid": {
".read": "$userid == auth.read_hash",
".write": "!data.exists() || ($userid == auth.read_hash && data.child("key") == auth.write_hash)",
}
}
}
Assuming you already have the write hash's key (because you've authenticated already according to your example), this will work:
{
"rules": {
"users": {
"$user": {
".read": "$user == auth.read_hash",
"$key": {
".write": "$user == auth.read_hash && $key === auth.write_hash"
}
}
}
}
}
If you need to create the hash before obtaining the key, for some strange use case (unlikely, but this will give you some key insights to security rules), you can do something like this:
".write": "$user == auth.read_hash && (!data.exists() || $key === auth.write_hash)"