How to determine which provider the user logged in with - facebook

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.

Related

Why device token generated in every run of the flutter application?

I'm using firebase cloud messaging to send notifications to devices. The problem is that the device token regenrated and added to firestore with different id in every run of the application. I want it to be generated juste once for the first installation of the application.
this is my code :
Future init() async {
_firebaseMessaging.getToken().then((token) {
saveTokens(token);
});
}
Future<void> saveTokens(var token) async {
try {
await _firestore.collection('deviceTokens').add({
'token': token,
});
} catch (e) {
print(e);
}
}
this is how I call it in the main():
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await _msgService.init();
// testFirestore();
FirebaseMessaging.onBackgroundMessage(_messageHandler);
this is _messageHandler function:
Future<void> _messageHandler(RemoteMessage message) async {
print(
'background message ${message.notification!.body} + ${message.notification!.title}');
}
Actually token only refresh on one of that cases:
The app deletes Instance ID
The app is restored on a new device
The user uninstalls/reinstall the app
The user clears app data.
So you need to check in your firebase collection if your token (getted on getToken()) is saved yet before add it. If it already exists in your database, don't save it.
For example:
Future<bool> doesTokenAlreadyExist(String token) async {
final QuerySnapshot result = await Firestore.instance
.collection('deviceTokens')
.where('token', isEqualTo: token)
.limit(1)
.getDocuments();
final List<DocumentSnapshot> documents = result.documents;
return documents.length == 1;
}
The registration token may change when:
The app is restored on a new device
The user uninstalls/reinstall the app
The user clears app data.
More :
Update from Play Store - Token remains same.
When close the application and reopen it - Token remains same.
I recommend you should record that token for the user every time your app launches. Then, you don't face any problems.
(add function to init state of home page of your app)

How to grant system permissions while writing flutter integration tests?

I am writing flutter integration tests https://docs.flutter.dev/cookbook/testing/integration/introduction
I can find and tap on my widgets perfectly fine but the problem arises when I have to click the system widgets to grant permissions in order to continue and test the remaining flow.
For example:
I tap a button then have to grant location permissions. At this moment the screen as below is shown
This screen is shown by the system and I have no key or way how to tap "While using the app".
Also the system screen might change between devices like ios and android and the text might be different as well.
What is the best practice to solve this issue? I am blocked from testing the remaining screens that use the required permissions..
What I tried so far?
I tried to grant the permissions manually for Android as a start but did not work
Future<void> grantRequiredAppPermissions() async {
if (!Platform.isAndroid) {
return;
}
const appPackageName = 'my.package';
print(
'We are going to manually grant the required permissions to the android package $appPackageName');
final Map<String, String> envVars = Platform.environment;
String adbPath = join(
envVars['ANDROID_SDK_ROOT'] ?? envVars['ANDROID_HOME']!,
'platform-tools',
Platform.isWindows ? 'adb.exe' : 'adb',
);
print('Using adb at $adbPath');
final permissions = [
'android.permission.READ_EXTERNAL_STORAGE',
'android.permission.WRITE_EXTERNAL_STORAGE',
'android.permission.ACCESS_FINE_LOCATION',
'android.permission.ACCESS_COARSE_LOCATION'
];
for (final permission in permissions) {
await Process.run(
adbPath, ['shell', 'pm', 'grant', appPackageName, permission]);
}
}
I was calling that function in my app/integration_test/driver.dart
import 'dart:io';
import 'package:integration_test/integration_test_driver_extended.dart';
import 'helpers/grant_required_permissions.dart';
Future<void> main() async {
await grantRequiredAppPermissions();
await integrationDriver();
}
but did not help at all. Also it is not a proper solution because even if it was going to work, would work only for android and i test also on ios devices.
You can try out the testing framework patrol https://pub.dev/packages/patrol
See the documentation here: https://patrol.leancode.co/
It let's you interact with the native ui like this:
await $.native.grantPermissionWhenInUse();
await $.native.grantPermissionOnlyThisTime();
await $.native.denyPermission();

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

Flutter Web Firebase Auth user is logged out after refresh

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.

Why am I not getting a password reset email from the Firebase service?

I am trying to implement a password reset function that incorporates a SnackBar to display either success or error messages. The code shown below produces both the success message and the various error messages, as appropriate - but I never receive the password reset email from the Firebase service. I'm not sure if it's a Firebase setup issue or an issue with the code.
Future resetPassword() async {
try {
await FirebaseAuth.instance
.sendPasswordResetEmail(email: _emailController.text.trim());
_showSnackBar('Password reset link sent');
} on FirebaseAuthException catch (e) {
_showSnackBar(e.message.toString());
return;
}
}
Future<void> _showSnackBar(String msg) async {
final snackBar = SnackBar(
content: Text(msg),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
Disregard. Code seems to work fine - problem was either with Firebase Auth service or my internet service provider last night. All good now.
Make sure to check your spam, because if did not add authorize rules in firebase then the reset link goes in spam mail.
.