How to do Phone Authentication in Flutter using Firebase? - firebase

I have searched many websites and I didn't find a way to implement phone authentication in Flutter using Firebase. Can anyone tell me how to this?

Well Documented Working Demo project here
Below is the detailed procedure
Steps
Ask for user's phoneNumber
Get OTP from Firebase
SignIn to Firebase
Rules
SignIn/Login is done in the same way.
The OTP is only used to get AuthCrendential object
AuthCredential object is the only thing that is used to signIn the user.
It is obtained either from verificationCompleted callback function in verifyPhoneNumber or from the PhoneAuthProvider.
(Don't worry if it's confusing, keep reading, you'll get it)
Workflow
User gives the phoneNumber
Firebase sends OTP
SignIn the user
If the SIM card with the phoneNumber is not in the device that is currently running the app,
We have to first ask the OTP and get AuthCredential object
Next we can use that AuthCredential to signIn
This method works even if the phoneNumber is in the device
Else if user provided SIM phoneNumber is in the device running the app,
We can signIn without the OTP.
because the verificationCompleted callback from submitPhoneNumber function gives the AuthCredential object which is needed to signIn the user
But in the previous case, it ain't called because the SIM is not in the phone.
Functions
SubmitPhoneNumber
Future<void> _submitPhoneNumber() async {
/// NOTE: Either append your phone number country code or add in the code itself
/// Since I'm in India we use "+91 " as prefix `phoneNumber`
String phoneNumber = "+91 " + _phoneNumberController.text.toString().trim();
print(phoneNumber);
/// The below functions are the callbacks, separated so as to make code more readable
void verificationCompleted(AuthCredential phoneAuthCredential) {
print('verificationCompleted');
...
this._phoneAuthCredential = phoneAuthCredential;
print(phoneAuthCredential);
}
void verificationFailed(AuthException error) {
...
print(error);
}
void codeSent(String verificationId, [int code]) {
...
print('codeSent');
}
void codeAutoRetrievalTimeout(String verificationId) {
...
print('codeAutoRetrievalTimeout');
}
await FirebaseAuth.instance.verifyPhoneNumber(
/// Make sure to prefix with your country code
phoneNumber: phoneNumber,
/// `seconds` didn't work. The underlying implementation code only reads in `milliseconds`
timeout: Duration(milliseconds: 10000),
/// If the SIM (with phoneNumber) is in the current device this function is called.
/// This function gives `AuthCredential`. Moreover `login` function can be called from this callback
verificationCompleted: verificationCompleted,
/// Called when the verification is failed
verificationFailed: verificationFailed,
/// This is called after the OTP is sent. Gives a `verificationId` and `code`
codeSent: codeSent,
/// After automatic code retrival `tmeout` this function is called
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout,
); // All the callbacks are above
}
SubmitOTP
void _submitOTP() {
/// get the `smsCode` from the user
String smsCode = _otpController.text.toString().trim();
/// when used different phoneNumber other than the current (running) device
/// we need to use OTP to get `phoneAuthCredential` which is inturn used to signIn/login
this._phoneAuthCredential = PhoneAuthProvider.getCredential(
verificationId: this._verificationId, smsCode: smsCode);
_login();
}
SignIn/LogIn
Future<void> _login() async {
/// This method is used to login the user
/// `AuthCredential`(`_phoneAuthCredential`) is needed for the signIn method
/// After the signIn method from `AuthResult` we can get `FirebaserUser`(`_firebaseUser`)
try {
await FirebaseAuth.instance
.signInWithCredential(this._phoneAuthCredential)
.then((AuthResult authRes) {
_firebaseUser = authRes.user;
print(_firebaseUser.toString());
});
...
} catch (e) {
...
print(e.toString());
}
}
Logout
Future<void> _logout() async {
/// Method to Logout the `FirebaseUser` (`_firebaseUser`)
try {
// signout code
await FirebaseAuth.instance.signOut();
_firebaseUser = null;
...
} catch (e) {
...
print(e.toString());
}
}
For more details on implementation please refer to the lib/main.dart file here.
If you found issues, edits are welcome to this answer and to this repo README

Currently _signInPhoneNumber is deprecated, so use this:
try {
AuthCredentialauthCredential = PhoneAuthProvider.getCredential(verificationId: verificationId, verificationsCode: smsCode);
await _firebaseAuth
.signInWithCredential(authCredential)
.then((FirebaseUser user) async {
final FirebaseUser currentUser = await _firebaseAuth.currentUser();
assert(user.uid == currentUser.uid);
print('signed in with phone number successful: user -> $user');
}
}

Below are the steps:-
Get OTP based on Phone Number:-
void sendOTP(String phoneNumber, PhoneCodeSent codeSent,
PhoneVerificationFailed verificationFailed) {
if (!phoneNumber.contains('+')) phoneNumber = '+91' + phoneNumber;
_firebaseAuth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: Duration(seconds: 30),
verificationCompleted: null,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: null); }
Use the codeSent function to retrieve verification_id and pass it to OTP screen
void codeSent(String verificationId, [int forceResendingToken]) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Otp(
phoneNumber: _phoneNumber,
verificationId: verificationId,
)));
}
Get user based on verification_id and OTP
Future<bool> verifyOTP(String verificationId, String otp) async {
AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: otp,
);
AuthResult result;
try {
result = await _firebaseAuth.signInWithCredential(credential);
} catch (e) {
// throw e;
return false;
}
print(result);
(await result.user.getIdToken()).claims.forEach((k, v) {
print('k= $k and v= $v');
});
if (result.user.uid != null) return true;
return false;
}
For more details watch the below video
https://youtu.be/e5M3EwJJYS4

