skip to Main Content

I’m developing a registraion form. I use Firebase auth as my authentication.

And I have two methods of registering users.

  1. Using Email / Password Provider
  2. Using Google Auth Provider

These are the steps I’m following.

Step 1.
First I register using Email/Password provider. And Account also creates perfectly.

Image after creating user with Email / Password

Step 2.
Then Let’s say user also tried to login using Google with the same email account using signInWithPopup. When I do that current provider ( Email/Password ) gets replaced with Google. Then I tried to login using Email / Password authentication doesn’t work. Because it is replaced with Google.

What I need Is

  1. When triyng to create user with signInWithPopup check if a user is already available straight away go to dashboard. without replacing Email / Password Provider.

Or

  1. Give an error message saying Email is already registered using Email / Password. Try Logging using Email / Password

I’m doing this in NextJS with Typescript

This is my full code.

import React, { useEffect, useState } from "react";
import Head from "next/head";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import Link from "next/link";
import styles from "../../styles/Register.module.scss";

import type { FieldError } from "react-hook-form";

import { doc, setDoc, Timestamp } from "firebase/firestore";

import {
  auth,
  createUserWithEmailAndPassword,
  db,
  sendEmailVerification,
  googleProvider,
  getAdditionalUserInfo,
  fetchSignInMethodsForEmail,
} from "../../config/firebase";
import { signInWithPopup } from "firebase/auth";

import VerifyMessage from "../../components/verify-message";

const formatErrors = (errors: Record<string, FieldError>) =>
  Object.keys(errors).map((key) => ({
    key,
    message: errors[key].message,
  }));

type AlertType = "error" | "warning" | "success";

function Alert({ children, type }: { children: string; type: AlertType }) {
  const backgroundColor =
    type === "error" ? "tomato" : type === "warning" ? "orange" : "powderblue";

  return <div style={{ padding: "0 16", backgroundColor }}>{children}</div>;
}

const AlertType = ({ children }: { children: React.ReactNode }) =>
  Boolean(children) ? (
    <span role="alert" style={{ color: "tomato" }}>
      {children}
    </span>
  ) : null;

const userSchema = z.object({
  email: z
    .string()
    .min(1, "Email is required")
    .email({ message: "Email is invalid" }),
  password: z.string().min(1, "Password is required"),
});

type UserType = z.infer<typeof userSchema>;

