Flutter how to stop auto login using firebase SMS - flutter

When users first open the app, I want them to authenticate themselves by phone number. When they get the sms code and hit login button, the phone number and other information are sent to the server together.
However, when I press the code verification button, it automatically logs the user in without login button pressing so I cannot send data accordingly.
FirebaseAuth.instance.authStateChanges().listen((user) {
if (user == null) {
print("signed out - MyAppState");
} else {
print("signed in - MyAppState");
Navigator.push(context,
MaterialPageRoute(builder: (context) => BottomBarScreen()));
}
});
try {
_testPhoneValidation(_phoneNumberController.text);
await _auth.verifyPhoneNumber(
phoneNumber: "+82" + _phoneNumberController.text.substring(1),
timeout: const Duration(seconds: 5),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: (verificationId) {},
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('failure: ${e.toString().substring(11)}'),
));
}
this is my current code related to login with Firebase.

I searched how to stop auto login but didn't quite find the right answer.
So I just changed the login logic to : send info when Firebase auth state changes.
There isn't much change in the code and I think it was a dumb mistake that I made not to think about changing the logic.
FirebaseAuth.instance.authStateChanges().listen((user) {
if (user == null) {
print("signed out");
} else {
print("signed in");
signIn();
}
});
Future<void> signIn() async {
await postAppToken(
await _getToken(),
_packageInfo.appName,
_packageInfo.version,
Theme.of(context).platform.toString().substring(15),
await _getPhoneNum())
.then((value) {
print("signIn");
Navigator.push(context,
MaterialPageRoute(builder: (context) => BottomBarScreen()));
}
);
}
So I just added a Future to post related information to the server when user is automatically logged in. This solved my problem.

You should do nothing on " verificationCompleted" callback.
just write like this
verificationCompleted: (PhoneAuthCredential credential) {},
Because verificationCompleted is for automatic handling of the SMS code on Android devices.

Related

User info getting saved even after logout. Flutter Firebase

I want to sign a user out and sign a new user in. I am not able to do that. The information of previous user is somehow being saved, I can't deduce how. Any idea what should I do? I followed the answer in this post : How to Signout a user in Flutter with Firebase authentication
added await keyword but its still not quite working
This is the code to it. I am going to Log in page but after login previous users information is being shown to this new user
Future<void> signOut(BuildContext context) async {
try {
await firebaseObject.signOut().then((value) {
Timer(const Duration(seconds: 1), () {
Navigator.pushReplacementNamed(context, RoutesName.login);
});
Utils.flushBarErrorMessage("Logged out successfully", context,
color: Constants.blueColor);
});
} on FirebaseAuthException catch (e) {}
}

Is it possible to cancel a function call after it has been called?

In my case, the user adds his phone number to get an OTp code from Firebase. After he called the send me OTP function, he decided to edit his phone number, so he pressed the device back button and edited his phone number, and then he pressed the send me OTP function again. What happened is that he may have gotten two SMSs if the previous phone is correct, because he called the function two times. or The previous Otp code may go to some one phone number. I don't want to send Otp two times or to an unknown phone number by mistake.
Here my code to send Otp from Firebase.
_verifyPhone() async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: '${widget.phone}',
verificationCompleted: (PhoneAuthCredential credential) async {
await FirebaseAuth.instance
.signInWithCredential(credential)
.then((value) async {
if (value.user != null) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => Tabs(widget.phone)),
(route) => false);
}
});
},
verificationFailed: (FirebaseAuthException e) {
Get.snackbar(
"something went wrong", "try again latter or Resend SMS",
duration:Duration(seconds: 8),
);
setState(() {
_start=0;
});
startTimer();
},
codeSent: (String? verficationID, int? resendToken) {
setState(() {
_verificationCode = verficationID;
_resendToken = resendToken;
});
},
codeAutoRetrievalTimeout: (String verificationID) {
setState(() {
_verificationCode = verificationID;
});
},
timeout: const Duration(seconds: 60));
_resendToken;
}
You can map your future to a Stream
_verifiyPhone().asStream().listen((result){});
Save the StreamSubscription in a variable
StreamSubscription subscription = _verifiyPhone().asStream().listen((result){});
And cancel it if you press the back button. For this you have to call the cancel Method
subscription.cancel()

