Open DLeddy opened 2 months ago
I looked into this and found that in the case of the asyncio approach, the requests sent to the user accounts API (https://identitytoolkit.googleapis.com/v1/projects/PROJECT_NAME/accounts) are close in timing but spaced out enough that Firebase properly returns a 400 Bad Request error for duplicate emails.
However, when using ThreadPoolExecutor, the request times are much closer—almost simultaneous—and the API does not throw the 400 Bad Request error for duplicate emails as expected. Instead, it creates multiple users with the same email, likely due to the near-simultaneous nature of the requests.
This suggests that Firebase may not be enforcing uniqueness consistently when handling requests with very close or simultaneous timings. It would be ideal for Firebase to enforce uniqueness regardless of the concurrency method or timing of the requests.
[READ] Step 1: Are you in the right place?
firebase
tag.[REQUIRED] Step 2: Describe your environment
Note that I have used other tenants on Google Identity Platform in this gcp project but I scrapped that and went back to the single tenant. This issue is happening on the default firebase tenant. (Sharing this fact in case it somehow affects the outcome)
[REQUIRED] Step 3: Describe the problem
I discovered that my tests for some CRUD functionality were creating duplicate firebase users with the same email which is problematic because we rely on the
firebase_admin._auth_utils.EmailAlreadyExistsError
to safeguard functionality. This appeared as my webserver API was called to create the same user concurrently and it actually made multiple firebase users.For context, my firebase project -> authentication -> settings -> user account linking -> link accounts with same email is active.
Steps to reproduce:
Creating a new user with an existing email address succeeds when its ran concurrently. I have tested 3 scenarios:
Create the new user (with existing email) a second after the existing user was created 1.1. I get the
firebase_admin._auth_utils.EmailAlreadyExistsError
like I expectAttempt to create 4 users with the same email address using asyncio library to create them quickly 2.1 Returns one created user and fires 3
firebase_admin._auth_utils.EmailAlreadyExistsError
like I expect3. Attempt to create 4 users with the same email address concurrently using ThreadPoolExecutor 3.1 Returns 4 new Firebase users who all share the same email address. Not good.
Calling the
auth.get_user_by_email(email)
returns the latest firebase user created, when I delete the latest one then the function returns the newest one after that and so on.I plan to add in concurrency/idempotent protections to my API logic in the mean time as this would cause a mess downstream ( as uncommon as it would occur)
Relevant Code:
This is a rough outline of my tests but in general just call create_user concurrently to hopefully see the same results. I use a Fastapi server so you can ignore some of this extracted code using async where its not needed. This is just to demo the issue. firebase_service.py
test.py