export default function Register() {
  const [error, setError] = useState(false);
  const [success, setSuccess] = useState(false);
  const [msg, setMsg] = useState("");

  const {
    register,
    handleSubmit,
    watch,
    formState: { errors, isSubmitting, isSubmitted, isDirty, isValid },
  } = useForm<UserType>({
    mode: "onChange",
    resolver: zodResolver(userSchema),
    defaultValues: {
      email: "",
      password: "",
    },
  });

  const onSubmit = async (user: UserType) => {
    setError(false);
    setSuccess(false);
    setMsg("");

    const userEmail = user.email;
    await createUserWithEmailAndPassword(auth, user.email, user.password)
      .then(async (userCredential) => {
        const user = userCredential.user;
        const docRef = doc(db, "users", user.uid);
        const verifyId = Math.random().toString(16).slice(2);

        let data = {
          email: user.email,
          verifyId: verifyId,
          userId: user.uid,
        };

        fetch("/api/register/create-user", {
          method: "POST",
          headers: {
            Accept: "application/json, text/plain, */*",
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
        })
          .then((res: any) => {
            fetch("/api/register/send-email", {
              method: "POST",
              headers: {
                Accept: "application/json, text/plain, */*",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(data),
            })
              .then((res: any) => {
                setSuccess(true);
                setMsg(
                  `Account Created. Verification Link Sent to - ${user.email}`
                );
              })
              .catch((error: any) => {
                setError(true);
                setMsg("Oops! Something went wrong. Please Try Again");
              });
          })
          .catch((error: any) => {
            setError(true);
            setMsg("Oops! Something went wrong. Please Try Again");
          });
      })
      .catch((error) => {
        setError(true);
        switch (error.code) {
          case "auth/email-already-in-use":
            fetchSignInMethodsForEmail(auth, userEmail).then(function (
              signInMethods
            ) {
              switch (signInMethods[0]) {
                case "password":
                  setMsg(`Email address ${userEmail} already in use.`);
                  break;
                case "google.com":
                  setMsg(
                    `Logged in Using Google. Please Try to Login from Google`
                  );
                  break;
                default:
                  setMsg(`Email address ${userEmail} already in use.`);
                  break;
              }
            });
            break;
          case "auth/invalid-email":
            setMsg(`Email address ${userEmail} is invalid.`);
            break;
          case "auth/operation-not-allowed":
            setMsg(`Error during sign up.`);
            break;
          case "auth/weak-password":
            setMsg(
              "Password is not strong enough. Add additional characters including special characters and numbers."
            );
            break;
          default:
            setMsg(error.message);
            break;
        }
      });
  };

  const handleGoogeAuthLogin = () => {
    signInWithPopup(auth, googleProvider)
      .then(function (result) {
        const verifyId = Math.random().toString(16).slice(2);

        let data = {
          email: result.user.email,
          verifyId: verifyId,
          userId: result.user.uid,
        };

        fetch("/api/register/send-email", {
          method: "POST",
          headers: {
            Accept: "application/json, text/plain, */*",
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
        })
          .then((res: any) => {
            setSuccess(true);
            setMsg(
              `Account Created. Verification Link Sent to - ${result.user.email}`
            );
          })
          .catch((error: any) => {
            setError(true);
            setMsg("Oops! Something went wrong. Please Try Again");
          });
      })
      .catch(function (error) {
        console.log(error);
      });
  };

  return (
    <>
      <Head>
        <title>Register</title>
      </Head>
      <div className={styles["register"]}>
        <div className={styles["register__container"]}>
          <div className={styles["register__logo"]}>
            <svg
              version="1.0"
              xmlns="http://www.w3.org/2000/svg"
              width="512.000000pt"
              height="512.000000pt"
              viewBox="0 0 512.000000 512.000000"
              preserveAspectRatio="xMidYMid meet"
            >
              <g
                transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
                fill="#000000"
                stroke="none"
              >
                <path d="M2425 4946 c-83 -27 -142 -62 -201 -121 -130 -129 -176 -306 -119 -462 13 -37 23 -68 22 -69 -1 -1 -214 -185 -472 -410 -259 -225 -478 -415 -488 -423 -14 -11 -22 -8 -63 27 -175 155 -461 148 -635 -15 -101 -94 -149 -207 -149 -346 1 -91 28 -172 87 -258 l31 -45 -161 -140 c-269 -232 -277 -241 -277 -283 0 -20 4 -41 8 -47 4 -6 571 -502 1260 -1103 1198 -1042 1255 -1091 1292 -1091 37 0 72 30 732 606 381 333 699 613 706 622 22 28 15 78 -14 101 -51 40 -73 30 -208 -89 l-124 -109 -426 487 -426 486 0 247 0 246 526 293 c470 261 528 290 542 277 13 -14 13 -22 -3 -80 -27 -100 -19 -194 26 -287 51 -105 144 -227 272 -353 112 -111 144 -132 181 -121 11 4 71 55 131 115 l110 108 165 -144 c90 -79 166 -148 168 -153 1 -5 -172 -163 -386 -351 -214 -187 -392 -348 -396 -356 -11 -29 -7 -52 15 -79 26 -33 75 -35 115 -4 49 38 828 720 842 736 6 9 12 32 12 52 0 42 -15 58 -250 260 -85 73 -161 139 -169 146 -11 10 -7 22 23 71 101 163 102 342 4 496 -152 239 -503 290 -714 104 l-43 -38 -418 362 c-230 199 -450 391 -489 425 l-71 63 23 68 c79 231 -59 490 -304 572 -72 24 -222 28 -287 7z m281 -176 c151 -74 218 -255 145 -393 -43 -81 -104 -161 -203 -262 l-87 -90 -66 65 c-88 85 -197 226 -230 297 -23 47 -26 66 -23 122 13 230 253 365 464 261z m-421 -681 c33 -40 100 -110 149 -155 122 -114 131 -114 252 0 49 46 116 116 149 155 33 40 65 69 72 67 17 -7 813 -697 813 -706 0 -9 -980 -552 -990 -548 -14 5 -940 879 -939 886 1 9 420 372 429 372 3 0 33 -32 65 -71z m-126 -867 l481 -453 0 -225 0 -224 -366 0 c-255 0 -372 -3 -387 -11 -11 -7 -209 -153 -439 -325 l-417 -313 -413 359 c-227 198 -414 363 -416 368 -1 4 74 74 167 154 l169 147 107 -104 c59 -57 116 -107 127 -111 40 -12 73 9 187 124 275 275 354 448 296 648 l-16 54 33 29 c287 250 393 341 399 339 4 -2 223 -207 488 -456z m-1221 188 c105 -53 169 -146 179 -260 7 -68 -19 -133 -93 -236 -55 -79 -211 -244 -229 -244 -15 0 -154 148 -215 230 -74 98 -104 167 -103 235 2 128 86 244 211 290 20 7 71 14 113 14 65 1 84 -3 137 -29z m3540 -5 c100 -54 165 -159 166 -269 1 -73 -26 -134 -105 -239 -66 -88 -197 -227 -214 -227 -15 0 -147 138 -209 219 -24 31 -58 83 -77 116 -30 53 -34 68 -33 130 2 133 82 243 211 291 25 9 67 13 123 11 71 -2 94 -8 138 -32z m-1373 -1720 c229 -262 418 -481 421 -488 4 -12 -948 -852 -966 -852 -17 0 -1411 1218 -1403 1226 5 4 181 138 393 298 l385 290 377 0 377 1 416 -475z" />
                <path d="M2481 4706 c-91 -33 -155 -124 -155 -221 0 -205 245 -313 392 -172 52 50 75 103 75 172 0 161 -162 276 -312 221z m142 -167 c25 -31 22 -85 -8 -114 -67 -68 -171 20 -124 106 23 43 99 47 132 8z" />
                <path d="M719 3345 c-154 -48 -210 -256 -102 -376 52 -58 104 -81 178 -81 177 0 289 180 210 339 -30 60 -78 100 -143 119 -55 17 -84 17 -143 -1z m139 -171 c48 -53 10 -134 -63 -134 -89 0 -114 120 -33 155 40 17 66 11 96 -21z" />
                <path d="M4250 3345 c-99 -32 -160 -117 -160 -222 1 -135 98 -235 229 -235 100 -1 174 46 217 137 86 182 -93 382 -286 320z m127 -161 c43 -27 34 -120 -12 -138 -83 -32 -158 52 -109 122 29 42 71 48 121 16z" />
                <path d="M2418 1731 c-170 -55 -298 -202 -328 -380 -29 -172 68 -350 328 -603 146 -142 138 -142 284 0 192 187 300 344 328 477 32 155 -65 359 -218 456 -107 67 -272 89 -394 50z m256 -151 c117 -44 196 -150 204 -276 4 -67 2 -75 -33 -142 -38 -73 -158 -224 -240 -302 l-47 -45 -80 84 c-244 256 -292 400 -185 557 85 125 241 176 381 124z" />
                <path d="M2452 1484 c-220 -110 -139 -438 108 -438 110 0 194 62 225 166 20 68 12 123 -29 190 -61 103 -192 138 -304 82z m162 -145 c28 -22 36 -72 16 -108 -16 -31 -65 -46 -100 -31 -37 15 -50 37 -50 84 0 41 37 76 80 76 15 0 39 -9 54 -21z" />
              </g>
            </svg>
          </div>
          <div className={styles["register__title"]}>Create account</div>
          <div className={styles["register__description"]}>
            Register to post Ads
          </div>
          <div className={styles["register__loginform"]}>
            <form method="post" onSubmit={handleSubmit(onSubmit)} noValidate>
              <div className={styles["register__loginform--email"]}>
                <svg
                  version="1.0"
                  xmlns="http://www.w3.org/2000/svg"
                  width="512.000000pt"
                  height="512.000000pt"
                  viewBox="0 0 512.000000 512.000000"
                  preserveAspectRatio="xMidYMid meet"
                >
                  <g
                    transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
                    fill="#000000"
                    stroke="none"
                  >
                    <path d="M361 4499 c-172 -34 -318 -182 -351 -358 -14 -74 -14 -3088 0 -3162 34 -180 179 -325 359 -359 74 -14 4308 -14 4382 0 180 34 325 179 359 359 14 74 14 3088 0 3162 -34 180 -179 325 -359 359 -67 12 -4325 12 -4390 -1z m3227 -1306 l-1017 -1017 -143 137 c-79 76 -541 533 -1028 1017 l-885 879 2045 1 2045 0 -1017 -1017z m-2524 49 l678 -675 -721 -721 -721 -721 0 1434 0 1433 43 -38 c23 -20 347 -341 721 -712z m3756 -684 l0 -1433 -717 717 -718 718 715 715 c393 393 716 715 717 715 2 0 3 -645 3 -1432z m-2605 -459 c138 -138 267 -259 287 -270 45 -23 89 -24 131 -3 18 9 146 129 285 267 l252 252 718 -718 717 -717 -2045 0 -2045 0 720 720 c396 396 722 720 725 720 3 0 118 -113 255 -251z" />
                  </g>
                </svg>
                <input
                  type="text"
                  placeholder="Enter Your Email"
                  {...register("email")}
                  aria-invalid={Boolean(errors.email)}
                />
              </div>
              <AlertType>{errors?.email?.message}</AlertType>
              <div className={styles["register__loginform--password"]}>
                <svg
                  version="1.0"
                  xmlns="http://www.w3.org/2000/svg"
                  width="512.000000pt"
                  height="512.000000pt"
                  viewBox="0 0 512.000000 512.000000"
                  preserveAspectRatio="xMidYMid meet"
                >
                  <g
                    transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
                    fill="#000000"
                    stroke="none"
                  >
                    <path d="M2420 5114 c-208 -27 -407 -101 -580 -217 -96 -64 -273 -241 -337 -337 -92 -137 -155 -282 -195 -450 -19 -77 -21 -125 -25 -497 l-5 -413 -107 0 c-64 0 -131 -6 -166 -15 -172 -45 -305 -179 -350 -352 -22 -86 -22 -2380 0 -2466 45 -173 178 -307 350 -352 52 -13 244 -15 1555 -15 1311 0 1503 2 1555 15 172 45 305 179 350 352 22 86 22 2380 0 2466 -45 173 -178 307 -350 352 -35 9 -102 15 -166 15 l-107 0 -5 413 c-4 372 -6 420 -25 497 -62 257 -170 450 -351 631 -185 185 -380 293 -631 350 -83 19 -331 33 -410 23z m371 -343 c182 -47 320 -128 454 -265 95 -97 162 -199 209 -317 57 -147 59 -163 63 -591 l4 -398 -961 0 -961 0 3 398 c4 367 6 403 25 473 51 184 130 319 262 449 154 153 303 227 551 274 59 11 277 -3 351 -23z m1295 -1917 c15 -11 37 -33 48 -48 21 -27 21 -33 24 -1192 3 -1289 7 -1207 -64 -1260 l-37 -29 -1497 0 -1497 0 -37 29 c-70 53 -66 -26 -66 1245 0 790 3 1159 11 1178 14 37 47 73 84 89 25 11 293 13 1517 11 1484 -2 1487 -2 1514 -23z" />
                    <path d="M2487 2340 c-222 -39 -383 -262 -348 -481 21 -131 116 -267 219 -313 l42 -19 0 -274 c0 -253 2 -277 20 -312 57 -113 223 -113 280 0 18 35 20 59 20 312 l0 274 42 19 c103 46 198 182 219 313 24 152 -51 326 -177 410 -94 62 -215 89 -317 71z m118 -326 c31 -14 65 -64 65 -94 0 -7 -7 -27 -16 -45 -19 -41 -73 -69 -114 -60 -37 8 -77 48 -85 85 -9 40 19 95 58 114 40 20 51 20 92 0z" />
                  </g>
                </svg>
                <input
                  type="password"
                  placeholder="Enter Your Password"
                  autoComplete="on"
                  {...register("password")}
                  aria-invalid={Boolean(errors.password)}
                />
              </div>
              <AlertType>{errors?.password?.message}</AlertType>
              <div className={styles["register__loginform--signup"]}>
                <input type="submit" value="Register" />
              </div>
            </form>
          </div>
          <div className={styles["register__divider"]}>
            <span>OR</span>
          </div>
          <div className={styles["register__socialmedia"]}>
            <div className={styles["register__socialmedia--google"]}>
              <button onClick={handleGoogeAuthLogin}>
                <div
                  className={styles["register__socialmedia--google-svgicon"]}
                >
                  <svg
                    xmlns="http://www.w3.org/2000/svg"
                    xmlnsXlink="http://www.w3.org/1999/xlink"
                    viewBox="0 0 48 48"
                  >
                    <defs>
                      <path
                        id="a"
                        d="M44.5 20H24v8.5h11.8C34.7 33.9 30.1 37 24 37c-7.2 0-13-5.8-13-13s5.8-13 13-13c3.1 0 5.9 1.1 8.1 2.9l6.4-6.4C34.6 4.1 29.6 2 24 2 11.8 2 2 11.8 2 24s9.8 22 22 22c11 0 21-8 21-22 0-1.3-.2-2.7-.5-4z"
                      />
                    </defs>
                    <clipPath id="b">
                      <use xlinkHref="#a" overflow="visible" />
                    </clipPath>
                    <path
                      clipPath="url(#b)"
                      fill="#FBBC05"
                      d="M0 37V11l17 13z"
                    />
                    <path
                      clipPath="url(#b)"
                      fill="#EA4335"
                      d="M0 11l17 13 7-6.1L48 14V0H0z"
                    />
                    <path
                      clipPath="url(#b)"
                      fill="#34A853"
                      d="M0 37l30-23 7.9 1L48 0v48H0z"
                    />
                    <path
                      clipPath="url(#b)"
                      fill="#4285F4"
                      d="M48 48L17 24l-4-3 35-10z"
                    />
                  </svg>
                </div>
                Connect With Google
              </button>
            </div>
          </div>
          <div className={styles["register__signin"]}>
            Already Have an account? <Link href="/signin">Sign In</Link>
          </div>
          {error && (
            <VerifyMessage
              className={`${styles["register__verify"]} ${styles["register__verify--error"]}`}
            >
              {msg}
            </VerifyMessage>
          )}

          {success && (
            <VerifyMessage
              className={`${styles["register__verify"]} ${styles["register__verify--success"]}`}
            >
              {msg}
            </VerifyMessage>
          )}
        </div>
      </div>
    </>
  );
}

tried using fetchSignInMethodsForEmail method inside signInWithPopup couldn’t resolved it

2

Answers


  1. The behavior you’re seeing is working as designed for certain email addresses for certain providers. The most common example: Google is a preferred provider for @gmail.com addresses, and signing into to Google with an account with a gmail address takes precedence over other providers using that address.

    To prevent this, you’ll want to indeed:

    1. Ask the user to enter their email address.
    2. Check of that is already in use in your project by calling fetchSignInMethodsForEmail, which returns the known providers/sign-in methods for the email address.

    You can then use this to show the correct provider UI to the user so they can sign in to their existing account.

    Login or Signup to reply.
  2. I got the same error, in my case it was happening because the email address wasn’t verified at the moment when the user try the Google authentication option.

    I resolved this sending an email verification link to the user right after the creation of the account (the user has to click on that link).

    This link or an password reset link verify the email address and when a verified user try with the Google provider or a different provider than email/password, this is added as a new provider and not replace the email/password provider.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search