Flutter Web Firebase Auth user is logged out after refresh - flutter

I have a Flutter Web App where the user is logged in via Firebase. The Problem is that every time the page is refreshed, the user is logged out.
Similiar for iOS: User stays logged in after refresh but is being logged out when completely closing the app.
I know there is this issue which says that it should be fixed with the latest version, but I am already on the supposedly fixed version:
firebase_core: ^1.20.0
firebase_auth: ^3.6.0
cloud_firestore: ^3.4.1
I am using the go_router and redirect the user if he is not logged in:
final GoRouter router = GoRouter(
key: Get.key,
urlPathStrategy: UrlPathStrategy.path,
redirect: (state) {
final String destination = state.location;
final bool isOnStartView = destination == '/start';
final bool isOnEmailFlow = state.subloc.contains('/email');
if (!isOnStartView && !isOnEmailFlow && !AuthService.isLoggedIn()) {
return '/start';
}
return null;
},
...
And this is my AuthService.isLoggedIn():
static final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
static User? get currentUser => FirebaseAuth.instance.currentUser;
static bool isLoggedIn() {
return _firebaseAuth.currentUser != null;
}
What am I missing here? It says everywhere that it should be fixed but it's not working for me.. Let me know if you need any more details!

Update to
dependencies:
firebase_auth: ^3.6.4
This bug is now resolved in lastest version.
Use stream -
FirebaseAuth.instance.authStateChanges()
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user != null) {
print(user.uid);
}
});

The one way to achieve that is you have to put a check and stored that value as a persistent data.
So you can use Shared Preferences and when the user login for the first time change the value of shared preference to true.
And then in initstate of login page get the value of shared preference and check if it is true or not. If true navigate to homepage otherwise to login page.
Note : this approach may not work in localhost but I m sure this will work when you host it to firebase hosting.
Hope this will help.

Related

Error when trying to authenticate users with google in firebase

