How I fixed the passwordless authentication flaw (~read flow) in my NextJs, Firebase, Node.js, and MongoDB app?
Since I believe a developer is always a learner so most of the times I build my apps with new tech stacks that I want to learn, but this is one of those projects which needs to be in the market as soon as possible, so I went with a stack which I'm most comfortable with.
So this project is going to be built with Node.js and MongoDB in the backend and Next.js in the frontend. But, you would wonder then why is there a logo of firebase in the cover picture, right?
The answer is not very appealing, but I am using Firebase for some of its features like Google OAuth, Analytics, Crashlytics, and Push Messaging which is totally free (that's why...) along with Node.js and MongoDB, also for authentication.
The previous authentication flow in my app was super simple, the user clicks on the
Get Started button on the landing page and the Google OAuth window opens up where the user has to sign in with their Google account and this creates a user in the firebase project which then gives me the user's data like Full Name, Email address, Profile Picture's URL, etc. along with a boolean value
At first, when I saw this value, I thought to make use of it and totally rely on it to decide whether to make the user fill a sign-up form or just log them in and redirect to their dashboard. Like, if the user
isNew then I will show them a sign-up form and save their data in my MongoDB database, and if not, then it should mean that the user has already signed up and I have their data saved so I will just log them in and get their data from the server.
Simple and cool right? not really.
Let's consider a case where the user signs in with their Google account but they don't complete the signup process for our app. So what happens? The
isNew value will now always be false whenever the user tries to sign up after this since the user is already created in the firebase project when they first authenticated with Google OAuth. So now the app will always try to log them in by requesting their data from the server but their data has never been saved in our database. You see the issue now?
So we have just lost a potential customer and in future a lot more like them.
Luckily, I figured out this issue before the project is released. I completely removed the use of the
isNew value coming from firebase and instead created a new endpoint in the server to authenticate the user after they are done authenticating with Google.
So now the authentication flow will be something like this - The user authenticates with Google OAuth, we get their firebase user id and we check with the backend if we have any user with this id (as I am storing this firebase id in my database too). If it exists which means the user has already signed up so we will just send the user data to the app and redirect them to the dashboard and if it doesn't exist then we will show them the sign-up form.
Now it's simpler and it works!
We could also have a separate login endpoint, in a way that we first check if the user exists, if yes then we send another login request otherwise the sign-up stuff. But, in this approach, we will be sending two requests every time where one is unnecessary many of the times since the user would already be there in the database.
So if the user exists then we will log them in within the same request otherwise a different signup request will be sent. In this approach, we will be sending two requests if the user has not signed up yet, but just one request if the user has. Much lesser hits to the server and much more efficient.