unfortunately, we're not using the Firebase authentication system, we have our username and password. We want to implement the following rules:
We want to give Firebase functions and google cloud functions full access to all the collections, so if the source of the request is the project id project-example
, then let the request pass through.
We want to implement the following rules for everyone else:
We have multiple collections and the document name usually is the same as the document id doc.id
in most collections
let's take the following 2 collections as an example
/users/xas1234
{
"id", "xas1234"
"username": "foo",
"password": "bar"
}
/tasks/xas1234
{
"id", "xas1234"
"title": "foo",
"description": "bar"
}
What we want to do is to have a firestore rule to check if the username
and password
sent by the user match the username and password in firestore then give the user read and write access to /user/xas1234
and /tasks/xas1234
, so give the user access to the documents which hold his id
one last condition is that the admins are stored in a admins/x1234abc
{
"id": "x123aa",
"username": "john",
"password": "foobar"
}
If the credentials sent match the admin's credentials, then the admin has to have access to everything because the admin needs to add, and remove users and so on.
So an unauthenticated user should only be able to authenticate against the credentials stored in the users
table.
I've tried many many rules but even the basic ones, like the one below, aren't working
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**}/user/{userId} {
allow read, write: if request.auth.username == resource.data.username && request.auth.password == resource.data.password;
}
}
}
I guess the main issue is the Flutter application
await FirebaseFirestore.instance
.collection("user")
.where("username", isEqualTo: usernameCtrl.text.trim())
.limit(1)
.get();
Since you usually to a where
clause on a single field, you won't end up having the username and the password in a single request.
we're not using the Firebase authentication system
If you're not signing the user in to Firebase Authentication, the request.auth
variable in your security rules is going to be null
and this rule will always reject access:
if request.auth.username == resource.data.username
To make this work, you will have to sign the user in to Firebase Authentication. There is no other way to populate the request.auth
variable in your security rules check.
Note that you can build your own username/password sign-in provider as shown in the documentation on custom sign-in, and there's even an example of username/password sign-in.
The only alternative without signing the user into Firebase Authentication, is to pass the information in another way. The other two options for passing data to the security rules are:
request.resource.data()
for write operations.Since your rule applies to both reads and writes, that second option can't be applied. So that leaves only passing the information in the path to the data, e.g.
await FirebaseFirestore.instance
.collection("user")
.document("myUsername") // 👈
.collection("password")
.document("myPassword") // 👈
.get();
So on the lines I've marked you'd pass the username and password, and then in your database you can either check in your security rules - or you can ensure documents only exist for valid username/password combinations.