Flutter_Bloc 8 and Firebase Verify Email -- Clear Prior State?

I am trying to utilize FirebaseAuth's verify email functionality in my Flutter app. I'm using Flutter_BLoC 8. The code works, but when I verify the email address by clicking the link in the generated email, the prior state is apparently still buffered in my app and I need to click TWICE on the 'Continue' button to proceed to the Home Screen.
Here's my code:
ElevatedButton(
onPressed: () async {
context.read<EmailVerificationBloc>().add(const IsEmailVerified());
final isVerified = context.read<EmailVerificationBloc>().state;
if (isVerified == const EmailVerificationState.verified()) {
context.router.replace(const HomeRoute());
} else {
showErrorFlash(context, 'Email Is Not Verified');
}
},
child: const Center(
child: Text('Continue'),
),
),
Here's the BLoC event:
Future<void> _onIsEmailVerified(IsEmailVerified event, Emitter emit) async {
final successOrFailure = await _authRepository.isUserEmailVerified();
successOrFailure.fold(
(failure) {
emit(EmailVerificationState.error(failure));
},
(success) {
if (success) emit(const EmailVerificationState.verified());
},
);
}
And lastly, here's the repository method that's being called:
Future<Either<AuthFailure, bool>> isUserEmailVerified() async {
try {
await _currentUser!.reload(); // We will only call on authenticated users, so we don't expect null
return right(_currentUser!.emailVerified);
} on FirebaseAuthException catch (e) {
final error = e.code;
switch (error) {
case 'network-request-failed':
return left(const AuthFailure.noNetworkConnection());
default:
return left(const AuthFailure.unexpectedError());
}
} on PlatformException {
return left(const AuthFailure.unexpectedError());
}
}
Any ideas on how I can, for lack of a better term, flush the prior state? I'm sort of new to Flutter and BLoC, so I'm hopeful it's a relatively easy thing I'm just overlooking.
Thanks in advance.

Flutter web firebase auth signInWithPhoneNumber showing error "captcha-check-failed" in release mode

I'm facing a problem with Flutter Web Firebase Phone Auth Verification. In debug it is working well and showing me the reCaptcha. But when I host it through GitHub pages in release mode, it shows an error "captcha-check-failed". Even the capcha isn't showing in release mode.
The signInWithPhoneNumber function:
Future<void> loginWithPhoneRequestOTPWeb(
WidgetRef ref,
GlobalKey<FormState> formKey,
String phoneNumber,
) async {
try {
EasyLoading.show();
await FirebaseAuth.instance
.signInWithPhoneNumber(
phoneNumber,
RecaptchaVerifier(
container: 'recaptcha',
size: RecaptchaVerifierSize.compact,
theme: RecaptchaVerifierTheme.dark,
onError: (e) {
print(e);
EasyLoading.showError(e.message!);
return;
},
onExpired: () {
print('Expired');
EasyLoading.showError('Session Expired');
return;
},
onSuccess: () {
EasyLoading.dismiss();
print('Captcha Success');
},
),
)
.then((ConfirmationResult result) {
// update the verificationphone provider
ref.read(sendOtpProvider(formKey).state).update((_) => true);
ref.read(confirmationResultProvider(formKey).state).update((_) => result);
EasyLoading.showSuccess(t!.otpSentSuccessfully);
});
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-phone-number') {
print('The provided phone number is not valid.');
EasyLoading.showError('The provided phone number is not valid.');
} else if (e.code == 'too-many-requests') {
print(
'You have exceeded the number of attempts allowed for this operation.');
EasyLoading.showError(
'You have exceeded the number of attempts allowed for this operation.');
} else {
print(e.code.toString());
EasyLoading.showError(e.code.toString());
}
} catch (e) {
print(e.toString());
EasyLoading.showError(e.toString());
}
}
I've tried without the RecaptchaVerifier as it is optional parameter.
Error Screenshot:
If I've missed anything please let me know. Thank You :)
Okay, I've figure out my problem. In the firebase authentication section, there is an "Authorized domains" section. Here I've to add my domains. But firebase only takes .com domains. As a result, I used firebase hosting and it is working fine

Firebase Phone Authentication Error In Flutter - The sms code has expired. Please re-send the verification code to try again

