3
votes

I have an application I am building in Kotlin, with a backend in Node.js. I am allowing users to login/signup using Firebase Authentication on the client application.

Part of the functionality of the application, is to let users save data online through Firebase's real time database. What happens inside the application, is once a user is logged in, I am passing his/her uid to the backend, which makes a request to the database.

Everything was working fine when the rules for the database were to allow read/write to everyone. Once I changed them to this:

{
  "rules": {
       "users": {
         ".read": "auth != null && auth.uid != null",
         ".write": "auth != null && auth.uid != null", 
        }
  }

}

I keep getting Permission Denied.

I have tried different variations of the rules:

  • Without the users key
  • Only checking that auth is not null

but none seem to work.

Is there some step I am missing?

I have combed over many similar StackOverflow questions and Firebase's real time database documentation, but have not found an answer to my problem.

Some code for reference:

Backend:

app.get('/someRoute', function (req, res) {
var database = firebase.database()
var uid = req.query.uid

database.ref('/users/' + uid).once('value')
.then(function(snapshot) {
  var data = snapshot.val() ? snapshot.val() : []
  res.status(200).send({ response: data})
}).catch(function(error) {
  console.log(error)
  res.status(500).json({ error: error})
  })
})

Client:

fun loginUser(view : View) {
    FirebaseAuth.getInstance().signInWithEmailAndPassword(userEmail, userPassword)
        .addOnCompleteListener(this) { task ->
            if (task.isSuccessful) {
                updateFirebaseUserDisplayName()
            } else {
                Toast.makeText(this, "An error has occurred during login. Please try again later.", Toast.LENGTH_SHORT).show()
            }
        }
}

fun updateFirebaseUserDisplayName() {

    FirebaseAuth.getInstance().currentUser?.apply {
        val profileUpdates : UserProfileChangeRequest = UserProfileChangeRequest.Builder().setDisplayName(userEmail).build()
        updateProfile(profileUpdates)?.addOnCompleteListener(OnCompleteListener {
            when(it.isSuccessful) {
                true -> apply {
                    Intent(this@LoginActivity, MainActivity::class.java).apply {
                        startActivity(this)
                        finish()
                    }
                }
                false -> Toast.makeText(this@LoginActivity, "Login has failed", Toast.LENGTH_SHORT).show()
            }
        })
    }
}
2

2 Answers

2
votes

After some searching, I found the solution to my problem.

It appears that because I was authenticating users on the client and having a backend that communicated with Firebase's Realtime Database, I had to use Firebase's Admin SDK in the backend.

This is because it was required to pass a unique token generated each time a user logs in and authenticates in the client. This token is then required to be sent to the backend and used when trying to access the Realtime Database.

For anyone else that will stumble upon this question and want to know how it can be done, follow the links below:

Also, make sure to reference your database name correctly

2
votes

It seems a problem with the permission you have given to your node i.e users and try below way

Only authenticated users can access/write data

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}