I need to add email/password authentication with flutter and firebase - firebase

I have a Google authentication working just fine, and I need to create another signUp method (with email/and password), so what are the best approaches for implementing this signUp method?
I tried to create a user and it worked. But then I can't navigate to my other pages!
I couldn't find any good blog posts or documentation. Note that when the user signs up I also need to add them in firebase Database.

Try something like this:
emailPasswordLogin(BuildContext context, String email, String password)async{
try {
AuthResult authResult = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = authResult.user;
} catch (ex) {
print("Code: " + ex.code);
switch (ex.code) {
case 'ERROR_USER_NOT_FOUND':
{
await createUser(email, password, context);
}
case 'ERROR_WRONG_PASSWORD':
print("wrong password);
break;
case 'ERROR_INVALID_EMAIL':
print("invalid email")
break;
case 'ERROR_OPERATION_NOT_ALLOWED':
print("Login Method not defined")
break;
case 'ERROR_WEAK_PASSWORD':
print("weak password")
break;
default:
print('Case ${ex.message} is not yet implemented');
}
return;
}
storeData(user, context);
}
storeData(FirebaseUser user, BuildContext context) async {
_database = new FirebaseDatabase();
await _database
.reference()
.child('user')
.push().set(<dynamic, dynamic> async{
"email": user.email,
}).then((_) {
print("Transaction commited");
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NextPage());
});
}

Related

Flutter Firebase: Error when user tries to register with an already existing email

I'm currently testing my app, when I try to register a new account with an email already it in use, I get the following error:
ArgumentError (Invalid argument(s) (onError): The error handler of Future.catchError must return a value of the future's type)
When I click sign in the app still informs the user that this email is already in use but the error crashes the app. If I try to test any of the other errors, my app does not crash.
I'm not quite sure how to begin to resolve this due to not really being sure why is error is occurring.
*.dart
void signUp(String email, String password) async {
if (GlobalKey<FormState>().currentState!.validate()) {
try {
await FirebaseAuth.instance //<--- Error stops here
.createUserWithEmailAndPassword(email: email, password: password)
.then((value) => {postDetailsToFirestore()})
.catchError((e) {
Fluttertoast.showToast(msg: e!.message);
});
} on FirebaseAuthException catch (error) {
switch (error.code) {
case "invalid-email":
errorMessage = "Your email address appears to be incorrect.";
break;
case "wrong-password":
errorMessage = "Your password is wrong.";
break;
case "user-not-found":
errorMessage = "User with this email doesn't exist.";
break;
case "user-disabled":
errorMessage = "User with this email has been disabled.";
break;
case "too-many-requests":
errorMessage = "Too many requests";
break;
case "operation-not-allowed":
errorMessage = "Signing in with Email and Password is not enabled.";
break;
default:
errorMessage = "An undefined Error happened.";
}
Fluttertoast.showToast(msg: errorMessage!);
print(error.code);
}
}
}
postDetailsToFirestore() async {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
User? user = _auth.currentUser;
UserModel userModel = UserModel();
// writing all the values
userModel.email = user!.email;
userModel.uid = user.uid;
userModel.userName = NameEditingController.text;
userModel.password = passwordEditingController.text;
await firebaseFirestore
.collection("users")
.doc(user.uid)
.set(userModel.toMap());
Fluttertoast.showToast(msg: "Account created successfully ");
Navigator.pushAndRemoveUntil(
(context),
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false);
}
async/await is just a syntactic sugar to handle promises. It makes your code more readable, and combining it with try/catch you can have a more clear way of handling exceptions.
I also recommend to handle the situation if the mobile device does not have Internet access, and a SocketException is thrown. For this kind of exception you need import 'dart:io';
Don't forget to add a simple catch as well, because you can have exceptions other than FirebaseAuthException and SocketException, and these will be not caught if you use only on.(You don't need the catch after on.)
Try it this way:
try {
await FirebaseAuth.instance.createUserWithEmailAndPassword(email: email,
password: password);
await postDetailsToFirestore();
} on FirebaseAuthException (error) {
...
} on SocketException {
...
} catch (e) {
..
}

