I check "is user
logged in or logged out" with Getx.
I got permission-denied error when I log in or log out to my app with verify phone number. but I restart my App then It works without error when I got:
Exception has occurred. FirebaseException ([cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.)
here my phone authentication dart
class PhoneAuthController extends GetxController {
static PhoneAuthController instance = Get.find();
late Rx<User?> _user;
FirebaseAuth auth = FirebaseAuth.instance;
CollectionReference subscribers = FirebaseFirestore.instance.collection("subscribers");
@override
void onReady(){
super.onReady();
_user = Rx<User?>(auth.currentUser);
_user.bindStream(auth.userChanges());
ever(_user, _initialScreen);
}
_initialScreen(User? user){
if(user==null){
Get.offAll(()=> const VerifyByNumber());
} else {
String number = user.phoneNumber.toString().substring(1);
var subscriber = subscribers.doc(number);
subscriber.update({"userId": user.uid});
Get.offAll(()=>HomePage(phoneNumber: number,));
}
}
void verifyPhoneNumber(String uPhoneNumber) {
String number = uPhoneNumber.substring(1);
auth.verifyPhoneNumber(
timeout: const Duration(seconds: 120),
phoneNumber: uPhoneNumber,
verificationCompleted: (PhoneAuthCredential credential) {
auth.signInWithCredential(credential).then((value) {});
},
verificationFailed: (FirebaseAuthException e) {
},
codeSent: (String id, int? token) {
Get.off(() =>
VerifyToCode(
phoneNumber: number,
verifyId: id,));
},
codeAutoRetrievalTimeout: (String verificationID) {});
}
void verifyCode(String uPhoneNumber, String verifyId, String smsCode) {
Map<String, dynamic> subscriberToSave = {
"phone": "+$uPhoneNumber",
"name": null,
"email": null,
"password": null,
"fcmToken": null,
"date": DateTime.now()
};
try {
FirebaseFirestore.instance.collection("subscribers").doc(uPhoneNumber).set(subscriberToSave);
PhoneAuthCredential credential = PhoneAuthProvider.credential(verificationId: verifyId, smsCode: smsCode);
auth.signInWithCredential(credential);
Get.offAll(VerificationSuccessfulScreen(number: uPhoneNumber));
} catch (e) {
Get.snackbar("verifyCode, ERROR:", ">$e");
}
}
void quitFirebaseAuth() {
FirebaseAuth.instance.signOut();
}
}
here my firestore rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /subscribers/{subscriber} {
allow get, write: if isSignIn() &&
request.auth.uid == resource.data.userId &&
request.auth.token.phone_number == resource.data.phone;
match /sites/{site} {
allow read, write: if isSignIn();
match /messages/{message}{
allow read, write: if isSignIn();
}
}
}
match /sites/{site} {
allow read, write: if isSignIn();
}
}
function isSignIn(){
return request.auth.uid != null;
}
}
Your code to write the user document does:
Map<String, dynamic> subscriberToSave = {
"phone": "+$uPhoneNumber",
"name": null,
"email": null,
"password": null,
"fcmToken": null,
"date": DateTime.now()
};
FirebaseFirestore.instance.collection("subscribers").doc(uPhoneNumber).set(subscriberToSave);
Your rules that allow/deny this write are:
match /subscribers/{subscriber} {
allow get, write: if isSignIn() &&
request.auth.uid == resource.data.userId &&
request.auth.token.phone_number == resource.data.phone;
You don't set a userId
field anywhere in the code, so this check will fail request.auth.uid == resource.data.userId
and the write will be rejected.
You should either add a userId
field with the correct value to the data, or remove that check from your rules.
In general, you'll want to use request.resource
rather than resource
when validating data in write rules. The resource
variable contains the resource as it exists before the operation, while request.resource
is the resource as it'll exist after the operation (assuming the operation is allowed).