I had the same issue but I was building an Ionic app.
you can do firebase phone auth on the web.
the idea is:
take the user's phone number and send to a webpage.
create a recaptchaVerifier
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
sign in the user with phone number:
firebase.auth().signInWithPhoneNumber(phoneNumber, recaptchaVerifier)
.then(confirmationResult => {
myVerificationId = confirmationResult.verificationId;
})
.catch(error => {
console.log('error', error);
})
send the confirmation id back to the flutter app through a deeplink
Firebase will send the sms code.
sign in the user with any method that takes the confirmation Id and
the code as parameters. for the web it goes like this:
let signinCredintial = firebase.auth.PhoneAuthProvider.credential(this.verificationId, this.code);
firebase.auth().signInWithCredential(signinCredintial)
.then(response => {
// user is signed in
})

I have implemented the phone auth singnIn with firebase in flutter its quite easy just import the firebase_auth library and validate the phone number is in proper format i.e it has a "+" sign in the beginning followed by country code then the phone number then the code goes like this
if (phoneExp.hasMatch(phon))
{
final PhoneVerificationCompleted verificationCompleted=(FirebaseUser user){
setState(() {
_message=Future<String>.value("auto sign in succedded $user");
debugPrint("Sign up succedded");
_pref.setString("phonkey",user.phoneNumber.toString());
MyNavigator.goToDetail(context);
//called when the otp is variefied automatically
});
};
final PhoneVerificationFailed verificationFailed=(AuthException authException){
setState(() {
_message=Future<String>.value("verification failed code: ${authException.code}. Message: ${authException.message}");
});
};
final PhoneCodeSent codeSent=(String verificationId,[int forceResendingToken]) async {
this.verificationId=verificationId;
};
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout = (String verificationId){
this.verificationId=verificationId;
};
await _auth.verifyPhoneNumber(
phoneNumber: phon,
timeout: Duration(seconds: 60),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout
);
}
and if the phone wasn't able to detect the otp sent automatically, get the otp in a string and implement this function
void _signInWithOtp() async{
final FirebaseUser user = await _auth.signInWithPhoneNumber(
verificationId: verificationId,
smsCode: _otpController.text,
);

Use the flutter_otp package to send SMS with OTP easily. The advantages are:
You can send custom OTP messages.
OTP can be generated in any range.
Example of using it :
import 'package:flutter_otp/flutter_otp.dart';
...
FlutterOtp myOtpObj = FlutterOtp();
myOtpObj.sendOtp('7975235555');
...
// you can check as follows
bool correctOrNot = myOtpObj.resultChecker(<OTP entered by user>);
Check out the flutter_otp repository here

Related

Flutter firebase phonenumberauth how to get code

I use Firebase login to let the user use Facebook, but I also need to save the phone number using otp. So I do it as follows:
sendOtp() async {
print('otp call');
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: '+923323789263',
verificationCompleted: (PhoneAuthCredential credential) {},
verificationFailed: (FirebaseAuthException e) {},
codeSent: (String verificationId, int code) {
},
codeAutoRetrievalTimeout: (String verificationId) {},
);
}
OTP Code is going to the number but I need that code so I can match it with user input.
I have entered user otp to verify also like this
AuthCredential credential = PhoneAuthProvider.credential(
verificationId: verificationIdd,
smsCode: textEditingController.text.trim());
print(credential);
FirebaseAuth.instance.signInWithCredential(credential);
final User user = FirebaseAuth.instance.currentUser;
But i don't want to signin I need to just check that the use enter correct otp or not. I try to print credential but its showing same response for correct and wrong otp which is this
AuthCredential(providerId: phone, signInMethod: phone, token: null)
And it's not showing anything from which i can guess otp is correct or not.

Flutter + Firebase - Link Phone Number with Existing Account

I have implemented Email Authentication using Firebase in Flutter. I want to take the phone number from the user and verify it by sending an OTP to ensure it belongs to them and then link their phone number with the account they created earlier using email.
For this purpose I did the following:
verifyPhoneNumber() async {
await auth.verifyPhoneNumber(
phoneNumber: '+92${widget.phoneNumber}',
verificationCompleted: (PhoneAuthCredential credential) async {
await auth.currentUser.updatePhoneNumber(credential);
// either this occurs or the user needs to manually enter the SMS code
},
verificationFailed: (FirebaseAuthException e) {
print(e.message);
},
codeSent: (String verificationId, int resendToken) {
setState(() {
_otpcode = verificationId;
});
},
codeAutoRetrievalTimeout: (String verificationId) {
setState(() {
_otpcode = verificationId;
});
},
);
}
The above code sends the OTP to the number. Now when the user enters the received code into the TextField I want to verify that both match so I can proceed with updatePhoneNumber.
I am not sure how to implement the verification part. I have the following code and not sure how to output the errors if any or to confirm the verification did happen.
verifyPhoneCode(otp, code) async {
final AuthCredential credential =
PhoneAuthProvider.credential(verificationId: otp, smsCode: code);
await auth.currentUser.updatePhoneNumber(credential);
}
Ok I have finally figured out the solution. In order to link phone number with an already existing account use .linkWithCredential
final AuthCredential credential =
PhoneAuthProvider.credential(
verificationId: _otpcode,
smsCode: val,
);
currentUser
.linkWithCredential(credential);
I hope it helps anyone looking for a solution to a similar problem.
I recommend you to go through the official firebase flutter docs. There they explained all the steps involved during the phone authentication step in a well mannered way.
Moreover, here is the code snippet from official docs which refers to the phone verification steps.
FirebaseAuth auth = FirebaseAuth.instance;
await auth.verifyPhoneNumber(
phoneNumber: '+44 7123 123 456',
codeSent: (String verificationId, int resendToken) async {
// Update the UI - wait for the user to enter the SMS code
String smsCode = 'xxxx';
// Create a PhoneAuthCredential with the code
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.credential(verificationId: verificationId, smsCode: smsCode);
// Sign the user in (or link) with the credential
await auth.signInWithCredential(phoneAuthCredential);
},
);

Flutter new phone authentication now do "not a robot" check in seperate browser which make entire procedure slow

