I'm writing an application on flutter and I'm a bit stuck at the moment of extracting the contents of firebase classes. How do I extract the contents of the PhoneNumber variable from "user"? I will need the extracted number to send to the server
This is the output of the debugPrint('user: $user'):
user: FirebaseUser({uid: hGLEDW6OT5ZMhWra9L4p6bB4Pw92, isAnonymous: false, phoneNumber: +79644054946, providerData: [{uid: hGLEDW6OT5ZMhWra9L4p6bB4Pw92, phoneNumber: +79644054946, providerId: firebase}], providerId: firebase, creationTimestamp: 1557420327980, lastSignInTimestamp: 1558848790729, isEmailVerified: false}
void _signInWithPhoneNumber() async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: _verificationId,
smsCode: _smsController.text,
);
final FirebaseUser user = await _auth.signInWithCredential(credential);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
debugPrint('user: $user');
setState(() {
if (user != null) {
Navigator.pushNamed(context, '/amenities');
} else {
_message = 'Вход не выполнен';
}
});
}
I need to extract PhoneNumber from "user" to send to server in Json
I'm pretty sure you can grab the phone number with just user.phoneNumber
Related
I want to add a user's DisplayName upon creating a new user. When I tried to use updateProfile method, it gives below warning
/// Updates a user's profile data.
#Deprecated(
'Will be removed in version 2.0.0. '
'Use updatePhotoURL and updateDisplayName instead.',
How can get around this ??
String email, String password) async {
final UserCredential userCredential =
await _firebaseAuth.createUserWithEmailAndPassword(
email: email.trim(), password: password.trim());
await userCredential.user.updateDisplayName('Mohamed Rahuman');
User user = userCredential.user;
print(user);
return _userFromFirebaseUser(user);
}
flutter: User(displayName: null, email: test1#gmail.com, emailVerified: false, isAnonymous: false, metadata: UserMetadata(creationTime: 2021-06-06 10:35:13.689, lastSignInTime: 2021-06-06 10:35:13.689), phoneNumber: null, photoURL: null, providerData, [UserInfo(displayName: null, email: test1#gmail.com, phoneNumber: null, photoURL: null, providerId: password, uid: test1#gmail.com)], refreshToken: , tenantId: null, uid: gghhhhhhhh55555666)
THIS IS FIXED: with firebase_auth 1.3.0 (see the Changelog)
Had the same issue. Solved by calling:
await user.reload();
user = await _auth.currentUser;
this is the full code snippet:
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
User? user = result.user;
if (user != null) {
//add display name for just created user
user.updateDisplayName(displayName);
//get updated user
await user.reload();
user = await _auth.currentUser;
//print final version to console
print("Registered user:");
print(user);
}
I had the same issue in my signup function while updating (updatePhotoURL)
You need to update your user by
ourUser = FirebaseAuth.instance.currentUser;
await ourUser!.updatePhotoURL(defaultProfileImage);
await ourUser!.displayName(userName);
Check how I did it
Future register(
String email,
String password,
String? name,
String? mob,
String? description,
//DateTime? signUPDate,
) async {
setIsLoading(true);
try {
UserCredential authResult = await _firebaseAuth
.createUserWithEmailAndPassword(email: email, password: password,);
ourUser = authResult.user;
setIsLoading(false);
//create new doc for the user with his unique uid
await UserDatabaseServices(uid: ourUser!.uid)
.addUserData(ourUser!.uid,email,password,name,mob,description,currentTime);
ourUser = _firebaseAuth.currentUser;
await ourUser!.updatePhotoURL(defaultProfileImage);
notifyListeners();
return ourUser;
} on SocketException {
setIsLoading(false);
setMessage('No internet');
} on FirebaseAuthException catch (e) {
setIsLoading(false);
setMessage(e.message);
}
}
The cause of this issue was displayName and photoURL is unable to be set to null. More details are discussed here. As you've mentioned, this issue should be fixed as of version 1.3.0.
To fetch the display name configured during Registration, you can fetch by calling currentUser on your FirebaseAuth instance.
var currentUser = FirebaseAuth.instance.currentUser;
debugPrint('${currentUser.displayName}');
The idea is here to keep the user logged in even after the user has closed the app and relaunch it at much later time.
I have the following code which logs in user and then try to store the tokens
Future<User> loginSocialFirebaseGoogle() async {
GoogleSignInAccount signInResult = await _googleSignIn.signIn();
GoogleSignInAuthentication auth = await signInResult.authentication;
final AuthCredential authCredential = GoogleAuthProvider.getCredential(
idToken: auth.idToken, accessToken: auth.accessToken);
final AuthResult authResult =
await _auth.signInWithCredential(authCredential);
final FirebaseUser firebaseUser = authResult.user;
assert(!firebaseUser.isAnonymous);
assert(await firebaseUser.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(firebaseUser.uid == currentUser.uid);
final SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString("idToken", auth.idToken);
pref.setString("accessToken", auth.accessToken);
pref.setString("provider", describeEnum(Provider.Google));
return await authResultToUser(authResult, Provider.Google);
}
Another portion of the code is called during app startup
Future<User> getCurrentUser() async {
final SharedPreferences pref = await SharedPreferences.getInstance();
if (pref != null) {
AuthCredential authCredential;
String provider = pref.getString("provider");
Provider providerEnum;
if (provider != null) {
if (provider == describeEnum(Provider.Google)) {
authCredential = GoogleAuthProvider.getCredential(
idToken: pref.getString("idToken"),
accessToken: pref.getString("accessToken"));
providerEnum = Provider.Google;
} else if (provider == describeEnum(Provider.Facebook)) {
authCredential = FacebookAuthProvider.getCredential(
accessToken: pref.getString("accessToken"));
providerEnum = Provider.Facebook;
}
final AuthResult authResult =
await _auth.signInWithCredential(authCredential);
return await authResultToUser(authResult, providerEnum);
}
}
return null;
}
However I am getting error during _auth.signInWithCredential(authCredential), the error is
`ERROR_INVALID_CREDENTIAL` - If the credential data is malformed or has expired.
Looks like what I stored in the SharedPreferences are wrong. Any idea how to fix this ?
The idea is here to keep the user logged in even after the user has closed the app and relaunch it at much later time.
Firebase already keeps the user signed in, and automatically tries to restore their session without you needing to write any code for it.
But since this may require a call to the server, you may be reading _auth.currentUser before that process has completed. To detect the authentication state properly, use a listener to authStateChanges as shown in the documentation:
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
I am trying to implement the example given at How do I access custom claims? to my existing code.
I have a Stream which listens to auth changes and updates my own user object with the responded Firebase user. When I store my user object, I would like to get the custom claims of that user as well.
The problem is in _userFromFirebaseUser.
It says "The await expression can only be used in an async function.
Try marking the function body with either 'async' or 'async*'."
But when I do so, the error is hops to my stream where it then says "The argument type 'Future Function(User)' can't be assigned to the parameter type 'User Function(User)'." for "_userFromFirebaseUser" in
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
Here is my complete authentication class:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
local.User _userFromFirebaseUser(auth.User user) {
final isAdmin = (await _currentUserClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _currentUserClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Am I heading into the wrong direction? Is there anything obvious, that I simply do not consider?
Thanks for your help!
For those, heading into the same problem, I found the solution after further research:
You will have to change the .map to .asyncMap.
Here is the code, which works for me:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
Future<local.User> _userFromFirebaseUser(auth.User user) async {
final isAdmin = (await _userClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().asyncMap(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _userClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Found here: In flutter, how can I "merge" Firebase onAuthStateChanged with user.getTokenId() to return a Stream?
I am trying to save the users who sign in in the firebase database.
this is the function which is used to update the signed in user in the firebase, the fuction uses a uid to create a document with this id :
final Firestore _db = Firestore.instance;
void upadteUserData(FirebaseUser user) async {
DocumentReference ref = _db.collection("users").document(user.uid);
print("in update");
return ref.setData({
"uid": user.uid,
'email': user.email,
'displayName': user.displayName,
//'emergency': []
}, merge: true);
}
and here is the sign in fuction:
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
upadteUserData(user);
print("signing in");
print(result.user.email);
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
here's the console after signing in
I tried it twice and it was working perfectly fine 3 weeks ago. However, when I try to sign in today with different e-mails, it did not update the fire base. Any idea?
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