Error Firebase "PlatformException(ERROR_INVALID_EMAIL" on createUserWithEmailAndPassword - firebase

I got this code to signup user with email and password in firebase. But the problem is that the try seems doesn't work. It keeps display an exception error message even when I have already put catch there.
static Future<FirebaseUser> signUp(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
FirebaseUser firebaseUser = result.user;
return firebaseUser;
} catch (e) {
print(e.toString());
return null;
}
}

The error says your email is not properly formatted i.e. it's not matching the username#domain.tld format or is undefined. Try print(email) before the createUserWithEmailAndPassword function and check it.
Also adding an if statement helps:
if (["", null].contains(email)) {
print("Email is null")
}
Validate email in dart might be useful but shouldn't be necessary as Firebase will throw an error is email is not valid.

Related

Future<UserData> returns a Future<UserData> instead of a UserData variable

I have a widget that uses a Future - async that returns a UserData variable, but for some reason, I'm getting a Future variable instead.
a line of code from a widget Build:
dynamic user = Auth.signIn(_email, _password);
Auth Class:
Future<UserData> signIn(String email, String password) async {
try{
UserCredential userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password
);
print('signed in!');
return _createUserFromFirebase(userCredential.user.uid, null);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
return null;
}
}
You could try something like
return Future.value(_createUserFromFirebase(userCredential.user.uid, null);
instead of
return _createUserFromFirebase(userCredential.user.uid, null);
Async cannot be used without it being in a function with await. You can't randomly retrieve an asynchronous variable randomly in the script without loading it asynchronously, because the future might not have the data ready to be used.
If you need the future variable's future value to be in a widget try using the FutureBuilder widget. This will make it so that the user data is loaded asynchronously and a place holder of your choice can be shown before it loads. This would allow you to use the UserData type in your widget.
Or you can get the user by having the button have an async variable that sets a variable in the widget from null to the UserData after signing in.
I've added a new future method inside the widget class that saves the user in a variable:
Future<void> getUserData(String email, String password) async{
_user = await _auth.signIn(email, password);
print('user: $_user');
}

Update a Firebase anonymous user using Email & Password

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

Firebase + Flutter: can't lock access to unverified email accounts

I'd like to block out people who didn't verify their email so i figured out this code for sign up:
// sign up
Future signUp(String email, String password) async {
try {
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
} catch (e) {
print('An error has occured by creating a new user');
print(
e.toString(),
);
}
try {
final FirebaseUser _user = await _auth.currentUser();
await _user.sendEmailVerification();
} catch (error) {
print("An error occured while trying to send email verification");
print(error.toString());
}
try {
await _auth.signOut();
} catch (err) {
print(err);
}
}
and this for sign in:
//Sign In with Email and Pass
Future signInWithEmailAndPassword(String email, String password) async {
FirebaseUser _user = await FirebaseAuth.instance.currentUser();
if (_user != null && _user.isEmailVerified == true) {
try {
await _auth.signInWithEmailAndPassword(
email: email, password: password);
return _user;
} catch (e) {
return null;
}
} else {
return null;
}
}
_auth is just an instance of FirebaseAuth.
The problem is that i can login even if i didnt verify the email.
Firebase Auth doesn't stop accounts from signing in if the user hasn't verified their email address yet. You can check that property _user.isEmailVerified to find out the state of that validation after the user signs in, and you can determine from there what the user should see.
isEmailVerified can be a little bit of trouble to get working correctly.
Make sure you are calling
await FirebaseAuth.instance.currentUser()..reload();
before your are calling isEmailVerified also in my own experience and I don't know if this is just something I was doing wrong but this did not work from my Auth class this did not start working until I put the code directly in initState() of my widget that checks whether the user is verified. Like I said that part might have been something I did wrong. Like stated this will not listen for change you must check yourself either periodically or at a point that you know email is verified.
Future(() async {
_timer = Timer.periodic(Duration(seconds: 10), (timer) async {
await FirebaseAuth.instance.currentUser()
..reload();
var user = await FirebaseAuth.instance.currentUser();
if (user.isEmailVerified) {
timer.cancel();
Navigator.of(context).popAndPushNamed(HearingsScreen.routeName);
}
});
});
So it checks every 10 seconds to see if the user has verified their email not the most elegant solution. The page I have this on just displays a message 'Please verify your email' so its not like this is interrupting other code. If your app is performing other tasks this might not be an option for you. If you want to play around with isEmailVerified go ahead but i spent a week of headaches until i settled on this.

Flutter : How to implement a callback

How to implement a callback return the error message?
Login function from AuthService class:
static void login(String email, String password) async {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
} catch (e) {
print(e);
}
}
Submit function from Login class:
_submit() {
// If fail to login then return the error message
AuthService.login(_email, _password);
}
Try the following:
Future<AuthResult> login(String email, String password) async {
try {
Future<AuthResult> result = await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
return result;
} catch (e) {
print(e);
}
}
Then you can call the method like this:
_submit() {
// If fail to login then return the error message
login(_email, _password).then((result) => {
print(result);
});
}
The method signInWithEmailAndPassword returns Future<AuthResult>, therefore assign it to that type, the await keyword will wait until the method finishes execution and then it will return the value of type Future<AuthResult>.
A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed.
When you call the method login(), you can add the then() method which registers callbacks to be called when this future completes.
When this future completes with a value, the onValue callback will be called with that value.
https://api.dartlang.org/stable/2.7.0/dart-async/Future/then.html
// wrapping the firebase calls
Future<FirebaseUser> loginUser({String email, String password}) {
return FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password);
}
the user info is returned, you not sure what you need the callback for?
userInfo = await AuthService().loginUser(email: _email, password: _password);

Flutter: Firebase authentication create user without logging In

I have a user management feature in my flutter app that uses firebase authentication. I can register new user accounts using firebase_auth's createUserWithEmailAndPassword() function.
return await FirebaseAuth.instance.
createUserWithEmailAndPassword(email: email, password: password);
The problem is when the registration is successful it automatically authenticates my FirebaseAuth instance as the new user even though I am already logged in.
I came across this answer: Firebase kicks out current user but it's in javascript and has a slightly different api.
How can I do the equivalent in dart?
Updated: firebase_core ^0.5.0 and firebase_auth ^0.18.0+1 has deprecated some of the old classes.
Below is code updated for firebase_core ^0.5.1 and firebase_auth ^0.18.2.
static Future<UserCredential> register(String email, String password) async {
FirebaseApp app = await Firebase.initializeApp(
name: 'Secondary', options: Firebase.app().options);
try {
UserCredential userCredential = await FirebaseAuth.instanceFor(app: app)
.createUserWithEmailAndPassword(email: email, password: password);
}
on FirebaseAuthException catch (e) {
// Do something with exception. This try/catch is here to make sure
// that even if the user creation fails, app.delete() runs, if is not,
// next time Firebase.initializeApp() will fail as the previous one was
// not deleted.
}
await app.delete();
return Future.sync(() => userCredential);
}
Original Answer
I experimented with the firebase authentication api and my current working solution is:
// Deprecated as of `firebase_core ^0.5.0` and `firebase_auth ^0.18.0`.
// Use code above instead.
static Future<FirebaseUser> register(String email, String password) async {
FirebaseApp app = await FirebaseApp.configure(
name: 'Secondary', options: await FirebaseApp.instance.options);
return FirebaseAuth.fromApp(app)
.createUserWithEmailAndPassword(email: email, password: password);
}
Essentially it comes down to creating a new instance of FirebaseAuth so the automatic login from createUserWithEmailAndPassword() do not affect the default instance.
Based on Swift's answer I post updated code for this work around.
Future signUpWithEmailPasswordAdminPastor(String email, String password, String role, String nama, String keuskupan, String kevikepan, String paroki) async {
try {
FirebaseApp tempApp = await Firebase.initializeApp(name: 'temporaryregister', options: Firebase.app().options);
UserCredential result = await FirebaseAuth.instanceFor(app: tempApp).createUserWithEmailAndPassword(
email: email, password: password);
// create a new document user for the user with uid
await DatabaseService(uid: result.user.uid).updateUserDataSelf(
true,
role,
nama,
keuskupan,
kevikepan,
paroki,
'',
DateTime.now(),
DateTime.now());
tempApp.delete();
return 'OK';
} catch (e) {
print(e.toString());
return null;
}
}
Above code works for firebase_core: ^0.5.0 and firebase_auth: ^0.18.0+1.

Resources