Firebase Phone auth is not verifying the OTP entered - firebase

I am trying to integrate phone authentication system in my flutter app. But even when I enter a wrong OTP the user gets verified and enters into the next page.
I am using Dialog box to ask for OTP
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter sms Code'),
content: TextFormField(
controller: _smsController,
keyboardType: TextInputType.number,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
hintText: 'Enter OTP', icon: Icon(Icons.perm_phone_msg)),
maxLength: 6,
maxLengthEnforced: true,
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
new RaisedButton(
child: Text('Login'),
textColor: Colors.white,
onPressed: () {
_signInWithPhoneNumber(context);
})
],
);
});```
```void _signInWithPhoneNumber(BuildContext context) async {
final AuthCredential credential = await PhoneAuthProvider.getCredential(
verificationId: _verificationId,
smsCode: _smsController.text,
);
await _auth
.signInWithCredential(credential)
.then((FirebaseUser user) async {
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InfoScreen(_phoneNumberController.text)));
}).catchError((e) {
print(e.message);
Navigator.of(context).pop();
});
}
}```

You have to get same of auth to verify a OTP of firebase admin as explained here.

You have done the mistake with the PhoneAuthProvider
final AuthCredential credential = await PhoneAuthProvider.getCredential(
verificationId: _verificationId,
smsCode: _smsController.text,
);
Replace above code with this:
FirebaseAuth.instance
.signInWithPhoneNumber(verificationId: verificationId, smsCode: smsCode)
.then((user) {
Navigator.of(context).pushReplacementNamed('/homepage');
}).catchError((e) {
print(e);
});
Using this you can verify the phone with code. For more details please visit Here

I think you have to add an onCompleteListener by changing the ".then(FirebaseUser ...)" to ".addOnCompleteListener(this, new OnCompleteListener(AuthResult res) {});"
The AuthResult object has a boolean "isSuccessful()" which you can check to make sure the user entered the correct code.
I assume that Firebase returns the FirebaseUser object no matter if the code actually matches.

Related

Flutter : Firebase PhoneAuthentication Problem

I have just developed an app which requires phone authentication. Inside login screen I can able to achieve to login via phone. But my concern is : for the first time when I enter phone number and enter verification number it comes back to login which in reality expected to navigate to homescreen. For the second try system is able to work and navigate to home screen as expected. Here is my code block. I am wondering which part of the code I make mistake since login info pop back again and system is able to navigate to home screen after second try:
My code block :
class _LoginScreenState extends State<LoginScreen> {
String phoneNo, smssent, verificationId;
get verifiedSuccess => null;
Future<void> verifyPhone() async {
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId) {
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResent]) {
this.verificationId = verId;
smsCodeDialoge(context).then((value) {
print("Doğrulama Kodu Gönderildi");
});
};
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential auth) {};
final PhoneVerificationFailed verifyFailed = (AuthException e) {
print('${e.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: verifyFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
);
}
Future<bool> smsCodeDialoge(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Doğrulama Kodunu Giriniz'),
content: TextField(
onChanged: (value) {
this.smssent = value;
},
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
FlatButton(
onPressed: () {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
} else {
Navigator.of(context).pop();
signIn(smssent);
}
});
},
child: Text(
'Doğrulama Yap',
style: TextStyle(color: Colors.blue),
),
),
],
);
});
}
Future<void> signIn(String smsCode) async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
await FirebaseAuth.instance.signInWithCredential(credential).then((user)
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(),
),
);
}).catchError((e) {
print(e);
});
}
if (user != null) {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
Here you are calling pop, which will take you to the previous screen if the user is not null, and after that, you are pushing the HomeScreen into the stack. Try not poping and just pushing, by the way, are you using routes and streamprovider in your code?

Flutter FirebaseAuth not sending verification code to phone

I'm trying to add a feature on my app that allows users to verify their phone number by sending a code to their phone numbers. I found that I can do that by using firebase. This is how I send verification code:
Future<void> verifyPhone() async {
void verificationCompleted(AuthCredential phoneAuthCredential) {
print('verificationCompleted $phoneAuthCredential');
_phoneAuthCredential = phoneAuthCredential;
_db.updateUserField(userUid, {
'isNumberConfirmed': true,
'linkedAccounts': FieldValue.arrayUnion(['P-$phoneAuthCredential'])
});
currentUser.isNumberConfirmed = true;
currentUser.linkedAccounts.add('P-$phoneAuthCredential');
worked = true;
}
void verificationFailed(FirebaseAuthException error) {
worked = false;
}
void codeSent(String verificationId, [int code]) {
_code = code;
_verificationId = verificationId;
print('Code: $code\t_verificationId: $_verificationId');
}
void codeAutoRetrievalTimeout(String verificationId) {
worked = false;
}
_auth.verifyPhoneNumber(
phoneNumber: '+1${currentUser.phone}',
timeout: Duration(milliseconds: 60000),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
}
but whenever this method get called, I get the following messages and no verification code is sent:
I/BiChannelGoogleApi(25924): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzao#bafccce
W/DynamiteModule(25924): Local module descriptor class for com.google.firebase.auth not found.
I/FirebaseAuth(25924): [FirebaseAuth:] Preparing to create service connection to gms implementation
I'm not sure why it is not sending the code. Am I missing something?
I find your code hard to debug so I am sharing this :
First make sure you have enabled phone sign in method in firebase console ,
and added Firebase Authentication plugin and Firebase core plugin in pubspec.yaml
define your verify phone number button like this :
onPressed: (){
//code for sign in
final mobile = _phoneTextBoxController.text.trim();
registerUser(mobile, context);
}
Now define whole verifying process like this :
Future registerUser(String mobile, BuildContext context) async{
FirebaseAuth _auth = FirebaseAuth.instance;
_auth.verifyPhoneNumber(
phoneNumber: mobile,
timeout: Duration(seconds: 60),
verificationCompleted: (AuthCredential authCredential){
_auth.signInWithCredential(_credential).then((AuthResult result){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
verificationFailed: (AuthException authException){
print(authException.message);
},
codeSent:(String verificationId, [int forceResendingToken]){
//show dialog to take input from the user
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text("Enter SMS Code"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _codeController,
),
],
),
actions: <Widget>[
FlatButton(
child: Text("Done"),
textColor: Colors.white,
color: Colors.redAccent,
onPressed: () {
FirebaseAuth auth = FirebaseAuth.instance;
smsCode = _codeController.text.trim();
_credential = PhoneAuthProvider.getCredential(verificationId: verificationId, smsCode: smsCode);
auth.signInWithCredential(_credential).then((AuthResult result){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
)
],
)
);
},
codeAutoRetrievalTimeout: (String verificationId){
verificationId = verificationId;
print(verificationId);
print("Timout");
}
);
So what above code does ? First it sends the OTP code to user and try to sign in using this code :
verificationCompleted: (AuthCredential authCredential){
_auth.signInWithCredential(_credential).then((AuthResult result){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
and pushes user to the home screen , you can change above code accordingly to match your app home screen . If auto retrieval fails then the codeSent : shows user a showDialog to manually enter the code . For more information visit this medium article and for complete code visit this github repository

How to make register using email/password and send Code to the phone in Firebase

I want to allow users to register an account on my app using flutter with email/password, and then send code to the phone. So when user enters their data to Sign up, it should register them with their email and then use their phone to the verity code. Also the user can't complete the registration and go to the Home page Without checking the entered code.
But in my code it's not working what i want do it.
My click function:
dynamic ruselt= await _auth.regsiterwithemail(_emailcontroller.text,_passwordcontroller.text);
if (ruselt!=null){
setState(() async {
loading =false;
verfitycode().verfityphoen(context);
});
}else{
setState(() {
loading=false;
SweetAlert.show(context,
title: "Title",
style: SweetAlertStyle.confirm,
subtitle: "Subtitle");
});
}
My registration function:
Future regsiterwithemail(String email , String password )
async {
try{
AuthResult result =await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user =result.user;
print('oky');
return _userfirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
class for verity code
class verfitycode {
String phonenumber;
String smsCode;
String vialdid;
GlobalKey<FormState>_form;
Future<void> verfityphoen(BuildContext context)async{
final PhoneCodeAutoRetrievalTimeout AutoRetriv =(String verid) {
this.vialdid =verid;
};
final PhoneCodeSent smsCodeset =(String verid,[int forceResendingToken]){
this.vialdid=verid;
smscodeDialog(context);
};
final PhoneVerificationCompleted verfiedcompletd=(AuthCredential user){
print('verfild');
};
final PhoneVerificationFailed verfilederror =(AuthException exception){
print('${exception.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber:"+967776523152",
timeout: const Duration(seconds:5),
verificationCompleted: verfiedcompletd,
verificationFailed: verfilederror,
codeSent: smsCodeset,
codeAutoRetrievalTimeout: AutoRetriv
);
}
Future<bool> smscodeDialog(BuildContext context){
Alert(
context: context,
title: "رمز التحقق",
content: Column(
children: <Widget>[
Form(
key: _form,
child: Directionality(
textDirection: TextDirection.rtl,
child:
TextField(
onChanged: (val){
this.smsCode=val;
},
decoration:InputDecoration (
icon: Icon(Icons.supervisor_account),
labelText: 'ادخل رمز التحقق',
),
),
)
)
],
),
buttons: [
DialogButton(
child: Text('خروج', style: TextStyle(color: Colors.white, fontSize:18)), onPressed:()=>Navigator.pop(context)),
DialogButton(
onPressed: (){
// _form.currentState.validate();
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.pop(context);
Navigator.push(context,
MaterialPageRoute(builder: (context) => Home()));
} else {
Navigator.pop(context);
_testSignlink();
}
});
},
child: Text(
"ادخل الرمز",
style: TextStyle(color: Colors.white, fontSize:18),
),
),
]).show();
}
_testSignlink() async {
FirebaseUser user;
String _smsCodeController;
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: vialdid,
smsCode: smsCode,
);
await user.linkWithCredential(credential).then((user){
print(user.user.uid);
}).catchError((onError){
print(onError.toString());
});
_smsCodeController = '';
return 'signInWithPhoneNumber succeeded: $user';
}
}

Flutter Firebase Auth: A network error (such as timeout, interrupted connection or unreachable host) has occurred

I am trying to integrate authentication on Flutter using firebase_auth.
However, whenever I call the verifyPhoneNumber("+256XXXXXXXXXX") I get the error message A network error (such as timeout, interrupted connection or unreachable host) has occurred., that is from the PhoneVerificationFailed callback. An for that reason cannot get the SMS.
I have tried;
Adding network permissions as seen below to my file (my internet connection works, as I am able to Google via the emulator)
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
Checking the validity of my API keys
I am totally confused as to why Flutter is unable to communicate with firebase. I have two questions.
How can I eliminate this error?
What other cases might cause this error besides a lacking internet connection?
My implimentatioin is as below;
import 'package:firebase_auth/firebase_auth.dart';
FirebaseAuth auth = FirebaseAuth.instance;
var message;
// fire this when Phone verification is completed
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential phoneAuthCredential) {
auth.signInWithCredential(phoneAuthCredential);
message = 'Received phone auth credential: $phoneAuthCredential';
print(message);
};
// fire this when Phone verification fails
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
message =
'Phone verification failed. Code: ${authException.code}. Message: ${authException.message}';
print(message);
};
// fire this when SMS code is sent is sent.
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
verificationId = verificationId;
print('Sent verification code');
};
// fire this when smsCode expires
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
verificationId = verificationId;
print('Auto retrival time-out');
};
// verify phone number
verifyPhoneNumber(String phoneNumber) {
auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(seconds: 30),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
print('Verification Initiated');
}
// sign in with phone.
signInWithPhoneNumber(String smsCode, String verificationId) async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
final FirebaseUser user = (await auth.signInWithCredential(credential)).user;
final FirebaseUser currentUser = await auth.currentUser();
assert(user.uid == currentUser.uid);
if (user != null) {
message = 'Successfully signed in, uid: ' + user.uid;
} else {
message = 'Sign in failed';
}
}
In my case I solved allowing Outgoing Connections on the Xcode's Runner.xcworkspace
In my case, my VPN was causing the problem. Disabling the VPN and testing it again solved the error. I hope it helps.
if you are trying this out in a simulator, then this error may pop up as the internet of your simulator is not connected. To solve this you can run your app on a physical device and it will work!
I am late to the party but I found another solution.
If using an android emulator, disabling the Android Studio HTTP proxy settings can restore the network connection.
Normally when the emulator is doing a lot of work on the thread, it tends to misbehave, such a losing internet connectivity even if your PC is well connected.
My suggestion(Which worked for me) is you kill the emulator, and go to Android Studio AVD Manager and Wipe Data for that Emulator, then restart the Emulator, it worked for me.
Kindly find below the image screenshot
"I am late to the party but I found another solution.
If using an android emulator, disabling the Android Studio HTTP proxy settings can restore the network connection." - MaximeBeasse
This network error has stopped me too and I have been seeking an answer ... After reading MaximeBeasse's response above and trying it out, I found that it worked : ) Authentication now works! Finally I can move on!
Many thanks #MaximeBeasse -- salute his answer please
Open Terminal in the project folder, run the following two commands:
adb kill-server
adb start-server
If adb is not recognized, add C:\Users\USERNAME\AppData\Local\Android\Sdk\platform-tools to environement variables.
import 'package:badam/varify.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:async';
import 'HomePage.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'FireBase Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new LoginPage(),
routes: <String, WidgetBuilder>{
'/loginpage' : (BuildContext context) => Dash(),
'/landpage' : (BuildContext context) => LoginPage(),
}
);
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String phoneNo, smsId, verificationId;
Future<void> verifyPhone() async{
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId){
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]){
this.verificationId = verId;
smsCodeDialoge(context).then((value){
print('Signed In');
});
};
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential auth){
print('verified');
};
final PhoneVerificationFailed verifyFailed = (AuthException e) {
print('${e.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: verifyFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
);
}
Future<bool> smsCodeDialoge(BuildContext context){
return showDialog(context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter OTP'),
content: TextField(
onChanged: (value) {
this.smsId = value;
},
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
onPressed: (){
FirebaseAuth.instance.currentUser().then((user){
if(user != null){
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Dash()),
);
}
else{
Navigator.of(context).pop();
signIn(smsId);
}
}
);
},
child: Text('Done', style: TextStyle( color: Colors.blue),))
],
);
},
);
}
Future<void> signIn(String smsCode) async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
await FirebaseAuth.instance.signInWithCredential(credential)
.then((user){
Navigator.of(context).pushReplacementNamed('/loginpage');
}).catchError((e){
print(e);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sign In')
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Phone Auth',style: TextStyle(fontSize: 20,color: Colors.blue),),
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
decoration: InputDecoration(
hintText: 'Enter your phone number',
),
onChanged: (value){
this.phoneNo = value;
},
),
),
SizedBox(height: 10.0),
RaisedButton(
onPressed: verifyPhone,
child: Text('Verify', style: TextStyle(color: Colors.white),),
elevation: 7.0,
color: Colors.blue,
)
],
),
);
}
}
It worked for me, hope this helps you!

Flutter user registration storing data

I'm new to flutter. I need some help with Registering users with my app. I'm using Firebase authentication just to get the email and I need to save the details of the users in a database. I'm trying with cloud firestore, but I'm feeling like its not the best ideal option. And when login I want verify if the user is registered or not
you can use this code for registration user in Cloud Firestore
final FirebaseAuth _auth = FirebaseAuth.instance;
_auth
.createUserWithEmailAndPassword(
email: _myEmail, password: _myPassword)
.catchError((e) {
showDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text('Error Occured'),
content: Text(e.toString()),
actions: <Widget>[
CupertinoButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop())
],
);
});
FirebaseAuth.instance
.signInWithEmailAndPassword(email: _myEmail, password: _myPassword)
.catchError((e) {
showDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text('Error Occured'),
content: Text(e.toString()),
actions: <Widget>[
CupertinoButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop())
],
);
});

Resources