I solved this issue (App crashes upon phone authentication after changing package name - Flutter) of app crash by adding implementation "androidx.browser:browser:1.2.0" into app/build.gradle dependencies.
But NOW whole phone authentication procedure got changed. Now app open a browser to do Not a robot test. But I don't want app to open a browser just to verify it's not a robot it make entire process slow and ugly. Below is the video example. How to get rid of this issue? It shows app firebase address in the browser link too.
Video example of issue is below
https://drive.google.com/file/d/1G7noQWyyAHvyTo_Te0v6d2O3IvaiClAw/view?usp=sharing
Below is the code snippet of verifyPhone function.
Future<dynamic> verifyPhone(phoneNo, BuildContext context) async {
var completer = Completer<dynamic>();
dynamic newUserResult;
Future<String> getOTPresult() async {
print("Dialog shown");
await showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: 270,
child: OTPBottomSheet(controller: _otpController),
),
);
return _otpController.text;
}
// >>>>>>>>>>>>> On Complete
final PhoneVerificationCompleted verificationComplete =
(AuthCredential authCred) async {
print(" I N S I D E C O M P L E T E ");
newUserResult = await signInWithPhoneNumber(authCred);
completer.complete(newUserResult);
};
// >>>>>>>>>>>>> On Timeout
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verID) {
print("\n2. Auto retrieval time out");
completer.complete(newUserResult);
};
// >>>>>>>>>>>>> On manual code verification
final PhoneCodeSent smsCodeSent =
(String verID, [int forceCodeResend]) async {
print(" I N S I D E C O D E S E N T");
var OTPDialogResult = await getOTPresult();
if (OTPDialogResult != null) {
AuthCredential authCred = PhoneAuthProvider.credential(
verificationId: verID, smsCode: OTPDialogResult);
newUserResult = AuthService().signInWithPhoneNumber(authCred);
if (!completer.isCompleted) {
completer.complete(newUserResult);
}
}
};
// >>>>>>>>>>>>> On Ver failed
final PhoneVerificationFailed verificationFailed =
(Exception authException) {
completer.complete(newUserResult);
};
await FirebaseAuth.instance
.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: Duration(seconds: 50),
verificationCompleted: verificationComplete,
verificationFailed: verificationFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
).catchError((error) {
print(error.toString());
});
print("New user result at the end before await: " + newUserResult.toString());
newUserResult = await completer.future;
print("New user result at the end after await: " + newUserResult.toString());
return newUserResult;
}
signInWithPhoneNumber function
Future signInWithPhoneNumber(AuthCredential authCreds) async {
try {
UserCredential result = await FirebaseAuth.instance.signInWithCredential(authCreds);
User customUser = result.user;
return _userFormFirebaseUser(customUser).getuid;
}
CustData _userFormFirebaseUser(User user) {
print("----> Inside _userFormFirebaseUser and user ID: " + user.uid);
return user != null
? CustData(
custId: user.uid,
)
: null;
}
// --- CustData model class
class CustData {
String custId;
String custName;
String custPhNo;
String custContactNO;
DateTime custDateOfBirth;
Map<String, dynamic> address;
String cartID;
CustData({
this.custId,
this.custName,
this.custPhNo,
this.custDateOfBirth,
this.address,
this.cartID,
this.custContactNO,
});
CustData.initial() : custId = '';
String get getuid => this.custId;
}
Already making the whole procedure slow...
Try this Auth Version and it will work well for you :)
implementation com.google.firebase:firebase-auth:19.3.1
The reason behind this is explained in the official documentation https://firebase.google.com/docs/auth/android/phone-auth#enable-app-verification
reCAPTCHA verification: In the event that SafetyNet cannot be used, such as when the user does not have Google Play Services support, or when testing your app on an emulator, Firebase Authentication uses a reCAPTCHA verification to complete the phone sign-in flow. The reCAPTCHA challenge can often be completed without the user having to solve anything. Please note that this flow requires that a SHA-1 is associated with your application.
To solve it you have to :
In the Google Cloud Console, enable the Android DeviceCheck API for your project. The default Firebase API Key will be used, and needs to be allowed to access the DeviceCheck API.
If you haven't yet specified your app's SHA-256 fingerprint, do so from the Settings Page of the Firebase console. Refer to Authenticating Your Client for details on how to get your app's SHA-256 fingerprint.

