Hello Everyone someone please offer me some help if you can. I'm not sure if this is an issue on Firebase's side or if I failed on my code or configuring the Firebase authentication correctly.
So here's the issue.
I wanted to see if a user existed inside the firestore. If it does then link the accounts. I read the documentation and understand that when using the SigninwithCredential() it should throw an error saying something on the lines of "ERROR USER ALREADY EXISTS IN FIRESTORE".
So testing went something like this to see if I'd get an error before trying to handle the error.
Continue with Facebook (OK).
Signed In (OK).
Continue with Google (Expected Error) (Didn't throw Error)
So what happened here instead was the user originally created by Facebook was overridden by Google.
Sign In with Facebook (Expected Error) (OK) Recieved error as expected "An Account already exists but with different sign-in credentials"
Read documentation several times and compared google code with Facebook code and can't seem to find answer.
class MyFirebase {
static bool isSignIn = false;
static auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
static FacebookLogin facebookLogin = FacebookLogin();
static GoogleSignIn googleSignIn = GoogleSignIn();
static Future<auth.User> loginGoogle() async {
final prefs = await SharedPreferences.getInstance();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
if (googleSignInAccount == null) {
print("ERROR HAS OCCURED WITH THE GOOGLE LOGIN");
throw new Exception("ERROR HAS OCCURED WITH THE LOGIN");
} else {
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
var item = googleSignInAccount.photoUrl;
print("PHOTO URL" + item);
prefs.setString("photoURL", item);
final auth.AuthCredential credential = auth.GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken);
try {
var authResult = await _auth.signInWithCredential(credential);
return authResult.user;
} catch (e) {
print(e);
throw (e);
}
}
}
static Future<auth.User> loginFacebook() async {
final FacebookLoginResult result = await facebookLogin.logIn(['email']);
final prefs = await SharedPreferences.getInstance();
var a;
switch (result.status) {
case FacebookLoginStatus.cancelledByUser:
print("CANCELLED BY USER DO NOT RETURN");
throw new Exception("CANCELLED BY USER DO NOT RETURN");
break;
case FacebookLoginStatus.error:
print("ERROR OCCURED");
throw new Exception("ERROR oCCURED");
break;
case FacebookLoginStatus.loggedIn:
try {
final FacebookAccessToken accessToken = result.accessToken;
auth.AuthCredential credential =
auth.FacebookAuthProvider.credential(accessToken.token);
print("########## MAKING GRAPH RESPONSE");
final response = await http.get(
'https://graph.facebook.com/v2.12/me?fields=name,first_name,picture.width(800).height(800),last_name,email&access_token=${accessToken.token}');
final profile = jsonDecode(response.body);
String item = profile['picture']['data']['url'];
prefs.setString("photoURL", item);
a = await _auth.signInWithCredential(credential);
return a.user;
} catch (e) {
print(e);
throw e;
}
break;
}
return null;
}
static Future<void> signOut() async {
await facebookLogin.logOut();
await _auth.signOut();
await googleSignIn.signOut();
}
}
**strong text** ```
Related
I'm using Flutter with Firebase. When I try to logout with email and password, it works good but when I try with google it doesn't work.
Here is my code:
`
try {
switch (user.providerData[0].providerId) {
case 'password':
await FirebaseAuth.instance.signOut();
break;
case 'google.com':
final GoogleSignIn googleSignIn = GoogleSignIn();
await googleSignIn.signOut();
break;
}
} on FirebaseAuthException catch (e) {
showAuthException(e, context);
}
`
I'm trying this
`
try {
switch (user.providerData[0].providerId) {
case 'password':
await FirebaseAuth.instance.signOut();
break;
case 'google.com':
final GoogleSignIn googleSignIn = GoogleSignIn();
await googleSignIn.signOut();
break;
}
} on FirebaseAuthException catch (e) {
showAuthException(e, context);
}
`
Please refer to this code :) and this will do the trick.
your Code:
final GoogleSignIn googleSignIn = GoogleSignIn();
await googleSignIn.signOut();
the problem is you have created googleSignIn variable and signOut from that variable.
you can also try to check using googleSignIn.isSignedIn(); this will return bool value.
Working Code.
class GoogleServiceProvider extends ChangeNotifier {
static final GoogleSignIn _googleSignIn = GoogleSignIn(); // <----
GoogleSignInAccount? _user;
GoogleSignInAccount? get user => _user;
Future<GoogleSignInAccount?> logInWithGmail() async {
final googleUser = await _googleSignIn.signIn();
if (googleUser != null) {
_user = googleUser;
notifyListeners();
}
return null;
}
Future logOut() async {
await _googleSignIn.signOut();
notifyListeners();
}
}
When I hit "Sign in with Google", it does let me choose the account although somehow, the previous user gets loaded.
class GoogleSignInProvider extends ChangeNotifier {
GoogleSignIn? _googleSignIn;
GoogleSignInAccount? _user;
GoogleSignInAccount get user => _user!;
bool _isGoogleLogin = false;
final _auth = FirebaseAuth.instance;
Future signInWithGoogle(context) async {
try {
_googleSignIn = GoogleSignIn();
final googleUser = await _googleSignIn!.signIn();
if (googleUser == null) {
return;
}
_user = googleUser;
final googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
var authCredential = await _auth.signInWithCredential(credential);
_isGoogleLogin = true;
if (authCredential.user != null) {
final snapshot = await FirebaseFirestore.instance
.collection('users')
.doc(authCredential.user!.uid)
.get();
if (!snapshot.exists) {
var userId = authCredential.user!.uid;
FirebaseFirestore.instance.collection('users').doc(userId).set({
"email": googleUser.email,
"workoutsCount": 0,
"lastWorkoutDate": null
});
}
}
notifyListeners();
Navigator.of(context).pushNamed('/dashboard');
} on FirebaseAuthException catch (e) {
showSnackBar(context, e);
} catch (e) {
print('ERROR = ');
print(e);
}
}
Future<void> signOut(context) async {
try {
if (_isGoogleLogin) {
final googleCurrentUser =
GoogleSignIn().currentUser ?? await GoogleSignIn().signIn();
if (googleCurrentUser != null) {
await GoogleSignIn().disconnect().catchError((e, stack) {
FirebaseCrashlytics.instance.recordError(e, stack);
});
}
}
await _auth.signOut();
} catch (e) {
showSnackBar(context, e);
} finally {
_isGoogleLogin = false;
Navigator.of(context).pushNamedAndRemoveUntil(
'/register_login', (Route<dynamic> route) => false);
}
}
Is there something wrong with this code??
Can you try to disconnect the account before signing out:
GoogleSignIn _googleSignIn = GoogleSignIn();
await _googleSignIn.disconnect();
await FirebaseAuth.instance.signOut();
You are using firebase auth then please use thsi for signout user :
How to Signout a user in Flutter with Firebase authentication
Remove if (_isGoogleLogin) from your signOut function. googleCurrentUser will check the consitionn and responses based on the result.
First Create a Global variable
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
GoogleSignIn googleSignIn = GoogleSignIn();
Make sure you are using the above variable for login and logout
final googleCurrentUser = firebaseAuth.currentUser;
if (googleCurrentUser != null) {
googleSignIn.signOut();
await firebaseAuth.signOut();
}
User above code for signout user
I'm developing an application with Flutter and Parse Server and I want to register Users from google account.
I have tried:
static Future<void> loginWithGoogle() async {
try {
final _googleSignIn = GoogleSignIn(scopes: ['email', 'https://www.googleapis.com/auth/contacts.readonly']);
final account = await _googleSignIn.signIn(); // I figure out that the real error is here;
final authentication = await account.authentication;
final googleAuth = google(_googleSignIn.currentUser.id, authentication.accessToken, authentication.idToken);
final response = await ParseUser.loginWith('google', googleAuth);
if (response.success) {
print(response);
//currentUser = await ParseUser.currentUser(customUserObject: User.clone());
//Get.offNamed('/oauth');
}
} catch (e) {
print(e);
AlertUtils.showErrorDialog(e.toString());
}
}
but here I faced that error:
plugin: PlatformException(sign_in_failed,
com.google.android.gms.common.api.ApiException: 10: , null)
You cannot do this without registering on Firebase. The official documentation says so.
I'm trying to implement Google Sign-In in a Flutter application but all the exception that I try to catch are not being caught. I don't know why. I have tried different methots to catch them but nothing.
This is my code:
Future signInWithGoogle() async {
GoogleSignInAccount googleSignInAccount;
try {
googleSignInAccount = await googleSignIn.signIn().then((res) {
print("success");
return null;
}, onError: (e) {
print("${e.toString()}");
return null;
});
} catch (err) {
print("${err.toString()}");
}
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
return 'signInWithGoogle succeeded: $user';
}
The exception is always thrown on line googleSignInAccount = await googleSignIn.signIn()
As you can see I try to catch exceptions in many ways but none of them actually works.
I see a problem with your sample code and its the .then call ...
googleSignInAccount = await googleSignIn.signIn().then((res) {
print("success");
return null;
}, onError: (e) {
print("${e.toString()}");
return null;
});
you cannot do both an await and a .then typically its one or the other. For the try/catch to work remove the .then
try {
googleSignInAccount = await googleSignIn.signIn();
} catch(err) {
...
}
I am unable to handle the platform exception when user presses cancel instead of logging in using the google sign in window.
I have even tried an if statement so to not run if signIn returns null, but that does not work either.
Future<void> _handleSignIn() async {
try {
await googleSignIn.signIn();
} catch (error) {
print(error);
}
}
What's not so obvious when authenticating with google, is that the process consist of three steps
_googleSignIn.signIn() // prompt window
googleAccount.authentication // obtaining the credentials
_auth.signInWithCredential(credential) // the actual authentication
// STEP 1
// This is where flow gets cancelled and it results in the `googleAccount` to be null
GoogleSignInAccount googleAccount = await _googleSignIn.signIn();
if (googleAccount == null) {
return null;
}
// STEP 2
// obtaining credentials (cached or via input)
GoogleSignInAuthentication googleAuthentication = await googleAccount.authentication;
AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleAuthentication.idToken,
accessToken: googleAuthentication.accessToken,
);
// STEP 3
// the provider authentication
AuthResult authResult = await _auth.signInWithCredential(credential);
FirebaseUser user = authResult.user;
// RESULT
print (user.uuid);
The issue I had and seem like others may have too, is that when wrapping three steps in try catch block and not handling the null value from the first step, and the catch block would expect the error object and code property - which would be the case. Anyways here is how I handle it now.
// https://github.com/flutter/flutter/issues/26705
Future<FirebaseUser> signInWithGoogle() async {
FirebaseUser user;
var errorMessage;
try {
GoogleSignInAccount googleAccount = await _googleSignIn.signIn();
if (googleAccount == null) {
errorMessage = "CANCELLED_SIGN_IN";
return Future.error(errorMessage);
}
GoogleSignInAuthentication googleAuthentication = await googleAccount.authentication;
AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleAuthentication.idToken,
accessToken: googleAuthentication.accessToken,
);
AuthResult authResult = await _auth.signInWithCredential(credential);
user = authResult.user;
return user;
}
catch(error) {
switch (error.code) {
case "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL":
errorMessage = "Account already exists with a different credential.";
break;
case "ERROR_INVALID_CREDENTIAL":
errorMessage = "Invalid credential.";
break;
case "ERROR_INVALID_EMAIL":
errorMessage = "Your email address appears to be malformed.";
break;
case "ERROR_WRONG_PASSWORD":
errorMessage = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
errorMessage = "User with this email doesn't exist.";
break;
case "ERROR_USER_DISABLED":
errorMessage = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
errorMessage = "Too many requests. Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
errorMessage = "Signing in with Email and Password is not enabled.";
break;
default:
errorMessage = "An undefined Error happened. ";
}
}
if (errorMessage != null) {
return Future.error(errorMessage);
}
return null;
}
And the google button
GoogleSignInButton(
onPressed: () async {
var isError = false;
var authService = Provider.of<AuthService>(context, listen: false);
await authService.signInWithGoogle().catchError((errorMessage) async {
isError = true;
if (errorMessage != 'CANCELLED_SIGN_IN') {
await showPopup(context, title: 'Validation', child: Text(errorMessage));
}
});
if (isError == false) {
Navigator.pushReplacementNamed(context, HomeScreen.routeName);
}
},
),
You can simply catch Error like -
Future<void> _handleSignIn() async {
await googleSignIn.signIn().catchError((e) {
print(e);
});
}