Issues encountered while implementing email login and authentication with firebase

김세현·2024년 3월 9일
0

프로젝트 일지

목록 보기
3/3

I had to implement the login process while working on the todaylink project.

In order to implement login, I need a server in charge of authentication, but I couldn't afford to implement a separate server.

I have experience writing server code while working on a team project before, but it was a long time ago,
Since I had to develop it by myself, it was also a burden to implement not only the client but also the server-side code.

So, for me, 'firebase' was a great alternative.

firebase helps to easily authenticate users through the following methods.
And I chose email authentication from these.

And I implemented user authentication through 'firebase' for about two weeks. (Actually, it's still not completed.)

Someone may not understand that I've spent two weeks implementing user authentication using 'firebase.' (Because implementing authentication with 'firebase' is not that difficult.)

To give my own excuse, I thought the series of processes of processing authentication through 'firebase' was too uncertain.

So I spent a lot of time trying to figure out how to deal with these problems.


The first problem, that only users who have already signed up can authenticate their emails.

Looking at the code in the official document below, 'firebase' sends an authentication mail to the user through a method called 'sendEmailVerification'.

However, as you can see, Arguments when calling this function is the information of the currently logged-in user.

I need email authentication to sign up for user membership, but I don't understand that I need an email from a user who has already signed up.

When it comes to membership registration and login through e-mail, it is common to use the e-mail ID that the user actually used.

If my actual e-mail is 'example@google.com ', email login is meaningful only if I can use this ID as it is.

It would be very inconvenient if I joined and used the site with 'test@google.com ', not my ID.

In addition, users who use actual 'test@google.com ' emails will experience another inconvenience.

That's why I thought we should verify that the user is the real owner of the email through email authentication during the membership process.

However, as seen above, the 'sendEmailVerification' method for email authentication requires that the information of the currently logged-in user be transmitted as an argument.

It didn't make sense in my common sense that I need the information of the user who has already logged in for authentication when I perform email authentication to sign up.

Alternative for implementing email authentication with firebase

The flow of the code I wrote is as follows.

  1. The user enters the email.
  2. The user clicks the 'Authentication' button.
  3. When user click the authentication button, an arbitrary password is generated as a random number, and this information is encrypted and temporarily registered in the user information.
  4. firebase is automatically logged in upon membership registration. Therefore, logged in user information can be obtained. (Official document below)
  5. Through the user information obtained in this way, 'sendEmailVerification' is called and user information is delivered.
  6. Authentication mail is sent to the entered email.
  7. Once the email authentication is complete, delete any account you signed up for in advance.
  8. After email authentication, when the user finally presses the registration button, the actual membership information registration is completed.

The key point is step 3.
To explain process 3, user information is temporarily registered in the system for authentication.

However, there is a possibility that someone might try to log in during this short period of time
So, I thought i shouldn't use predictable passwords or use fixed values.
That's why I created random numbers and encrypted them so that no one could log in.

The code is as follows.

 	// It produces a cryptographically safer random number than Math.random().
    const array = new Uint32Array(1);
    const temporalPassword = window.crypto.getRandomValues(array)[0];
    const encryptedPassword = crypto.AES.encrypt(
      temporalPassword.toString(),
      `${process.env.REACT_APP_SECRET_KEY}`
    ).toString();
    // Request to register user information temporarily for authentication
    fakeSignup.sendRequest({
      email: test@google.com
      password: encryptedPassword,
    });

I wrote a code that handles email authentication when signing up for membership in the same way as above.

but, I don't think this method is programmatically correct.

In order to authenticate e-mail in the way I wanted to implement, not in the basic method provided by firebase, there was a process 3 or 4 that was not actually necessary, and there was another process 7 due to the process 3 or 4.

and I had to consider various situations and add logic accordingly to the process of '7'. (If you press 'authentication', but you don't sign up and leave the page, etc.)

However, I thought it was the best way to use firebase in the current situation.

Second problem, problems encountered in implementing UI to show email authentication and processing

The problem I encountered was how to check it in the existing app and how show the UI accordingly when the user clicks the authentication link after sending the authentication mail.

In other words, when an event called authentication occurs, it is a question of how to 'listen' it and handle the task in the existing app.

In fact, in most apps, the method provided by firebase is sufficient to handle the email authentication and verification process.
However, this problem was caused because I tried to handle authentication and verification work according to the flow I wanted using firebase.

The work flow for the email authentication I want is as follows.

  1. Send an authentication email.
  2. The user can complete the authentication by clicking on the link sent to the email
  3. After the authentication is completed, the existing membership PAGE detects this and the UI should be changed.
  4. After that, according to the general trend, users sign up for membership.

At this time, the point where the problem occurred is step 3.

i wanted to detect when authenticating the email and change the app's UI accordingly.
The UI I wanted is as follows.

  1. When user press the Authentication button.

  2. When authentication is complete (when the user clicks the link in the authentication mail)

Once again, it is clear that email authentication can be implemented only with the basic functions provided by firebase.
However, I wanted to handle the authentication process at my own flow, which is why I encountered the above mentioned issues and had to deal with them.

The flow of checking the email authentication that firebase provides by default is as follows.

1.Set up the link to be sent as the content of the authentication mail. (This link is a path to handle authentication operations in apps.)

2.Users can click this link to complete authentication.

What I expected was that when I clicked on the link in step 1, it would be detected in the existing membership page and the UI on the page would change.

However, when I clicked on this link, the page of the app that corresponds to this path turned on in the New tab.
(Of course, the work on authentication is handled normally.)

I wanted to be able to detect this on the existing page(existing app) rather than the new tab.

So the first flow I thought of was as follows.

  1. When user click the authentication link, a new tab that opens shows the phrase "email authentication is complete. After 3 seconds, the window closes on its own" and causes the window to close on its own after 3 seconds.

  2. Store the value of whether or not to authenticate in local storage (because local storage can share the value in other tabs)

  3. Detect this on the existing page and change the UI.

But, of course, this still did not solve the problem.

The fundamental problem is that it should be possible to detect the moment the user clicks the authentication link on the existing page.
So, what i think is i set the time for the authentication job to be processed (for example, 60 seconds) and, on the existing page, should I periodically check whether authentication has been performed or not through a function called 'setInterval' for 60 seconds?

While I was struggling to fix this, I happened to see a question from Stack Overflow and it was 100% the same as what I was struggling with.

Stack Overflow Questions and Answers

And all of the answers to this were called 'setTimeout' or 'setInterval' to detect authentication events.

Accordingly, the flow of email authentication I implemented is as follows.

  1. Set variables such as "flag" to false. which detects whether the user has pressed the authentication button.
const [flag, setFlag] = useState(false);
  1. Enter the email and press the Authentication button to send the email authentication mail and change it to flag = true.

  2. If "flag" is true using the useEffect hook, the setInterval function periodically starts to inquire the current user information.

  useEffect(() => {
    if (flag) {
      const intervalId = setInterval(() => {
        auth.currentUser?.reload(); // Periodically query current user information.
        		...
        }
      }, 1000);
    }
  }, [flag]);
  1. When the user information is inquired, if the field indicating whether or not email authentication is true, the authentication is completed, and at this time, the set 'interval' is released using the 'cleanerInterval' function.
  useEffect(() => {
    if (flag) {
      const intervalId = setInterval(() => {
        auth.currentUser?.reload();
        if (auth.currentUser?.emailVerified) { // When the mail is authenticated,
          setEmailVerificationMessage({
            type: "normal",
            message: "Authentication successfully completed.",
          });
          clearInterval(intervalId);
        }
          ...
      }, 1000);
    }
  }, [flag]);

Result GIF

profile
under the hood

0개의 댓글