Firebase Email Enumeration Rules: A Workaround For fetchSignInMethodsForEmail
Tags: reactnative, react, webdev, firebase
Published: 2024-02-04 08:37:24



![Email Protection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2u4f65nz6ritjzvj8n5u.jpeg)

If you manage users with Firebase Auth, this article will help you understand recent Google security implementation aimed at improving security of your users' identity information. You probably _noticed_ that `fetchSignInMethodsForEmail` started failing for **_new_** projects created after September 15, 2023. That is **NOT** a bug! Also, if you use Firebase Auth for users management, you probably got a nice email from Google letting you know that new projects from the date aforementioned now have e_mail enumeration protection enabled by default_. What does this mean? Why? What are the consequences to be expected? Speculations on possible direction Google is headed?

First, let us clarify a few things.

### What is email enumeration?

Google Cloud documentation has a clean definition of this, so let me not reinvent the wheel here. In this [article](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) by Google Cloud, there is this concise definition of email enumeration:

> _Email enumeration is a type of brute-force attack in which a malicious actor attempts to guess or confirm users in a system by passing an email address to the API and checking the response._>

In the case of case of Firebase Auth, the attacker calls the Identity Platform API and tries to guess the existence or not of a particular user on the target project. There are a few ways this call can be made, eg via a REST API like [createAuthUri](https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/createAuthUri), via a client SDK, etc. Basically, anyone who can access your `https://www.googleapis.com/identitytoolkit` or [https://cloud.google.com/gcp](https://cloud.google.com/gcp?hl=en) technically can enumerate users on the chosen project if they know their emails. A good Auth quota limit means emails obtained from a security breach, can be looped through to determine which emails are being used in that project. A more sinister case will be a bad actor, eg a scammer, a bad state actor, etc, who somehow gets access (eg API key, reverse-engineer an app to operated under the radar of that app's authentication, etc) being able to confirm the existence or otherwise of an account under the email of their victim. Zoom out a bit, and you realize how dangerous this is: in some places, _belonging_ to an organization, group, using certain products or services, etc, can immediately spell grave threat to the life and safety of the individual if found by the government. I can already think of a few ways this would even get worse from there: confirm 'A' belongs to an organization, and then start fishing around 'A's circle to see who else is in that organization with them. I will not go further down that rabbit hole, but you get the point. And we are just painting the picture of one scenario!

There are quite a few ways to hit the `EMAIL_NOT_FOUD` error on GCP, or its lil nephew `auth/user-not-found`. What makes this a bit problematic is that you don't need a OAuth credential to get these responses, just the email, and an app with Firebase Auth properly initialized, or API key in the case of a REST API call.

### What is the problem?
Abuse of a feature suggests the existence of a legitimate use. This also means that **a lot** of apps built their auth flow around this feature that enables them to first confirm the existence of an email on their platform, or lack of it. Typical scenario is most user onboarding process where you want to confirm there is no existing account with the email supplied by the new user trying to sign up, or you want to be sure someone trying to request a password reset even exists on your platform.

Now, here is the problem in my opinion: in **_a new user scenario_**, if that user comes in authenticated from a social auth provider, eg Google, Facebook, Apple, etc, trying to sign them in **_will create a new user in Firebase with their credentials!_** The cherry on top? The only method that has separate login and signup functions is the "email and password". Take a cursory look at the documentation around the [methods](https://firebase.google.com/docs/reference/node/firebase.auth.Auth#methods) available for `Auth` service instance for an app. There is only but one "creatUser…" method, and that is `createUserWithEmailAndPassword`. If you are coming from an [auth provider](https://firebase.google.com/docs/reference/node/firebase.auth.AuthProvider), you just have to sign in, and if you do, and no user existed, a new one is created. Then there is the the [isNewUser](https://firebase.google.com/docs/reference/kotlin/com/google/firebase/auth/AdditionalUserInfo#isNewUser()) boolean that I have filed under "blink and you miss it": you get it only once. Interpretation: if for any reason a new user onboarding is incomplete, you will have to find other indirect ways to deduce they are new users on _subsequent logins_. Do you really want to expose too much user metadata to the FE in an attempt to maybe try figuring out the user age via their creation time? So how do we circumvent or at least mitigate email enumeration risks and still have a functionality that mimics [fetchSignInMethodsForEmail](https://firebase.google.com/docs/reference/node/firebase.auth.Auth#fetchsigninmethodsforemail)? That is the point of this article.

### My Solution

My suggested solution in one sentence: lean into Firebase Function. A function endpoint gives you some superpowers within your Firebase project, _**and**_ you can still pile on other security measures. My go to defence mechanisms are:

- Enable [AppCheck](https://firebase.google.com/docs/app-check) on your project and use it to protect your APIs.
- Implement strong encryption in transit. This is a good playground for security easter eggs: spice things up with encrypted stuff that should respond in a specific way in line with your business logic, a deviation of which will indicate malicious attempt.
- Use header information to filter for allowed origins at your API.

A sample code for the API could look like this:
{% embed https://gist.github.com/chinomnsoawazie/8990be310d35b8b52561fc49752cad3f %}


The trick here is to use [getUserByEmail](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.baseauth.md#baseauthgetuserbyemail) method from the function API. This method is available with the elevated privileges of the admin . You can even mess with [getUserByPhoneNumber](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.baseauth.md#baseauthgetuserbyphonenumber) if you so wish.

In my tests, with [email enumeration](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) turned on,[fetchSignInMethodsForEmail](https://firebase.google.com/docs/reference/node/firebase.auth.Auth#fetchsigninmethodsforemail) fails at the client, but the API methods worked like charm.

If you liked this article, bookmark it for later, and share to help others.