Firebase not sending OTP to the phone number in device

I have implemented firebase otp in my flutter application
When I install the app on my device and requested for the same number on that deice it does not send me the message. But when I change the number it send the message
So I changed the device and tried to send the previous number, it sent message successfully. Then I used the number on second device and it did not send the message
* When I use the same number on the device it does not send the OTP
Here is the code for sending the otp
Future<void> verifyPhone() async {
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verID) {
this.verificationId = verID;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]) {
this.verificationId = verId;
};
final PhoneVerificationCompleted verifiedSuccess =
(AuthCredential phoneAuthCredential) {
print('verified');
};
final PhoneVerificationFailed verifyFailed = (AuthException exception) {
print('${exception.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: widget.phone,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: verifyFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve);
log("OTP sent");
}
This is the code for verifying the OTP
signIn() async{
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: enteredOtp,
);
await FirebaseAuth.instance.signInWithCredential(credential).then((user) {
//SOME CODE HERE
}).catchError((e) {
showAlert(
context: context,
title: "Empty or Invalid OTP",
);
log("Invalid OTP");
});
}
What's wrong in here? How do I fix this and get the otp for the same number in device.
Actually in point of fact, I believe that's actually expected, and intended usage. (Or close to it) as if Firebase can confirm phone identity via push notifications, it doesn't even send the text, as it's already verified. (Usually only happens if it's verified the phone once). But if you're sending it to a phone number other than the phone number the device is connecting to, then it can't use the previous verificationID in the system.
So I would suggest checking to see which parts of the code function and not. Check to see if the code and authorization fails, or if it will continue to the next part of the program fully authorized. Add some print statements in there just to make sure. But I'm pretty sure that should be the way it works.

How to signup user via phone number using firebase in flutter?

I am trying to create a login and sign up system using firebase in my flutter app.
Primary login will be based on the Phone number.
-For signup I intend to require:
Phoone number(required)
Name
State and city
On, submit I will send an OTP to the phone number to verify and successfully create a profile of the user.
-For login I will require that phone number, the OTP to phone number will be send and then successfully login.
I have seen the email and password-based login and signup with the following:
createUserWithEmailAndPassword()
signInWithEmailAndPassword()
-Currently, I have found verifyPhoneNumber() But, don't know how signup will be done. Also, how will it verify when signup hasn't happened?
Please direct me accordingly.
Edit:
I am not using Firestore to store any data. For that, I have the backend to receive data using auth token sent in the header.
You can first enable phone number authentication from firebase console.
After you do that, suppose you have have a phone number collected from a form. Below code can achieve auth for android. For iOS, please see docs.
FirebaseAuth auth = FirebaseAuth.instance;
await auth.verifyPhoneNumber(
phoneNumber: '+91 999 999 999',
verificationCompleted: (PhoneAuthCredential credential) async {
await auth.signInWithCredential(credential);
},
);
Later, you can create a firebase collection that will contain Name, state, and city after you request it from user through another form. There is nothing like signup here, the authentication is phone number based, if user is new you have request these details, else it is a sign in.
try firebase_auth 0.15.3
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
demo
void _verifyPhoneNumber() async {
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential phoneAuthCredential) {
_auth.signInWithCredential(phoneAuthCredential);
setState(() {
_message = 'Received phone auth credential: $phoneAuthCredential';
});
};
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
setState(() {
_message =
'Phone number verification failed. Code: ${authException.code}. Message: ${authException.message}';
});
};
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
widget._scaffold.showSnackBar(const SnackBar(
content: Text('Please check your phone for the verification code.'),
));
_verificationId = verificationId;
};
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
_verificationId = verificationId;
};
await _auth.verifyPhoneNumber(
phoneNumber: //put here phone number,
timeout: const Duration(seconds: 5),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
}
hope it helps

Resources