I've been struggling with this for months, sinc.e I'm just learning firebase with flutter.
I have a simple button that when pressed should give the option to register or start section with google, but when running I get errors in the console.
There are so many that I don't know which one it is.
Error: Assertion failed:
file:///C:/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_web-1.7.2/lib/src/firebase_core_web.dart:207:11
options != null
"FirebaseOptions cannot be null when creating the default app."
Error: [core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
here in onpress of the button:
child: MaterialButton(
onPressed: () async {
User? user = await Authenticator.IniciarSesion(context: context);
print(user?.displayName);
},
and the class that authenticates
class Authenticator {
static Future<User?> IniciarSesion({required BuildContext context}) async {
FirebaseAuth authenticator = FirebaseAuth.instance;
User? user;
GoogleSignIn objGooglesignIn = GoogleSignIn();
GoogleSignInAccount? objGooglesignInAccount =
await objGooglesignIn.signIn();
if (objGooglesignInAccount != null) {
GoogleSignInAuthentication objGoogleSignInAuthentication =
await objGooglesignInAccount.authentication;
AuthCredential credential = GoogleAuthProvider.credential(
accessToken: objGoogleSignInAuthentication.accessToken,
idToken: objGoogleSignInAuthentication.idToken);
try {
UserCredential userCredential =
await authenticator.signInWithCredential(credential);
user = userCredential.user;
return user;
} on FirebaseAuthException catch (e) {
print("error de auth");
}
}
}
}
Sorry if I put a lot of code. I don't know exactly what the error could be.
There are two issues with your application, first one seems like you are not initializing firebase in the main function properly. Please refer mentioned link for setup.
Flutter firebase setup guide
Additionally as I can see there is no firebase_options.dart in your lib directory which basically manages (api key) configurations for different platforms.
If you have followed steps mentioned in guide correctly you can create these configs using
flutterfire configure
command only after you have already installed flutterfire cli using following command
dart pub global activate flutterfire_cli

Firebase Authorization is not checking if the user is logged in properly

in my flutter app I want to check if a user is logged in before or not and based on this navigate them to either HomePage or SignIn page. The code I am using is not working fine, it is not navigating to the SignIn page after I've done registration and deleted the account in Firebase Console. In short, when I delete a user, who registered well before, in Firebase Console the application is keeping to show the HomePage and all the posts the user made. How can I handle it?
"In short, I want to navigate the user to SignIn page after I delete his account in Firebase Console"
Here is the code in my main.dart file:
_check(){
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
if(user == null){
HiveDB.deleteUser();
return SignInPage();
}
HiveDB.putUser(id: user.uid);
return HomePage();
}
#override
void initState() {
// TODO: implement initState
super.initState();
_check();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: _check(),
routes: {
SignInPage.id:(context) => SignInPage(),
SignUpPage.id:(context) => SignUpPage(),
HomePage.id:(context) => HomePage(),
},
);
}
The _check() function is not working as it's desired...
While Firebase persists the user info in local storage and automatically restores it when the app/page reloads, this requires that it makes an asynchronous call to the server and thus may not be ready by the time your check runs.
Instead of depending on currentUser, you'll want to listen for changes to the authentication state through the authStateChanges stream. Most apps wrap this in a StreamBuilder and then show the correct screen based on the stream state and last event.
This is something that can be resolved by using async . the problem is you are getting response in future but the action is performed before. Please check the following documentation for detailed overview:
https://dart.dev/codelabs/async-await

Flutter: iOS doesn't ask for permissions

I have this code showing the Retry button:
TextButton(
onPressed: () async {
await _checkPermission().then(
(hasGranted) {
if (hasGranted == PermissionStatus.granted) {
refreshContacts();
}
},
);
},
...
Future<PermissionStatus> _checkPermission() async {
final Permission permission = Permission.contacts;
final status = await permission.request();
return status;
}
This always returns:
PermissionStatus.permanentlyDenied
but it doesn't open the permission question again.
Info.plist:
<key>NSContactsUsageDescription</key>
<string>This app requires access to display the contacts list. This will be used to import contacts automatically if necessary to the app contacts list.</string>
If a permission is permanently denied, it can only be enabled from the application settings (bar reinstalling the app). You can use the openAppSettings function to redirect the user to the settings.
Fixed by this issue on its repo: https://github.com/Baseflow/flutter-permission-handler/issues/718#event-5560838657
as per the current version of permission handler you need to add contact permission in Podfile and plist both files make sure you have added this.

How to determine which provider the user logged in with

I am using multiple Firebase sign in methods in my app (Facebook, Google, Phone, etc). But once the user is logged in, I am unable to determine which provider was used to sign in.
I tried below(very close to the recommended solution. But does not work):
_auth.currentUser().providerData[0].providerId
I expect it to return something like "google.com" or "facebook.com"
Instead it returns "firebase"
I cant find any other solution where it clearly lets us determine if a google or facebook sign in was used. All solutions in the forum iOS specific or for web. Can't find anything flutter specific. Appreciate any help in advance.
I checked and _auth.currentUser().providerData[0].providerId does provide "google.com" or "facebook.com"
Not sure at what point it showed me "firebase". Will post to this if I figure this out
You can use the following code :
#override
Future<void> signOut() async {
final FirebaseAuth _auth = FirebaseAuth.instance;
try {
final _providerData = _auth.currentUser!.providerData;
if (_providerData.isNotEmpty) {
if (_providerData[0]
.providerId
.toLowerCase()
.contains('google')) {
await GoogleSignIn().signOut(); // google signOut
} else if (_providerData[0]
.providerId
.toLowerCase()
.contains('facebook')) {
await FacebookAuth.instance.login(); // facebook signOut
}
}
await _auth.signOut(); // firebase signOut
} catch (e) {
print(e);
}
}
I am not sure about facebook since I have never implemented login with facebook, but for Google, you may be able to do something like this.
You probably have a statement similar to GoogleSignInAccount googleUser = await _googleSignIn.signIn(). Why not check if google User is null or not once the user is signed in or on auth change? This will let you know if the user signed in with google.
Similar functionality for facebook should be possible but I just don't know if it is the same.
If googleUser != null then google was used.
If say facebookUser != null then facebook was used,
Then if googleUser is null and say facebookUser is null it was email.
Maybe this is helpful, maybe not, but it is just what I could think of right now.

How to properly implement Logout with firebase on flutter

It sounds stupid but I cant really properly logout of my app and that is because I use multiple FirebaseAnimatedList and route on my MaterialApp
routes: <String,WidgetBuilder>{
'/StartAppPage':(BuildContext context)=>new StartAppPage(),
'/LoginPage':(BuildContext context)=> new LoginPage(),
'/HomePage':(BuildContext context)=> new HomePage)
},
So the app checks for use and routes to HomePage or Login based on is there is a user or not.
My Home page has a FirebaseAnimatedList and
on my Home page there is a Logout button that do this
await googleSignIn.signOut();
await FirebaseAuth.instance.signOut();
await FirebaseDatabase.instance.goOffline();
return Navigator.pushReplacementNamed(context, '/StartApp');
to HomePage and logout the user.
But when the other user login again the List shows data of the old user and the list is usable messing my Database
How can I properly implement this and the setPersistance is off or on makes no difference
Solution: Keep a single instance of your firebase user through the app. I recommend a global variable
After signing out, what you can consider doing here is check on the current screen if the user is authenticated or not. If the user is still logged-in, display the data. If not, then navigate to a Login screen.
FirebaseAuth.instance
.idTokenChanges()
.listen((User? user) {
if (user == null) {
debugPrint('User is currently signed out!');
// TODO: navigate to Login screen?
} else {
debugPrint('User is signed in!');
// TODO: display data
}
});