Update firebase user information is not working properly - firebase

I am trying to update firebase user information, but it is not working properly where I need to hot restart the app to see the changes. This is the method that I am using
Future<void> saveUserNumber(
{String number, PhoneAuthCredential phoneCredential}) async {
try {
await user.updatePhoneNumber(phoneCredential);
await Get.find<FirestoreService>().saveUserPhoneNumber(user.uid, number);
print('user before reload $user');
await user.reload();
update();
print('user after reload $user'); //prints null phone number
} catch (e) {
print(e.message);
}
}
note that signing out works fine but I am not sure why this is not working as well as when I try to change the display name.
and this is what I am using to listen to the user changes with Getx package
// get firebase user, make it observable
Rx<User> _firebaseUser = FirebaseAuth.instance.currentUser.obs;
// getter for user
User get user => _firebaseUser.value;
#override
void onInit() {
super.onInit();
_firebaseUser.bindStream(_auth.authStateChanges());
}

Problem solved!
#override
void onInit() {
super.onInit();
// _firebaseUser.bindStream(_auth.authStateChanges());
_firebaseUser.bindStream(_auth.userChanges());
}
the reason why it was not working is that authStateChanges() listens to the authentication status of the user and doesn't listen to other things such as credentials.
the solution is to use userChanges() instead of authStateChanges() where the userChanges() method has the functionality of listening to the auth state as well as the credentails

Related

Showing: Ignoring header X-Firebase-Locale because its value was null, when I call user.reload()

I am facing this problem when I call the method reload() from FB Auth: Ignoring header X-Firebase-Locale because its value was null.
The authentication system is working fine.
The code below is called after the createUserWithEmailAndPassword method, in another page.
User user = FirebaseAuth.instance.currentUser;
Future<void> checkEmailVerified() async {
print(user.emailVerified);
print(user.uid);
await user.reload();
if (user.emailVerified) {
print('ok');
timer.cancel();
Get.offAll(() => HomePage(), binding: HomeBinding());
}
}
#override
void initState() {
controller.user.sendEmailVerification();
timer = Timer.periodic(Duration(seconds: 5), (timer) {
user.reload();
checkEmailVerified();
});
FirebaseAuth.instance.authStateChanges().listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
super.initState();
}
Same clicking in the email verifying, the emailVerified variable doesn't change to the true.
The issue appears when the code call the reload() method.
I already searched for all, but I can't get solved this issue.
The login and the signup system is work fine. The user is registered in the FB Authentication console.
I would like the implementing email verify in my app
Regards,

How to find out if the user is logged in from Firebase Manual Authentication Or Google Authentication Flutter

My question is how to find out if a user who is currently signed in has been authenticated using firebase manual sign in or google sign in?
When I tap into the user property of class FirebaseUser , when I try to access the providerID, it returns "Firebase" even though I am currently signed in through the Google Sign In provider through Firebase. So, is there any way to find out what provider the user has used for registration of the app?
Thanks a lot for your help. Below is the code that I have already written.
Future<FirebaseUser> getFirebaseUser() async {
FirebaseUser user = await _auth.currentUser();
print(user.email);
return user;
}
#override
void initState() {
// TODO: implement initState
super.initState();
startTimer();
}
void startTimer() {
timer = Timer.periodic(Duration(seconds: 2), (timer) {
if (getFirebaseUser() != null) {
// I Need to push to the menu screen while providing the parameters so that it can recognise if the user is from Firebase Manual Auth or google sign in provider.
//print(_user.providerId);
// Navigator.push(context, MaterialPageRoute(
// builder: (context)
// {
// SideBarLayoutStateful(app: MenuScreen(), isFromGoogleSignIn: ,resultUser: _user, profilePicture: _user.photoUrl,);
// }
//));
}
Navigator.pushNamed(context, 'welcome');
timer.cancel();
});
}
I Need to push to the menu screen while providing the parameters so that it can recognise if the user is from Firebase Manual Auth or google sign in provider, which effects the content displayed on the menu screen.
Thanks for your help and I appreciate it!
Is this supposed to happen:
Future<FirebaseUser> getFirebaseUser() async {
FirebaseUser user = await _auth.currentUser();
print(user.email);
print(user.providerId);
return user;
}