FlutterFire authentication using role authentication

I'm trying to sign in based on the role of the user from firebase. But mostly, the tutorials i saw are using older firebase version, therefore I'm using firebase documentation and my logic.
here is my function for auth role sign in
Future<User?> signIn(String email, String password) async {
try {
UserCredential userCredential = await _auth.signInWithEmailAndPassword(
email: email, password: password);
User? user = userCredential.user;
await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get()
.then((DocumentSnapshot documentSnapshot) {
var docData = documentSnapshot.data();
var role = docData['role'];
if (documentSnapshot.data()['roles'] == 'dosen') {
return 'ok';
} else {
return 'err';
}
});
return user;
} 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.');
}
}
}
but mostly having problem with this section
the error is like this
"The method '[]' can't be unconditionally invoked because the receiver can be 'null'. Try making the call conditional (using '?.') or adding a null check to the target ('!')."
await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get()
.then((DocumentSnapshot documentSnapshot) {
var docData = documentSnapshot.data();
var role = docData['role'];
if (documentSnapshot.data()['roles'] == 'dosen') {
return 'ok';
} else {
return 'err';
}
});
Thank you.
---------------EDIT---------------
i've solved it using this
Map<String, dynamic> data =
documentSnapshot.data()! as Map<String, dynamic>;
so then i can pass the data using string, in which i call it like this
role = data['role'].toString();
thank you for the help :)
REPLACE THIS:
if (documentSnapshot.data()['roles'] == 'dosen')
WITH:
if (documentSnapshot.data()!['roles'] == 'dosen')

Having problem with login with Flutter Firebase email and password authentication

I have problems with the Flutter code for the Firebase email/password sign-in.
When email address and password are correct, nothing happens.
When email address or password are not correct the "Error "+errMsg.toString() is generated.
In console I read:
W/System (21892): Ignoring header X-Firebase-Locale because its value
was null. D/FirebaseAuth(21892): Notifying id token listeners about
user ( R6GCSXPvbUgbu4uQXdLYtXD1Lyo1 ).
Here is the code:
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
void loginAndAuthenticateUser(BuildContext context) async{
final User firebaseUser = (await _firebaseAuth.signInWithEmailAndPassword(
email: emailTextEditingController.text,
password: passwordTextEditingController.text)
.catchError((errMsg) {
displayToastMessage("Error " + errMsg.toString(), context);
})).user;
if (firebaseUser != null) {
usersRef.child(firebaseUser.uid).once().then((DataSnapshot snap){
if (snap.value == null){
Navigator.pushNamedAndRemoveUntil(context, MainScreen.idScreen, (route) => false);
displayToastMessage("You are logged in.", context);
}
else{
_firebaseAuth.signOut();
displayToastMessage("User not found.", context);
}
});
}
else{
displayToastMessage("Error occurred.", context);
}
}
displayToastMessage(String message, BuildContext context) {
Fluttertoast.showToast(msg: message);
}
In this case the best thing that I know to do is do a "try" "except" statement, I have definitely come across this problem before and wish it was easier to manage.

Flutter - Get Firebase custom claims while writing Firebase user to own user instance

