I have encountered the following scenario(my tech stack is react-native with Expo and Firebase):
- User is shown the Signup screen.
- They fill out the form.
- The form is validated
- If the form is valid the signUp() function is called, if not the error messages are shown.
- In the signUp() functions should do the following things:
- Check if the username exists in the Firestore database
- Check if email exists in the Firestore database
- if they do not
createUserWithEmailAndPassword
function is called. - only now if the function call was successful create a user in the Users collection with the document id of the Firebase auth user id and inside store the information that I choose.
the problem:
when user clicks on the signup button, they are not yet authed, so they need to read the database to complete steps 5.1 and 5.2 while they are not authed, as far as I understand that’s a security issue.
Most articles I could find recommend using the cloud functions, but I cannot afford them at the moment.
2
Answers
There are two methods available,
signInWithEmailAndPassword()
andcreateUserWithEmailAndPassword()
. Both are signing the user in, but the latter also creates the user in the Firebase Authentication. So once a user is created, there is no need to callcreateUserWithEmailAndPassword()
anymore, butsignInWithEmailAndPassword()
. If you want to know if a user exists, regardless of what exists in the database, then you should check the value of the feedbackAdditionalUserInfo#isNewUser property, with returns a boolean that indicates:Your use case seems to require that an unidentified user can check whether an existing email-address is already in use. There are a few ways to do this:
fetchSignInMethodsForEmail
to determine whether there are any providers for the email address already. If that returns any results, show the sign in screen for that provider instead of showing a sign up screen.If you store the email addresses in Firestore (as seems to be your plan), you can also:
Fully restrict access to that data from client-side code, but then implement your own server-side API (e.g. with Cloud Functions or Cloud Run) that reads from the database to check if the email address is already in use. So this custom API would take the email address and nothing else, and returns a boolean to indicate whether it is already in use. This way you only expose minimal information and can secure who can call that end-point.
At that point though, you can also implement the exact same thing just with Firestore. You’d create a collection with the email as the document ID, allow unauthenticated access to only that collection in your security rules, and disallow the
list
operation – so that a user can onlyget
a specific document by name. This is equivalent to #2.You can first sign the user in anonymously, and then use that sign-in status when accessing the database up til step 5.2. Then instead of creating a new user account, you link the email address to the anonymous account.
Note that all approaches above are susceptible to email enumeration attacks. A good way to reduce the chances of that and other forms of abuse, also: