flutterfirebasedartfirebase-realtime-databasefirebase-authentication

Firebase Authentication: Email validation always returns "Email not registered" in Flutter


I'm working on a Flutter app where I'm using Firebase Authentication for email and password sign-in. The app has a flow where users first enter their email on the sign-in screen. After they click "Continue," the app checks if the email is registered in Firebase. If it's not registered, they get a message asking them to create an account. If the email is registered, they are taken to a second screen where they can enter their password.

The issue I'm facing is that even when I enter an email that is registered in Firebase (I can see it in the Firebase Console), it still returns the message "Email is not registered."

Here is the relevant code for the email screen, the first is the function I created to check if the email is verified:

Future<void> checkEmail() async {
  String email = emailController.text.trim();

  setState(() {
    isLoading = true;
  });

  try {
    // Attempt to fetch sign-in methods for the email
    List<String> signInMethods =
        await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);

    if (signInMethods.isEmpty) {
      // Email is not registered
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Email is not registered. Please create an account.'),
        ),
      );
    } else {
      // Email is registered, proceed to password screen
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => SigninScreenPassword(
            email: email,
          ),
        ),
      );
    }
  } catch (e) {
    // Handle errors
    print('Error fetching sign-in methods: $e');
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('An error occurred: $e'),
      ),
    );
  } finally {
    setState(() {
      isLoading = false;
    });
  }
}

Below you can see the full code for the screen for context including where I called the function:

// ignore_for_file: deprecated_member_use

import 'package:cloture/screens/authentication/create_account_screen.dart';
import 'package:cloture/screens/authentication/sign_in_screen_password.dart';
import 'package:cloture/utilities/buttons.dart';
import 'package:cloture/utilities/colors.dart';
import 'package:cloture/utilities/text.dart';
import 'package:cloture/utilities/textfield.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:cloture/gen/assets.gen.dart';

class SigninScreenEmail extends StatefulWidget {
  const SigninScreenEmail({super.key});

  @override
  State<SigninScreenEmail> createState() => _SigninScreenEmailState();
}

class _SigninScreenEmailState extends State<SigninScreenEmail> {
  TextEditingController emailController = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  bool isLoading = false;

  Future<void> checkEmail() async {
    String email = emailController.text.trim();

    setState(() {
      isLoading = true;
    });

    try {
      // Fetch sign-in methods for the email
      List<String> signInMethods =
          await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);

      if (signInMethods.isEmpty) {
        // Email is not registered
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Email is not registered. Please create an account.'),
          ),
        );
      } else {
        // Debugging: Print the sign-in methods
        print("Sign-in methods for email: $signInMethods");

        // Email is registered, proceed to password screen
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => SigninScreenPassword(
              email: email,
            ),
          ),
        );
      }
    } catch (e) {
      // Check for Firebase-specific errors
      print('Error fetching sign-in methods: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('An error occurred: $e'),
        ),
      );
    } finally {
      setState(() {
        isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: white100,
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 27, vertical: 123),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              reusableText(
                'Sign in',
                32.sp,
                FontWeight.bold,
                black100,
                -0.41,
                TextAlign.left,
              ),
              SizedBox(height: 32.h),
              Form(
                key: _formKey,
                child: authTextField(
                    hintText: 'Email Address',
                    controller: emailController,
                    keyboardType: TextInputType.emailAddress,
                    textInputAction: TextInputAction.done,
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Please enter your email';
                      } else if (!RegExp(r'^[^@]+@[^@]+\.[^@]+')
                          .hasMatch(value)) {
                        return 'Please enter a valid email address';
                      }
                      return null;
                    }),
              ),
              SizedBox(
                height: 16.h,
              ),
              appButton(
                'Continue',
                primary200,
                white100,
                47.h,
                344.w,
                100.r,
                16.sp,
                FontWeight.w500,
                Colors.transparent,
                -0.5,
                () {
                  if (_formKey.currentState!.validate()) {
                    checkEmail();
                  }
                },
              ),
              SizedBox(
                height: 16.h,
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.start,
                children: [
                  reusableText(
                    'Don\'t have an account?',
                    12.sp,
                    FontWeight.w500,
                    black100,
                    -0.5,
                    TextAlign.left,
                  ),
                  SizedBox(
                    width: 8.w,
                  ),
                  GestureDetector(
                    onTap: () {
                      Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (context) => const CreateAccountScreen(),
                        ),
                      );
                    },
                    child: reusableText(
                      'Create One',
                      12.sp,
                      FontWeight.bold,
                      primary200,
                      -0.5,
                      TextAlign.left,
                    ),
                  ),
                ],
              ),
              SizedBox(
                height: 71.h,
              ),
              authButton(
                'Continue with Apple',
                Assets.images.appleSvg.image(height: 24.h),
                () {},
              ),
              SizedBox(
                height: 12.h,
              ),
              authButton(
                'Continue with Google',
                Assets.images.googlePng0.image(height: 24.h),
                () {},
              ),
              SizedBox(
                height: 12.h,
              ),
              authButton(
                'Continue with Facebook',
                Assets.images.facebookPng0.image(height: 24.h),
                () {},
              ),
              SizedBox(
                height: 71.h,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

What I have tried: Verified that the Firebase project is set up correctly, as I can see the registered email in the Firebase Console. Checked that the email being entered is valid and matches the one in Firebase. Tried trimming the email before sending it to fetchSignInMethodsForEmail() to remove any leading or trailing spaces.

Expected Behavior: If the email exists in Firebase, the app should navigate to the password screen. If the email doesn't exist, it should show a message asking the user to create an account. Actual Behavior: Even when I enter an email that exists in Firebase, I get the message "Email is not registered."

Question: What might be causing this issue where the email is not recognized as registered, even though it exists in Firebase? How can I resolve this so that the app correctly identifies registered emails?


Solution

  • On projects created since September 2023, Firebase automatically enables its protection against email enumeration attacks. With this protection enabled, the fetchSignInMethodsForEmail no longer works - as that is the precise type of API that would allow the attack.

    Your options are to: