Ad

How To Check If User With The Email Entered By User On The Login Form Exists Or Not In Firebase

It's my first time I am working with firestore db. So I have this User class in my app:

data class User(
    val name: String,
    val email: String,
    val standard: String,
    val formNum: String,
    val isVerified: Boolean = false
)

Every time a new user registers, it save the data to the firestore database. I want to query when the user clicks the login button, but before they are actually signed in, if a user with that email exists or not. I use the following code:

db.collection(User.USERS_DB_NAME)
            .whereEqualTo("email", email.text.toString())
            .get()
            .addOnSuccessListener {
                if (it.size() == 0)
                {
                    Log.d(TAG, "Email not found")
                    email.error = getString(R.string.error_invalid_email)
                }
            }

However in the log, it gives warning as:

W/Firestore: (19.0.2) [Firestore]: Listen for Query(users where email == [email protected]) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}

Here is my rule:

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{user} {
      allow read, write: if request.auth != null && user == request.auth.uid;
    }
  }
}

How do I modify my rules to allow this query? Or my way of structuring the data is wrong? Please suggest the best way to solve this.

Ad

Answer

Since you commented on Mohammad's answer that the user isn't signed in yet, it its expected that your rules will reject the query. The obvious solution is to allow reads to everyone:

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{user} {
      allow read;
      allow write: if request.auth != null && user == request.auth.uid;
    }
  }
}

Note that this exposes your entire list of users to the world though, which may be a security risk.

You may want to limit what a user can query, by using the approach documented in securely querying data. To secure the query, you'd specify the list operation:

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{user} {
      allow list: /* TODO: something to require a query on email address */;
      allow read, write: if request.auth != null && user == request.auth.uid;
    }
  }
}

Note: I am still looking how to verify the query in the rules, since from the documentation it seems the where() condition is not exposed in request.query.

But this still runs the risk of exposing data of another user: if you know someone's email address, you can list their document, and thus get its data.

To prevent this, you have two options:

  1. Create a Cloud Function (or other server-side endpoint) that performs the query in a trusted environment, and only returns the true/false result to the user.
  2. Create a separate collection with just the user email addresses. In this collection I'd use the email addresses as the document ID, and then store the UID of the user as the (probably only) value in the document.

What you're trying to implement sounds quite close to Firebase Authentication's fetchSignInMethodsForEmail API, which lists the providers for a given email address. The idea of this API is that you combine it with the "only allow a single account per email address" setting, to implement a flow where on the first screen the user enters their email address, and then on a second screen you either allow them to register (the email address isn't known yet), or ask them for their credentials.

Ad
source: stackoverflow.com
Ad