I use Google sign in button with google package.
If user choose sign in with google, and if user login first time, then i want to create his/her account, but my code create when she/he login to app, everytime create like new user. I want to check with if statement, but how i do not know. If user uid exist in firestore, then it have not to be create new account.
This is my google sign in login function:
Future login() async {
isSigningIn = true;
final user = await googleSignIn.signIn();
if (user == null) {
isSigningIn = false;
return;
} else {
final googleAuth = await user.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
await FirebaseAuth.instance.signInWithCredential(credential);
FirebaseFirestore.instance.collection('users').add({
'email': user.email,
'username': user.displayName,
'uid': user.id,
'userPhotoUrl': user.photoUrl
});
isSigningIn = false;
}
}
Firestore console image here
If you want to store per-user information in Firestore, you should use the user's UID as the ID of the document. It's guaranteed to be unique per user. Don't use add() to add the document with a random ID, use set() with the known ID to read and create the document.
Also, you should know that signInWithCredential returns the known user object, which lets you get their UID.
UserCredential userCrediental = await FirebaseAuth.instance.signInWithCredential(credential);
String uid = userCredential.user.uid;
await FirebaseFirestore.instance.collection('users').doc(uid).set(...)
If you want to check if the document was already created for that user, you can simply try to get() it first and see if it exists.
I solve this problem with my friend which is good person.
I replace this code :
await FirebaseAuth.instance.signInWithCredential(credential);
because there is a logical problem.
Related
Well I'm using the code from Firebase and the in the user object the photoURL which being returned is General facebook profile with no image
which is generic profile picture
Future signInWithFacebook() async {
try {
// Trigger the sign-in flow
final result = await FacebookAuth.instance.login();
// Create a credential from the access token
final FacebookAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(result.token);
// Once signed in, return the UserCredential
UserCredential res = await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
User user = res.user;
//create a new document for the user with the uid
await UserProfileDatabaseService(uid: user.uid).updateUserData(
user.displayName,
user.email,
user.emailVerified,
user.phoneNumber,
user.isAnonymous
, {'helper': true},
250
); return user;
} catch (e) {
print(e.toString());
return null;
}
}
you can use the following code to display the Facebook profile picture URL after a successful login:
await FacebookAuth.instance.login();
final user = await FacebookAuth.instance.getUserData();
Image.network(user["picture"]['data']['url']);
This code logs the user in via FacebookAuth, retrieves their user data, and displays their profile picture using the Image.network widget.
I'm trying but failing to re-trigger the authentication steps that the user gets taken through when they authenticate themselves using Google sign-in, following deletion of the user. The deleted user simply gets signed in immediately (instead of being taken through the authentication steps), when using Google sign-in the second time. I want to be able to re-trigger the authentication steps for my own testing purposes.
Specifically, I've got a user who I've authenticated and signed in as per the FlutterFire documentation, i.e.
Future<UserCredential> signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
// Create a new credential
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
return await FirebaseAuth.instance.signInWithCredential(credential);
}
I then proceed to delete the user; again, as per the FlutterFire documentation, i.e.
try {
await FirebaseAuth.instance.currentUser.delete();
} catch on FirebaseAuthException (e) {
if (e.code == 'requires-recent-login') {
print('The user must reauthenticate before this operation can be executed.');
}
}
That works, insomuch as the user is no longer listed amongst the authenticated users in the Firebase console. However, if I now proceed to call signInWithGoogle() again, then instead of getting taken through the authentication steps again (i.e. being prompted to enter an email, password, etc.), the user simply gets signed in straight away. It's as if the user hasn't been properly deleted. How would I go about re-triggering the authentication steps?
You must also call GoogleSignIn().signOut() after the Firebase sign out or delete.
In my case, I had to reauthenticate firebase user inside the delete functions try-catch as currentUser() always return null AND GoogleSignIn().signOut() didnt work. Maybe a bug.
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
//will need to sign in to firebase auth again as currentUser always returns null
//this try-catch block should be inside the function that deletes user
try {
//FirebaseUser user = await _auth.currentUser(); //returns null so useless
//signin to google account again
GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
//get google credentials
AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleSignInAuthentication.idToken,
accessToken: googleSignInAuthentication.accessToken);
//use credentials to sign in to Firebase
AuthResult authResult = await _auth.signInWithCredential(credential);
//get firebase user
FirebaseUser user = authResult.user;
print(user.email);
//delete user
await user.delete();
//signout from google sign in
await _googleSignIn.signOut();
} catch (e) {
print('Failed to delete user ' + e.toString());
}
People can sign into my app anonymously:
FirebaseAuth.instance.signInAnonymously();
Once inside, they have a little indicator that reminds them if they want be able to save their data across phones, they'll need to sign in. For Google authentication that looks like this:
Future<void> anonymousGoogleLink() async {
try {
final user = await auth.currentUser();
final credential = await googleCredential();
await user.linkWithCredential(credential);
} catch (error) {
throw _errorToHumanReadable(error.toString());
}
}
where googleCredential is:
Future<AuthCredential> googleCredential() async {
final googleUser = await _googleSignIn.signIn();
if (googleUser == null) throw 'User Canceled Permissions';
final googleAuth = await googleUser.authentication;
final signInMethods = await auth.fetchSignInMethodsForEmail(
email: googleUser.email,
);
if (signInMethods.isNotEmpty && !signInMethods.contains('google.com')) {
throw 'An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.';
}
return GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
}
Now I'd like to do the same thing but instead link the current anonymous account with email and password authentication.
EDIT:
Found a possible solution while writing this. Leaving for others to possibly see. Feel free to correct me.
On the FirebaseUser object the method updatePassword has this doc string:
/// Updates the password of the user.
///
/// Anonymous users who update both their email and password will no
/// longer be anonymous. They will be able to log in with these credentials.
/// ...
You should just be able to update both and then they'll be authenticated as a regular user. For me that looked like:
Future<void> anonymousEmailLink({
#required FirebaseUser user,
#required String email,
#required String password,
}) async {
try {
await Future.wait([
user.updateEmail(email),
user.updatePassword(password),
]);
} catch (error) {
throw _errorToHumanReadable(error.toString());
}
}
I have implemented Facebook and Google sign in.
But FireBase document says this will cause an error if the same user first signs up with Facebook and later try sign in with Google (with the same email).
So I follow doc and try to configure account linking.
But I do not know how to do.
Should I try link account every time user is logged in? Problem is I not know if the user already has signed in with another auth provider.
For example, the original code has:
Google:
void _signInWithGoogle() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final FirebaseUser user = await _auth.signInWithCredential(credential);
}
Facebook:
void _signInWithFacebook() async {
final AuthCredential credential = FacebookAuthProvider.getCredential(
accessToken: _tokenController.text,
);
final FirebaseUser user = await _auth.signInWithCredential(credential);
}
Is correct to call every time in _signInWithFacebook() and _signInWithGoogle() :
user = await auth.linkWithCredential(credential);
For example:
void _signInWithFacebook() async {
final AuthCredential credential = FacebookAuthProvider.getCredential(
accessToken: _tokenController.text,
);
final FirebaseUser user = await _auth.signInWithCredential(credential);
user = await auth.linkWithCredential(credential); //new
}
How I can implement correctly?
Thanks!
When the user enters their email address to sign in, you'll want to use fetchSignInMethodsForEmail() to find out if that email address is already known.
If a user has already signed up with another provider, that's a good moment to ask them if they want to merge those accounts, and then call the account linking API.
I'm creating app using Flutter with Firebase. I have some weird issues. I'm creating authentication and it is working fine but when i try to add some collections to my Firestore Database, the record of displayName is set to null.
Future<FirebaseUser> createUser(email, password, displayName) async {
final FirebaseUser user = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
UserUpdateInfo info = new UserUpdateInfo();
info.displayName = displayName;
_auth.updateProfile(info);
Firestore.instance.collection('users').document().setData({
'name': user.displayName,
'uid': user.uid,
'email': user.email,
'isEmailVerified': user.isEmailVerified,
'photoUrl': user.photoUrl,
});
return user;
}
this is Future class that creates user.
void _handleSubmitted() {
userAuth
.createUser(
emailController.text, passwordController.text, nameController.text)
.then((onValue) {
print("Sign Up button clicked: $onValue");
});
}
this method is handle when sign up button is clicked.
and collection looks like this picture.
If I recall correctly, the local user profile is not immediately updated when you call updateDisplayName or updatePhotoURL. This means you should either just write the local values to the database (easiest) or force a reload of the profile (safest).
Write the local values
As said, this is the simplest approach:
FirebaseFirestore.instance.collection('users').doc().set({
'name': displayName,
'uid': user.uid,
'email': user.email,
'isEmailVerified': user.emailVerified, // will also be false
'photoUrl': user.photoURL, // will always be null
});
Note that emailVerified will always be false, and photoUrl will always be null on a newly created email+password account.
Force a reload of the profile
You can force a reload of the user data by calling FirebaseUser.reload():
await _auth.currentUser!.updateDisplayName(displayName);
await _auth.currentUser!.updateEmail(newEmail);
await _auth.currentUser!.updatePhotoURL(photoURL);
await _auth.currentUser!.reload();
user = _auth.currentUser;
To Update users display name and photo url this will definitely helpful.
FirebaseUser user = await FirebaseAuth.instance.currentUser();
UserUpdateInfo userUpdateInfo = new UserUpdateInfo();
userUpdateInfo.displayName = name;
userUpdateInfo.photoUrl = url;
user.updateProfile(userUpdateInfo);
It's inconvenient the way Firebase Auth works, and they say it won't change for now, so I made a package to address this, grab it here: firebase_user_stream
In the Readme I explain the issues and how the package fixes them, there are examples and etc, enjoy!
EDIT (Jul/2020):
Firebase Auth for Flutter now has a userChanges stream:
which triggers whenever auth state, id token or profile changes occur. Essentially, this acts as a way to obtain realtime changes on currently stateless functionality (such as updateProfile).
I also tried everything but however, this works for me! Just call the FirebaseAuth.instance twice like the one in the code.
FirebaseUser currentUser = await _auth.currentUser();
await currentUser.reload();
currentUser = await _auth.currentUser();
print("Current User ${currentUser.displayName}");
print("Current User ${currentUser.photoUrl}");
hope this helps you!