Update a Firebase anonymous user using Email & Password - firebase

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());
}
}

Related

Check if user is logged in or not before authentication using google sign in - flutter

In my flutter app, am trying to check if a user is logged in or not before authenticating the user in firebase, so if he is not then do not authenticate
Future<String> loginUserWithGoogle() async {
String returnValue = "error";
GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: [
'email',
'https://www.googleapis.com/auth/contacts.readonly',
],
);
UserData _user = UserData();
try {
GoogleSignInAccount _googleUser = await _googleSignIn.signIn();
GoogleSignInAuthentication _googleAuth = await _googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(idToken: _googleAuth.idToken, accessToken: _googleAuth.accessToken);
UserCredential _authResult = await _auth.signInWithCredential(credential);
if (_authResult.additionalUserInfo.isNewUser) {
String userGoogleName = _authResult.user.displayName;
List userSplitName = userGoogleName.split(" ");
String userGoogleFirstName = userSplitName.first;
String userGoogleLastName = userSplitName.last;
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('googleUid', _authResult.user.uid);
await prefs.setString('googleEmail', _authResult.user.email);
await prefs.setString('googleFirstName', userGoogleFirstName);
await prefs.setString('googleLastName', userGoogleLastName);
await prefs.setString('googleUserType', "user");
returnValue = "new";
} else {
_currentUser = await RealDatabase().getUserData(_authResult.user.uid);
if (_currentUser != null) {
returnValue = "success";
}
}
} on PlatformException catch (e) {
returnValue = e.message;
} catch (e) {
print(e);
}
return returnValue;
}
}
Here what I want to check is that if it is a new user then save his google data in sharedpreference and take him to another page where he can complete some other registration and then sign him in. but what this code does is that if it is a new user it will authenticate, save the info in sharedpeference and then take him to the page and if maybe that user decided to go back to the previous page (since i use Navigator.push(context)) and still click the google sign in button again then it will take him to the home screen without him completing the other registration I want him to do because it already authenticated him first. So please is there a way to do this without first authenticating the user.
You can use stream provider to control if user logged in or not.
Here is an example how to use stream provider in your project;
https://flutterbyexample.com/lesson/stream-provider.

How can i check user id in login section?

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.

Flutter Firebase - failing to properly delete a Google authenticated user

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());
}

How to delete firebase account when user data is deleted on flutter?

is it possible to delete firebase account in authentication on flutter? if yes, how to do that? I have been search but not found the way.
Firestore.instance.collection("users").document(uid).delete().then((_){
// delete account on authentication after user data on database is deleted
});
Using flutter, if you want to delete firebase accounts together with the associated firestore user collection document, the following method works fine. (documents in user collection named by the firebase uid).
Database Class
class DatabaseService {
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection =
Firestore.instance.collection('users');
Future deleteuser() {
return userCollection.document(uid).delete();
}
}
Use Firebase version 0.15.0 or above otherwise, Firebase reauthenticateWithCredential() method throw an error like { noSuchMethod: was called on null }.
Authentication Class
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future deleteUser(String email, String password) async {
try {
FirebaseUser user = await _auth.currentUser();
AuthCredential credentials =
EmailAuthProvider.getCredential(email: email, password: password);
print(user);
AuthResult result = await user.reauthenticateWithCredential(credentials);
await DatabaseService(uid: result.user.uid).deleteuser(); // called from database class
await result.user.delete();
return true;
} catch (e) {
print(e.toString());
return null;
}
}
}
Then use the following code inside the clickable event of a flutter widget tree to achieve the goal;
onTap: () async {
await AuthService().deleteUser(email, password);
}
Code for deleting user:
FirebaseUser user = await FirebaseAuth.instance.currentUser();
user.delete();
To delete a user account, call delete() on the user object.
For more on this, see the reference documentation for FirebaseUser.delete().
User user = FirebaseAuth.instance.currentUser;
user.delete();
From this you can delete user

Example of Firebase Auth with Email and Password on Flutter?

First time implementing Firebase Auth, also new to Flutter dev, and I'm looking to use email and passwords, not Google sign-in. The only examples I see of Firebase Auth being used are anonymously or with Google. Is there a resource / tutorial that shows how to properly set up the calls with the signInWithEmailAndPassword method?
Many thanks!
In my app I have an auth class because I don't like to litter my widget classes with non-widget code, but you can plop this code inside your widget class if you want.
In auth.dart I have:
import 'package:firebase_auth/firebase_auth.dart';
class Auth {
final FirebaseAuth auth = FirebaseAuth.instance;
Future<User> handleSignInEmail(String email, String password) async {
UserCredential result =
await auth.signInWithEmailAndPassword(email: email, password: password);
final User user = result.user!;
return user;
}
Future<User> handleSignUp(email, password) async {
UserCredential result = await auth.createUserWithEmailAndPassword(
email: email, password: password);
final User user = result.user!;
return user;
}
}
Then in my login/register screen I create an instance of my auth class:
var authHandler = new Auth();
Finally, in my login buttons onPressed callback I have:
onPressed: () {
authHandler.handleSignInEmail(emailController.text, passwordController.text)
.then((FirebaseUser user) {
Navigator.push(context, new MaterialPageRoute(builder: (context) => new HomePage()));
}).catchError((e) => print(e));
}
Here is one example from my service. I ask for the email, name and password fields.
registerWithEmail(String name, String email, String password) async {
First I check if the email is already registered with facebook or something.
List<String> providers = await firebaseAuth.fetchProvidersForEmail(email: email);
This is not ideal solution, but you want to handle if the user is already registered, and than link email with current user
if (providers != null && providers.length > 0) {
print("already has providers: ${providers.toString()}");
return handleProviders(providers);
}
Create new User
FirebaseUser newUser = await firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
await newUser.sendEmailVerification();
This is basically it. I update the name filed of the user, since I have it.
var userUpdateInfo = new UserUpdateInfo();
userUpdateInfo.displayName = name;
await firebaseAuth.updateProfile(userUpdateInfo);
await newUser.reload();
And later You can save the user inside your firestore, where user uid is document ID. So I can get it later.
Git repo with full explanation example for Email/Password Sign in Firebase using flutter
This may help you.
Git Repo: https://github.com/myvsparth/firebase_email_signin
Full Article Step by Step: https://www.c-sharpcorner.com/article/how-to-do-simple-login-with-email-id-in-flutter-using-google-firebase/
This code is for register new user
fb_auth.UserCredential userCredential = await fb_auth
.FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
And this is for login previous user
fb_auth.UserCredential userCredential = await fb_auth
.FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password)
And that fb_auth is
import 'package:firebase_auth/firebase_auth.dart' as fb_auth;

Resources