Persistent log in using Firebase currentUser not working - Flutter app

My goal for this app is to have persistent log in, so that the user of my Flutter app needs to sign in only once (unless they sign out) and whenever my Flutter app restarts, they do not need to log back in. I saw many examples with using Firebase Authentication and the .currentUser() method, but I am still having to log in every time I restart the app. I have tested this on the simulator (ios) and on my physical iphone while running debug mode on xCode (usb connection). Is it possible that I cannot test this functionality this way? Because I do see a message pop up on both android studio and xCode terminals that mention lost connection to device or stopped running because of the restarting. If that's the case, how can I test that persistent log in is working?
If that isn't the case, what am I doing wrong?? I've included the code below. This is happening within a stateful widget, of course.
final _auth = FirebaseAuth.instance;
checkIfCurrentUserExists() async {
try {
await _auth.currentUser().then((user) {
print('this is the user $user');
if (user != null && user.email != null) {
userIsLoggedIn = true;
// this works fine!
}
});
} catch (e) {
print('current user was not found $e');
// this works fine!
}
}
// called inside initState()
setClientOnLoad() async {
await Spryte.checkIfCurrentUserExists();
var doesCurrentUserExist = userIsLoggedIn;
var currentUser = await returnCurrentUser();
if (doesCurrentUserExist == false) {
//if user is not authenticated, set anonymous user
// this works fine!
}
else {
//print('current user does exist');
await foo(currentUser.uid);
// 'foo' is meant to retrieve some data about the client on loading of the app
// so that the user doesn't have to log in every time the app restarts
// but for some reason it's not working for me
}
}
I have got the same problem. I was able to sign in but when restarting the app, I was getting another random UID.
It might sound stupid, but make sure you are not calling signInAnonymously(); at any point in your app without checking if there is already a current user.
This was my problem, I was always signing in anonymously at every app restart.
Here is what I did from my starting app page (which takes care of setting up the app including the user):
Future<FirebaseUser> signInAnonymously() async {
AuthResult result = await _auth.signInAnonymously();
final FirebaseUser user = result.user;
assert(user != null);
assert(await user.getIdToken() != null);
return user;
}
And here is the checking method:
Future<FirebaseUser> tryToFetchUser() async {
var user = await _auth.currentUser();
if (user == null) {
user = await FirebaseAuth.instance.onAuthStateChanged.first;
}
if (user == null) {
user = await signInAnonymously();
}
return Future.value(user);
}
I hope it will help some of you and avoid wasting time on stupid mistakes as I did!
You r calling setClientOnLoad() which is async inside init() method & other app navigation depends on this method so u need wrap all your async stuff in FutureBuilder().
if not then build() method ll be called before complete execution of setClientOnLoad()
e.g. :-
FutureBuilder(
future: setClientOnLoad(),
builder: (context, AsyncSnapshot<R> snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
}
// after executing method completely
},
);

Flutter: How to listen to the FirebaseUser is Email verified boolean?

