1

Following the post here I created a simple security rule and cloud function that gets called to see if a username already exists. The problem is that the security rule write check always passes and just sets the new value in that location (/username_lookup/user1).

When I try to write at this location using the realtime database rules simulator it works as expected, i.e. the write is blocked.

Can someone spot the problem?

The firebase security rule

"rules": {    
 "username_lookup": {
  "$username": {
     // not readable, cannot get a list of usernames!
     // can only write if this username is not already in the db
     ".write": "!data.exists()",

     // can only write my own uid into this index
     ".validate": "newData.val() === auth.uid"
  }
 }
}

And the cloud function

var fb = admin.database().ref();
createUser(uid, username);

function createUser(userId, usrname) {
    fb.child('username_lookup').child(usrname).set(userId, function(unerr) {
        if(unerr) { 
            res.setHeader('Content-Type', 'application/json');
            res.send(JSON.stringify({error: "the_error_code" }));
         }
     }); 
 }

Screenshot of the username_lookup object/index enter image description here

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Padawan
  • 770
  • 1
  • 8
  • 18

1 Answers1

2

You Cloud Functions access the Firebase database through:

var fb = admin.database().ref();

As you can see, the module is admin which indicates that you're using the Firebase Admin SDK. One of the key traits of the Firebase Admin SDK is:

Read and write Realtime Database data with full admin privileges.

source: https://firebase.google.com/docs/admin/setup

So the Admin SDK actually bypasses your security rules.

It's also a pretty bad practice to use an error handler for basic flow control.

Instead, use a Firebase transaction to read/write the location with the name in an atomic way:

fb.child('username_lookup').child(usrname).transaction(function(value) {
    if (value) {
        res.setHeader('Content-Type', 'application/json');
        res.send(JSON.stringify({error: "the_error_code" }));
        return; // abort the transaction
    }
    else {
        return userId;
    }
}); 
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • As always, I appreciate the answer! I am having trouble finding documentation on how to obtain a non admin database reference. I read this post https://stackoverflow.com/questions/44615920/how-to-write-in-db-with-firebase-function-not-as-admin (fyi the link in the answer is a 404) however that is based off of a trigger and using the event object. In my cloud function I am using a post request. Will I need to import another Firebase SDK to get a non admin reference? – Padawan May 26 '18 at 20:37