Firebase not sending OTP to the phone number in device - firebase

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.

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);
},
);

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

How do I update a FirebaseUser's phone number in firebase_auth?

In my Flutter app I use Firebase's phone number authentication as my main form of authentication. After authenticating, I create a user in my users collection with these details:
{
phoneNumber: FirebaseAuth.instance.currentUser().phoneNumber,
displayName: 'Comes from user textbox',
...
}
But say one day a user want's to change their phone number. How do I do this? Because I cannot simply change the user's phone number in the document, because the phone number needs to be authenticated. And after authentication, the user gets a new authUID. Which should then be a new user?
Could someone explain the logic behind a user that wants to keep their profile details but change their number.
In order to achieve this, you can use User.updatePhoneNumber. This allows you to update the phone number of a user.
You would use it in the same manner that you also authenticated with phone number in the first place (using signInWithCredential), i.e. you retrieve a credential using FirebaseAuth.verifyPhoneNumber and pass the credential that you get from either verificationCompleted or your user when they enter the SMS code they received. I will only sketch out what this would look like as I assume that you know how to perform this task:
FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(minutes: 2),
verificationCompleted: (credential) async {
await (await FirebaseAuth.instance.currentUser()).updatePhoneNumber(credential);
// either this occurs or the user needs to manually enter the SMS code
},
verificationFailed: null,
codeSent: (verificationId, [forceResendingToken]) async {
String smsCode;
// get the SMS code from the user somehow (probably using a text field)
final AuthCredential credential =
PhoneAuthProvider.getCredential(verificationId: verificationId, smsCode: smsCode);
await (await FirebaseAuth.instance.currentUser()).updatePhoneNumber(credential);
},
codeAutoRetrievalTimeout: null);
When updatePhoneNumber is called, you probably also want to update your database document. Alternatively, you could listen to onAuthStateChanged and update your document this way.
async function save(phone: string, e) {
e.preventDefault();
const { currentUser:fuser } = firebase.auth();
if(fuser && fuser.phoneNumber !== phone) {
try {
const verifier = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
callback: (response) => console.log('callback', response),
size: 'invisible',
});
const phoneProvider = new firebase.auth.PhoneAuthProvider();
const id = await phoneProvider.verifyPhoneNumber(phone, verifier);
const code = window.prompt('Bitte zugeschickten Code eingeben');
const cred = firebase.auth.PhoneAuthProvider.credential(id, code);
await fuser.updatePhoneNumber(cred);
console.log('phone number changed', id, cred, fuser);
setSuccess(true);
} catch(e) {
console.error(e);
}
}
}

How to do Phone Authentication in Flutter using 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

Resources