I implemented Firebase phone authentication (OTP Login) successfully and it was working fine. However, I had to change my keystore alias and generate a new keystore to update the app in Play Store.
Since then, I am facing this issue where it says the otp is wrong every time. Even when I copy-paste the otp from message, it throws the error every time. It is also behaving weird, for some phone numbers, the otp works successfully but for some others, it doesn't. Any little help will be appreciated a lot.
It is important to note that OTP Authentication is working fine in emulators but the error shows up when installed in a real device.
Here's my code snippet below -
String verficationIdRecieved = "";
int? _resendToken;
void verifyNumber() {
auth.verifyPhoneNumber(
phoneNumber: "+880" + phoneNoController.text,
verificationCompleted: (PhoneAuthCredential credential) async {
await auth.signInWithCredential(credential).then((value) async {
print("verficationCompleted : Logged in");
// Get.offAll(() => OtpVerifyCodeScreen);
});
},
verificationFailed: (FirebaseAuthException exception) {
LoadingDialog().dismiss();
print(exception.message);
getPrefix.Get.snackbar("Error verifyNumber",
"Please check if your phone number is right. ${exception.message}");
},
codeSent: (String verficationId, int? resendToken) async {
verficationIdRecieved = verficationId;
_resendToken = resendToken;
otpCodeVisible = true;
setState(() {
timedButtonActtive = true;
});
LoadingDialog().dismiss();
},
timeout: Duration(seconds: 60),
forceResendingToken: _resendToken,
codeAutoRetrievalTimeout: (String verificationId) {
// ADDED LATER https://stackoverflow.com/questions/61132218/resend-otp-code-firebase-phone-authentication-in-flutter
// REMOVE IF ERROR
verficationIdRecieved = verificationId;
},
);
}
var firebaseToken;
void verifyCode(BuildContext context) async {
LoadingDialog().show(context);
PhoneAuthCredential credential = PhoneAuthProvider.credential(
verificationId: verficationIdRecieved, smsCode: codeController.text);
try {
await auth.signInWithCredential(credential).then((value) {
// print("\n\n User UID should be ${value.user!.uid}");
print("verifyCode : Login successfull");
value.user!.getIdToken().then((value) => {
firebaseToken = value,
print("This is the firebase token == $firebaseToken"),
verifyOtp(
mobileNo: phoneNoController.text,
otp: codeController.text,
ftoken: firebaseToken)
});
final snackBar = SnackBar(content: Text("Login Successful"));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
// getPrefix.Get.offAll(() => SetUsernameScreen());
});
} catch (e) {
final snackBar = SnackBar(
content: Text(
"The sms code has expired or you entered a wrong code. Please re-send the code to try again."));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
I think I know your problem.
Android automatically confirms the user as soon as it receives the code before the user clicks the confirmation button.
Saying that you do not have this problem in the emulator is why you receive the code on the real phone.
If you want to manually verify the user, you have two options:
One is to set the timeout to 5 seconds. (So ​​that Android can not automatically verify the user).
The second solution is not to do anything in the verificationCompleted method because now Android is using this method to confirm the user. (My suggestion is to use this way.)
I hope this will solve your problem.
void verifyNumber() {
auth.verifyPhoneNumber(
phoneNumber: "+880" + phoneNoController.text,
verificationCompleted: (PhoneAuthCredential credential) async {
// await auth.signInWithCredential(credential).then((value) async {
// print("verficationCompleted : Logged in"); //Here
// // Get.offAll(() => OtpVerifyCodeScreen);
// });
},
verificationFailed: (FirebaseAuthException exception) {
LoadingDialog().dismiss();
print(exception.message);
getPrefix.Get.snackbar("Error verifyNumber",
"Please check if your phone number is right. ${exception.message}");
},
codeSent: (String verficationId, int? resendToken) async {
verficationIdRecieved = verficationId;
_resendToken = resendToken;
otpCodeVisible = true;
setState(() {
timedButtonActtive = true;
});
LoadingDialog().dismiss();
},
timeout: Duration(seconds: 60),
forceResendingToken: _resendToken,
codeAutoRetrievalTimeout: (String verificationId) {
// ADDED LATER https://stackoverflow.com/questions/61132218/resend-otp-code-firebase-phone-authentication-in-flutter
// REMOVE IF ERROR
verficationIdRecieved = verificationId;
},
);
}