firebase i have a problem of authentication - android

I am facing a problem in Firebase that sometimes it sends verify to the number and sometimes it does not send even though it did not exceed the limit allowed for sending in Firebase
my code
FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted: (PhoneAuthCredential credential) {
},
verificationFailed: (FirebaseAuthException e) {
setState(() {
isLoading=false;
});
print(e.message);
if (e.code == 'invalid-phone-number') {
print('The provided phone number is not valid.');
}
},
codeSent: (String verificationId, int? resendToken) async {
setState(() {
isLoading=false;
});
await Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return verifyCode(
verification: verificationId.toString(),
registerModel: user,
);
}));
},
timeout: const Duration(seconds: 60),
codeAutoRetrievalTimeout: (String verificationId) {},
);
}}
I'm using Flutter.

Related

http api is works on emulator but not on physical devices

when login button is pressed it should navigate to next page using api. it works on chrome emulator but when login button is pressed nothing happens in physical device. could there be problem with the codes?.
Future<void> login(String email, String password) async {
Map request = {'email': email, 'password': password, 'device_name': 'dell'};
try {
final response =
await http.post(Uri.parse('http://192.168.1.98:8081/api/login'),
body: jsonEncode(request),
headers: {
"Content-Type": "appliction/json",
},
encoding: Encoding.getByName("UTF-8"));
// ignore: avoid_print
print(response.statusCode);
if (response.statusCode == 200) {
// ignore: unused_local_variable
var data = jsonDecode(response.body.toString());
// ignore: use_build_context_synchronously
Navigator.push(context,
MaterialPageRoute(builder: (context) => const FrontPage()));
} else {
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text("Invalid Credentials")));
}
} catch (e) {
// ignore: avoid_print
print(e.toString());
}
}
ElevatedButton(
onPressed: () async {
login(emailController.text, passwordController.text);
if (_formkey.currentState!.validate()) {
return;
} else {
return;
}
},
child: const Text("Login"),
Add internet permission in your manifest
<uses-permission android:name="android.permission.INTERNET" />
also in login method
ElevatedButton(
onPressed: () async {
if (_formkey.currentState!.validate()) {
login(emailController.text, passwordController.text);
}
},
child: const Text("Login"),

Flutter: Check if existing user or new user after OTP Authentication

I am trying to check after the OTP Process whether a user is a returning user that exists in my firebase or a new user and if so should be taken to a page where he inputs his details in it.
I have already made the user go to the chat screen after verifying OTP, but I want it to check first to see if the user is existing or not so that I get extra details before he continues to the chat screen.
This is the authentication function that I use.
Future<void> phoneSignIn(
BuildContext context,
String phoneNumber,
) async {
TextEditingController codeController = TextEditingController();
try {
// FOR ANDROID, IOS
final newUser = await _auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(seconds: 120),
// Automatic handling of the SMS code
verificationCompleted: (PhoneAuthCredential credential) async {
// !!! works only on android !!!
await _auth.signInWithCredential(credential);
},
// Displays a message when verification fails
verificationFailed: (e) {
showSnackBar(context, e.message!);
},
// Displays a dialog box when OTP is sent
codeSent: ((String verificationId, int? resendToken) async {
showOTPDialog(
codeController: codeController,
context: context,
confirmOTP: () async {
PhoneAuthCredential credential = PhoneAuthProvider.credential(
verificationId: verificationId,
smsCode: codeController.text.trim(),
);
// !!! Works only on Android, iOS !!!
await _auth.signInWithCredential(credential);
Navigator.of(context).pop(); // Remove the dialog box
},
);
}),
codeAutoRetrievalTimeout: (String verificationId) {
// Auto-resolution timed out...
},
);
} catch (e) {
print(e);
}
}
and this is the OTP simple dialog to input the OTP code received
void showOTPDialog({
required BuildContext context,
required TextEditingController codeController,
required VoidCallback confirmOTP,
}) {
showDialog(
context:context,
barrierDismissible: false,
builder: (context)=> AlertDialog(
title: const Text("Enter OTP"),
content: Column(
mainAxisSize: MainAxisSize.min,
children:<Widget>[
TextField(
controller: codeController,
)
]
),
actions: <Widget>[
TextButton(
child:const Text('Confirm Code'),
onPressed: confirmOTP,
)
],
),
);
}```
This is how you can do
auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted: (PhoneAuthCredential credential) async {
final userCredential = await auth.signInWithCredential(credential);
final isNew = userCredential.additionalUserInfo?.isNewUser ?? false;
print('isNew $isNew'); // This is where you will get the value
},
verificationFailed: (FirebaseAuthException e) {},
codeSent: onOtpSentHandler,
codeAutoRetrievalTimeout: (String verificationId) {},
);

how do I verify a new user's email in Flutter and Firebase before granting them access to the app?

I am trying to have new users create an account, receive a verification email and then only be granted access to the app once they have clicked the verification link sent by Firebase. I am not getting any errors but there are clearly shortcomings in my code. The new user information is accepted and stored by Firestore and a verification email is sent but once the verification link is clicked the user remains on the Verify page instead of navigating to the HomePage (called Jobs Page in this code).
I cannot figure this out after days of trying so any help would be greatly appreciated. I have included several pages of my code. Thanks in advance!
abstract class AuthBase {
User? get currentUser;
Stream<User?> authStateChanges();
Future<User?> signInWithEmailAndPassword(String email, String password);
Future<void> createUserWithEmailAndPasswordVerify(
String email, String password);
Future<void> signOut();
}
class Auth implements AuthBase {
// Value that retrieves an instance of the FirebaseAuth object. This is used for managing users between the app and the Firebase backend
final _firebaseAuth = FirebaseAuth.instance;
#override
Stream<User?> authStateChanges() => _firebaseAuth.authStateChanges();
#override
User? get currentUser => _firebaseAuth.currentUser;
#override
Future<User?> signInWithEmailAndPassword(
String email, String password) async {
final userCredential = await _firebaseAuth.signInWithCredential(
EmailAuthProvider.credential(
email: email,
password: password,
),
);
return userCredential.user;
}
#override
Future<User?> createUserWithEmailAndPasswordVerify(
String email, String password) async {
final userCredential = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
try {
await userCredential.user?.sendEmailVerification();
} catch (e) {
print(
'An error occurred while trying to send email verification',
);
}
return userCredential.user;
}
#override
Future<void> signOut() async {
await GoogleSignIn().signOut();
await _firebaseAuth.signOut();
}
}
class VerifyPage extends StatefulWidget {
const VerifyPage({
Key? key,
}) : super(key: key);
#override
State<VerifyPage> createState() => _VerifyPageState();
}
class _VerifyPageState extends State<VerifyPage> {
AuthBase? auth;
Timer? timer;
User? user;
bool isUserEmailVerified = false;
#override
void initState() {
super.initState();
user = auth?.currentUser;
user?.sendEmailVerification();
timer = Timer.periodic(
const Duration(
seconds: 5,
),
(timer) {
checkEmailVerified();
},
);
}
Future<void> checkEmailVerified() async {
user = auth?.currentUser;
await user?.reload();
final signedInUser = user;
if (signedInUser != null && signedInUser.emailVerified) {
timer?.cancel();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const JobsPage(),
),
);
}
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Container(),
title: const Text('Verify Email'),
),
body: const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'An email has just been sent to your email. Click on the link provided to complete registration.',
style: TextStyle(
fontSize: 20.0,
),
),
),
),
);
}
}
code from email sign in form
Future<void> _submitVerify() async {
try {
await model.submitVerify(context);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const VerifyPage(),
),
);
} on FirebaseAuthException catch (e) {
showExceptionAlertDialog(
context,
exception: e,
title: 'Sign In Failed',
);
}
}
I think you just don't initiate Auth()
late AuthBase auth;
late Timer timer;
User? user;
bool isUserEmailVerified = false;
#override
void initState() {
super.initState();
auth = Auth();
user = auth.currentUser;
user?.sendEmailVerification();
timer = Timer.periodic(
const Duration(
seconds: 5,
),
(timer) {
checkEmailVerified();
},
);
}

Sign In widget is rebuilt everytime sign in button is clicked

AuthProvide Class*
class AuthProvider extends ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool get isSignedIn => _auth.currentUser != null;
User get currentUser => _auth.currentUser!;
String message = '';
Future<String> signIn(
{required String email, required String password}) async {
try {
await _auth.signInWithEmailAndPassword(email: email, password: password);
notifyListeners();
message = 'Successfully signed in';
return message;
} on FirebaseAuthException catch (e) {
message = getMessageFromErrorCode(e.code);
return message;
}
}
}
I call the isSignedIn getter in main using a consumer and return login screen or home screen based on the bool value.
SignIn Widget
SignInBar(
label: 'Sign in',
color: const Color(0xff092E34),
onPressed: () async {
FocusScope.of(context).unfocus();
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
String message = await context
.read<AuthProvider>()
.signIn(email: _email, password: _password);
SnackBar snackBar = SnackBar(content: Text(message));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
},
),
When SignIn onPressed is called my widget gets rebuilt and the form is cleared. how to stop it from rebuilding?
Ok so I don't know the specific reason for the above question but I tried with a different approach for auth provider and truly it has solved my problem.
I am posting this in the answer because I want to share this auth provider template with everyone since I faced lot problem finding one with null safety.
Auth Provider
class AuthProvider with ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
Future<String?> register(String email, String password) async {
setLoading(true);
try {
UserCredential authResult = await firebaseAuth
.createUserWithEmailAndPassword(email: email, password: password);
User? user = authResult.user;
setLoading(false);
notifyListeners();
return user != null ? null : 'An error Occurred';
} on SocketException {
setLoading(false);
notifyListeners();
return "No internet, please connect to internet";
} on FirebaseAuthException catch (e) {
setLoading(false);
notifyListeners();
return e.message;
}
}
Future<String?> login(String email, String password) async {
setLoading(true);
try {
UserCredential authResult = await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
User? user = authResult.user;
setLoading(false);
notifyListeners();
return user != null ? null : 'An error Occurred';
} on SocketException {
setLoading(false);
notifyListeners();
return "No internet, please connect to internet";
} on FirebaseAuthException catch (e) {
setLoading(false);
notifyListeners();
return e.message;
}
}
Future logout() async {
await firebaseAuth.signOut();
}
void setLoading(val) {
_isLoading = val;
notifyListeners();
}
Stream<User?> get user =>
firebaseAuth.authStateChanges().map((event) => event);
}
Main.dart
MultiProvider(
providers: [
ChangeNotifierProvider<AuthProvider>.value(value: AuthProvider()),
StreamProvider<User?>.value(
value: AuthProvider().user,
initialData: null,
)
],
child: MaterialApp(
title: 'Revora',
debugShowCheckedModeBanner: false,
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: Colors.teal,
accentColor: Colors.amber,
textTheme: AppTheme.textTheme,
appBarTheme: AppBarTheme(
backgroundColor: Colors.white,
elevation: 0.0,
),
platform: TargetPlatform.android,
),
home: Wrapper(),
),
);
If this is helpful pls upvote the answer so that everyone can gain from it.

Cloud Firestore Document Check after Phone Authentication

I will try to explain it as clearly as possible
I wanted to add a check when verification is complete. In that the check is supposed to be like: Check if there is a document with document id as the user UID which has authenticated. If it is there then go to the home. If it is not there then create a document using updateData class that I have created already and then go to the home page
Here is my code for phone authentication
Future phoneAuthentication(
String fullName,
String phoneNumber,
String phoneIsoCode,
String nonInternationalNumber,
String profilePicture,
String verificationCode,
BuildContext context,
) async {
_auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: Duration(seconds: 0),
verificationCompleted: (AuthCredential authCredential) async {
_auth.signInWithCredential(authCredential).then(
(UserCredential result) async {
User user = result.user;
await DatabaseService(uid: user.uid).updateUserData(
fullName,
phoneNumber,
phoneIsoCode,
nonInternationalNumber,
profilePicture,
);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => CustomerDashboard(),
),
(route) => false,
);
},
).catchError(
(e) {
return null;
},
);
},
verificationFailed: (FirebaseAuthException exception) {
return "Error";
},
codeSent: (String verificationId, [int forceResendingToken]) {
var _credential = PhoneAuthProvider.credential(
verificationId: verificationId,
smsCode: verificationCode,
);
_auth.signInWithCredential(_credential).then(
(UserCredential result) async {
User user = result.user;
await DatabaseService(uid: user.uid).updateUserData(
fullName,
phoneNumber,
phoneIsoCode,
nonInternationalNumber,
profilePicture,
);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => CustomerDashboard(),
),
(route) => false,
);
},
).catchError(
(e) {},
);
},
codeAutoRetrievalTimeout: (String verificationId) {
verificationId = verificationId;
},
);
}
Please help me how I am supposed to add this check.
I copied & adapted this code here from this Stack Overflow Question
DocumentSnapshot ds = await YOUR_DB_REFERENCE_IDENTIFIER.collection("YOUR_COLLECTION_NAME").document(user.uid).get();
return ds.exists;

Categories

Resources