My Idea:
I want to use the Firebase Auth Plugin in Flutter to register the users.
But before they can access the App, they have to verify their Email address.
Therefor I push the Firebase users after registration to a verification screen. This is just a loading screen which tells the user that he has to verify his email.
But now: How can I continuously listen, if the users email is verified or not and send him (when true) to the Homescreen?
I'm new to Flutter and I don't know if I have to use a Streams or Observables or a while Loop or setState() or something else for such a boolean check. And I also don't know how to setup a solution.
This is my basic code for register a user:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final Firestore _db = Firestore.instance;
Future<FirebaseUser> get getUser => _auth.currentUser();
Stream<FirebaseUser> get user => _auth.onAuthStateChanged;
Future<FirebaseUser> edubslogin(String email, String password) async {
try {
final FirebaseUser user = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await user.sendEmailVerification();
//email verification somewhere here
updateUserData(user);
return user;
} catch (error) {
print(error);
return null;
}
}
I've tried this:
if (user.isEmailVerified == true) {
//go to Homescreen
return true;
} else {
//show verification screen(loading spinner)
return false;
}
But I don't get a boolean value true out of isEmailVerified.
What do I have to do?
I faced the same situation in my app. My solution was to create a periodic timer into the initState method of a strategic route to hold the app until the e-mail is verified. It is not so elegant as using a listener but works fine.
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
class _AccountConfirmationState extends State<AccountConfirmation> {
late Timer _timer;
#override
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 5), (timer) async {
await FirebaseAuth.instance.currentUser?.reload();
final user = FirebaseAuth.instance.currentUser;
if (user?.emailVerified ?? false) {
timer.cancel();
Navigator.pop(context, true);
}
});
}
#override
void dispose() {
super.dispose();
_timer.cancel();
}
#override
Widget build(BuildContext context) {
//TODO: Implement your amazing waiting screen here
}
}
This verification isn't as straightforward as you'd hope. First, there is the problem of recognizing that the user has verified their email. Second, there is the issue that there isn't any sort of a notification you can listen to that will automatically trigger a change in your app.
Check this thread for info about emailVerified: https://github.com/flutter/flutter/issues/20390#issuecomment-514411392
I was only able to verify the user if I 1) Created their account, 2) Signed them in, 3) Then checked to make sure they verified their email.
final FirebaseAuth _auth = FirebaseAuth.instance;
var _authenticatedUser = await _auth.signInWithEmailAndPassword(email: _email, password: _password);
//where _email and _password were simply what the user typed in the textfields.
if (_authenticatedUser.isEmailVerified) {
//Verified
} else {
//Not verified
}
Part 2: How do you get your app to recognize that the user has confirmed their email? Find a way to trigger the function that checks confirmation. A button would be easy enough. If you want it to see "automatic" then I guess you could create a timer that checks for email verification every 10 seconds or so.
Well I created a stream to handle this. Not so elegant but works. Use a StreamProvider.value() to handle events.
Stream<userVerificationStatus> checkUserVerified() async* {
bool verified = false;
yield userVerificationStatus(status: Status.LOADING);
while (!verified) {
await Future.delayed(Duration(seconds: 5));
FirebaseUser user = await _auth.currentUser();
if(user!=null)await user.reload();
if (user == null) {
yield userVerificationStatus(status: Status.NULL);
} else {
print("isemailverified ${user.isEmailVerified}");
await user.reload();
verified = user.isEmailVerified;
if(verified)
yield userVerificationStatus(status: Status.VERIFIED);
else
yield userVerificationStatus(status: Status.NOT_VERIFIED);
}
}
}
True. None of the FirebaseAuth idTokenChanges() , authStateChanges() or userChanges() will send you an event if the user verifies their email. I'm using a combination of the methods to get an email verification update in my app and it seems to be working well.
First I check the status in the initState() method and start a timer if email is not verified
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
//Get Authenticated user
user = context.read<AuthenticationService>().currentUser();
_isEmailVerified = user.emailVerified;
if (!_isEmailVerified) _startEmailVerificationTimer();
}
I also listen for app background/foreground events in case the user happens to leave the app to confirm their email ( If you also do this, add WidgetsBindingObserver to your class)
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
user = context.read<AuthenticationService>().reloadCurrentUser();
if (user.emailVerified) {
setState(() {
_isEmailVerified = user.emailVerified;
});
timer?.cancel();
} else {
if (!timer.isActive) _startEmailVerificationTimer();
}
}
}
This is the _startEmailVerificationTimer() method
_startEmailVerificationTimer() {
timer = Timer.periodic(Duration(seconds: 5), (Timer _) {
user = context.read<AuthenticationService>().reloadCurrentUser();
if (user.emailVerified) {
setState(() {
_isEmailVerified = user.emailVerified;
});
timer.cancel();
}
});
}
Don't forget to dispose the timer
#override
void dispose() {
timer?.cancel();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
My Firebase User methods in case anyone is interested:
User currentUser() {
return _firebaseAuth.currentUser;
}
User reloadCurrentUser() {
User oldUser = _firebaseAuth.currentUser;
oldUser.reload();
User newUser = _firebaseAuth.currentUser;
return newUser;
}
In order for the app to recognise if the user has verified their email you can achieve this with a simple user.reload.
In order to test it yourself implement a button with onPressed code:
FlatButton(
child: Text("check"),
textColor: Colors.white,
onPressed: () async {
try {
FirebaseUser user = await _firebaseAuth.currentUser();
await user.reload();
user = await _firebaseAuth.currentUser();
print( user.isEmailVerified);
} catch (e) {
return e.message;
}
}),
I had the same problem with the latest version of firebase auth.
But I found out there is a function for reloading the current user which signed in
Future<bool> get userVerified async {
await FirebaseAuth.instance.currentUser.reload();
return FirebaseAuth.instance.currentUser.emailVerified;
}
referesh token after checking current user emailVerified is true
var user = FirebaseAuth.instance.currentUser;
await user?.reload();
if (user?.emailVerified == true) {
await FirebaseAuth.instance.currentUser?.getIdToken(true);
//rest code..
}
also please let me know if this a correct way of doing things.
I have found a way by updating firebase user profile and calling it in init() like below function.
void _checkEmailVerification() async {
await widget.auth.getCurrentUser().then((user) {
UserUpdateInfo userUpdateInfo = new UserUpdateInfo();
userUpdateInfo.displayName = user.displayName;
user.updateProfile(userUpdateInfo).then((onValue) {
setState(() {
_isEmailVerified = user.isEmailVerified;
});
});
});
}
Auth state change listener didn't work for me. Field isEmailVerified remains false even after user verifies his email.
My workaround:
Started from the assumption that user leaves the app to verify his email (which mean app is paused), and he returns to the app after verifying it (app resumes).
What I did was attach a WidgetsBinding to a relevant stateful widget where I wanted to display if email was verified (but can be done elsewhere). This involves two steps.
First step is to attach the binding:
#override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Second step is to override the didChangeAppLifecycleState to reload the user. I created a function that does the reload and sets a new firebaseUser object
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed && !firebaseUser.isEmailVerified)
refreshFirebaseUser().then((value) => setState(() {}));
super.didChangeAppLifecycleState(state);
}
Future<void> refreshFirebaseUser() async {
await firebaseUser.reload();
firebaseUser = FirebaseAuth.instance.currentUser;
}
So what this is basically doing is to reload firebase user object everytime the user returns to the app, while its email is not verified. I chose this solution over setting and cancelling a timer as it avoided setting a recurrent action through a timer which could be overkill for this particular problem.
Since authOnChanged only listens for sign in and sign out actions, in your sign in method, first sign out then try to sign in.
await _firebaseAuth.signOut();
authResult = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
return authResult.user;
In the onAuthChanged, when you control if user.isEmailVerified, it will work since you have signed out and it will update the user even if you haven't signed in yet because sign out will trigger your onAuthChanged even if you haven't signed in.
It is like cheating but the only way that I have found without timeout is this.

FirebaseAuth current user is not returning null after calling sign out?

I am trying to logout user and switch the widgets but after calling the following function -
void logoutUser() async {
await FirebaseAuth.instance.signOut();
}
If I check for the current user, it's returning user object but with null id -
FirebaseAuth.instance.currentUser()
I try to kick the user out to main.dart after logout which checks if user is signed in or not and loads a correct widget. Does anyone have any idea why currentUser() isn't returning null after calling signOut()?
_auth.currentUser() is probably returning an anonymous FirebaseUser object. Check the isAnonymous property.
Example :
auth.currentUser().then((user) {
if (user == null || user.isAnonymous) {
// what will you do?
return;
}
setState(() {
_uid = user.uid;
});
});
Yet, I would highly recommend to monitor the onAuthStateChanged stream instead. This way you will be informed when the user logs in or logs out immediately.
Check this article, it covers it in depth.

Resources