I am trying to implement the example given at How do I access custom claims? to my existing code.
I have a Stream which listens to auth changes and updates my own user object with the responded Firebase user. When I store my user object, I would like to get the custom claims of that user as well.
The problem is in _userFromFirebaseUser.
It says "The await expression can only be used in an async function.
Try marking the function body with either 'async' or 'async*'."
But when I do so, the error is hops to my stream where it then says "The argument type 'Future Function(User)' can't be assigned to the parameter type 'User Function(User)'." for "_userFromFirebaseUser" in
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
Here is my complete authentication class:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
local.User _userFromFirebaseUser(auth.User user) {
final isAdmin = (await _currentUserClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _currentUserClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Am I heading into the wrong direction? Is there anything obvious, that I simply do not consider?
Thanks for your help!
For those, heading into the same problem, I found the solution after further research:
You will have to change the .map to .asyncMap.
Here is the code, which works for me:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
Future<local.User> _userFromFirebaseUser(auth.User user) async {
final isAdmin = (await _userClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().asyncMap(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _userClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Found here: In flutter, how can I "merge" Firebase onAuthStateChanged with user.getTokenId() to return a Stream?

Flutter Firestore adding data

I want to add data on firestore and it wont work. can somebody help me.
This is the newest updated version and I can't figure out how...
firebase_auth: ^0.18.0+1 cloud_firestore: ^0.14.0+2
This is the sign up screen so I want to send data after I create the email and password.
I want to add the document with user uid too.
onPressed: () async {
try {
UserCredential userCredential = await FirebaseAuth
.instance
.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
if (userCredential != null) {
firestore
.collection("user")
.doc('user.uid')
.set({
'username': username,
'email': email,
})
.then((value) => print("User Added"))
.catchError((error) =>
print("Failed to add user: $error"));
Navigator.of(context).pushNamed(AppRoutes.authLogin);
}
} catch (e) {
print(e);
_usernameController.text = "";
_passwordController.text = "";
_repasswordController.text = "";
_emailController.text = "";
//TODO: alertdialog with error
}
setState(() {
saveAttempted = true;
});
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
}
},
Can someone help me with the firestore.. Thank you..
First Create a User class.
class UserData {
final String userId;
final String fullNames;
final String email;
final String phone;
UserData(
{this.userId,
this.fullNames,
this.email,
this.phone});
Map<String, dynamic> getDataMap() {
return {
"userId": userId,
"fullNames": fullNames,
"email": email,
"phone": phone,
};
}
}
Then you can use a function like this one to save the credentials and save the data to firestore
createOrUpdateUserData(Map<String, dynamic> userDataMap) async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
DocumentReference ref =
Firestore.instance.collection('user').document(user.uid);
return ref.setData(userDataMap, merge: true);
}
==
bool validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
void validateAndSubmit() async {
if (validateAndSave()) {
try {
String userId = _formType == FormType.login
? await widget.auth.signIn(_email, _password)//use your signin
: await widget.auth.signUp(_email, _password);//use your signup
if (_formType == FormType.register) {
UserData userData = new UserData(
fullNames: _fullNames,
email: _email,
phone: "",
);
createOrUpdateUserData(userData.getDataMap());
}
} catch (e) {
setState(() {
_isLoading = false;
switch (e.code) {
case "ERROR_INVALID_EMAIL":
_authHint = "Your email address appears to be malformed.";
break;
case "ERROR_EMAIL_ALREADY_IN_USE":
_authHint = "Email address already used in a different account.";
break;
case "ERROR_WRONG_PASSWORD":
_authHint = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
_authHint = "User with this email doesn't exist.";
break;
case "EMAIL NOT VERIFIED":
_authHint = "Email not verified: Please go to yor email and verify";
break;
case "ERROR_USER_DISABLED":
_authHint = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
_authHint =
"Too many Attemps. Account has temporarily disabled.\n Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
_authHint = "Signing in with Email and Password is not enabled.";
break;
case "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL":
_authHint = "The email is in use by another account";
break;
default:
_authHint = "An undefined Error happened.";
}
});
print(e);
errorDialog(context, _authHint);
}
} else {
setState(() {
_authHint = '';
});
}
}
Then use
onpressed:(){
validateAndSubmit();
}
the formtype is an Enum
enum FormType { login, register, reset }
widget.auth.signIn and widget.auth.signUp should be replaced with your signin and signup respectively.
Added a custom error block to differentiate firebase auth errors as well.
Defining an auth page independently will help you reuse your code in future.

Resources