4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CrunchtimerAdvent Calendar 2020

Day 9

Firebase Authenticationでの会員登録ログインの実装

Last updated at Posted at 2020-12-10

React(Next.js)でfirebaseのAuthenticationを用いて会員登録とログインを実装しましたので
その実装方法とfirebase Consoleの使い方を紹介していきます。
※細かな部分は説明を省きます
※Tailwindを使用しています

##画面の作成
###1.まず新規登録画面を作成します。

上2つにSNS(facebookとtwitter)での登録用ボタンを追加、
SNSアカウントを使わずメールアドレスだけで登録される方用に
メールアドレスとパスワード入力、パスワード再入力の欄と送信ボタンを作成しました。
この入力が完了後、登録のメールアドレスにメアド確認メールが送信されます。


type Props = {
  setIsSignedIn: Function;
};

enum SignUpStatus {
  Input,
  Confirm,
  Loading,
  Submitted,
}

const Index: React.FC<Props> = ({ setIsSignedIn }) => {
  const [toastMessage, setToastMessage] = useState<string>('');
  const [toastType, setToastType] = useState<ToastType>(ToastType.Notification);
  const [toastState, setToastState] = useState<boolean>(false);
  const router = useRouter();

  const signInHandler = (type: number) => {
    UserPresenterInstance.signIn({ type: type }).then(async (response) => {
      if (response.body != undefined) {
        setIsSignedIn(true);
        router.push('/mypage');
      } else {
        setToastType(ToastType.Error);
        setToastMessage(response.error as string);
      }
    });
  };

  // inputの状態
  const [email, setEmail] = useState<string>();
  const [password, setPassword] = useState<string>();
  const [rePassword, setRePassword] = useState<string>();

  // errorメッセージ
  const [emailError, setEmailError] = useState<string[]>([]);
  const [passwordError, setPasswordError] = useState<string[]>([]);
  const [rePasswordError, setRePasswordError] = useState<string[]>([]);

  // 確認ボタンのisDisabled
  const [confirmIsDisabled, setConfirmIsDisabled] = useState<boolean>(true);

  // 入力可能・確認・提出完了
  const [status, setStatus] = useState<SignUpStatus>(SignUpStatus.Input);

  const emailValidateHandler = () => {
  //省略
  };

  const passwordValidateHandler = () => {
    //省略
  };

  const rePasswordValidateHandler = () => {
  //省略
  };

  const confirmIsDisableHandler = () => {
  //省略
  };

  useEffect(() => {
    confirmIsDisableHandler();
  }, [emailError, passwordError, rePasswordError]);

  const confirmOnClickHandler = () => {
    setStatus(SignUpStatus.Confirm);
    if (matchMedia('only screen and (max-width: 768px)').matches) {
      window.scrollTo(0, 0);
    }
  };

  const fixOnClickHandler = () => {
    setStatus(SignUpStatus.Input);
    if (matchMedia('only screen and (max-width: 768px)').matches) {
      window.scrollTo(0, 530);
    } else {
      window.scrollTo(0, 0);
    }
  };

  const submitOnClickHandler = () => {
    UserPresenterInstance.emailSignUp(email!, password!).then(
      async (response) => {
        if (response.body != undefined) {
          setStatus(SignUpStatus.Loading);
          setTimeout(() => {
            setStatus(SignUpStatus.Submitted);
          }, 2000);
        } else {
          setStatus(SignUpStatus.Loading);
          setTimeout(() => {
            setStatus(SignUpStatus.Confirm);
            setToastType(ToastType.Error);
            setToastMessage(response.error as string);
            setToastState(true);
          }, 2000);
        }
      }
    );
  };

  useEffect(() => {
    if (status == SignUpStatus.Submitted) {
      setToastType(ToastType.Notification);
      setToastMessage('送信完了しました。');
      setToastState(true);
    }
  }, [status]);

  //パスワードの文字列をアスタリスクに変換
  const pass = String(password);
  const passResult = pass.replace(pass, '*'.repeat(pass.length));

  switch (status) {
    case SignUpStatus.Input:
      return (
        <>
          <main>
            <section className="container mx-auto p-inner my-4 ">
              <H2 text="無料会員登録" className={'p-inner'} />
              <div className={'lg:pt-16  lg:flex'}>
                <div className="w-full lg:w-3/5">
                  <H2
                    text="SNSアカウントで登録"
                    className={'mx-auto text-center mt-6'}
                  />
                  <div className=" mx-auto w-full lg:w-100 ">
                    <div className="px-8 mx-auto max-w-2xl mb-5 relative">
                      <Icon
                        type={IconType.FacebookLogin}
                        className="absolute pt-4 pl-5"
                      />
                      <RoundedButton
                        text={'Facebookで登録'}
                        isDisabled={false}
                        colorType={RoundedDivColorType.Facebook}
                        size={RoundedDivSize.M}
                        onClick={() => signInHandler(SignInType.Facebook)}
                      />
                    </div>
                    <div className="px-8 mx-auto max-w-2xl mb-5 relative">
                      <Icon
                        type={IconType.TwitterLogin}
                        className="absolute pt-4 pl-5"
                      />
                      <RoundedButton
                        text={'Twitterで登録'}
                        isDisabled={false}
                        colorType={RoundedDivColorType.Twitter}
                        size={RoundedDivSize.M}
                        onClick={() => signInHandler(SignInType.Twitter)}
                      />
                    </div>
                  </div>
                </div>
                <div className={'w-full lg:w-3/5 mt-12 lg:mt-6 pb-16'}>
                  <H2
                    text={'メールアドレスで登録'}
                    className={'mb-6 mx-auto text-center'}
                  />
                  <div className=" mx-auto sm:w-10/12">
                    <table className={'w-full'}>
                      <tbody>
                        <tr>
                          <ContactListItem
                            title={'メールアドレス'}
                            form={
                              <Input
                                holder={'yourmail@subhika.jp'}
                                setter={setEmail}
                                value={email}
                                onBlur={emailValidateHandler}
                              />
                            }
                            error={emailError}
                          />
                        </tr>
                        <tr>
                          <ContactListItem
                            title={'パスワード'}
                            form={
                              <Input
                                setter={setPassword}
                                value={password}
                                onBlur={passwordValidateHandler}
                                type="password"
                              />
                            }
                            error={passwordError}
                          />
                        </tr>
                        <tr>
                          <ContactListItem
                            title={'パスワード (確認用)'}
                            form={
                              <Input
                                setter={setRePassword}
                                value={rePassword}
                                onBlur={rePasswordValidateHandler}
                                type="password"
                              />
                            }
                            error={rePasswordError}
                          />
                        </tr>
                      </tbody>
                    </table>
                  </div>
                  <div className=" mx-auto w-4/5 sm:w-8/12 ">
                    <RoundedButton
                      colorType={RoundedDivColorType.Positive}
                      size={RoundedDivSize.M}
                      onClick={confirmOnClickHandler}
                      isDisabled={confirmIsDisabled}
                      text={'確認画面へ'}
                    />
                  </div>
                </div>
              </div>
            </section>
          </main>
        </>
      );
    case SignUpStatus.Confirm:
      return (
        <>
          <main>
            <Toast
              type={toastType}
              text={toastMessage}
              isShow={toastState}
              isShowSetter={setToastState}
            />
            <section className="container mx-auto p-inner my-4 ">
              <H2 text="無料会員登録" className={'p-inner'} />
              <div className={'mb-6  pt-8 lg:w-1/2 mx-auto'}>
                <SmallText
                  text={
                    '登録内容をご確認ください。'
                  }
                />
                <SmallText text={'登録に必要なURLをメールでお送りします。'} />
              </div>
              <div className={'lg:w-1/2 mx-auto'}>
                <H2 text={'登録内容の確認'} className={'mb-2'} />
                <table className={'w-full mb-6'}>
                  <TableRow title={'メールアドレス'}>
                    <Text text={String(email)} />
                  </TableRow>
                  <TableRow title={'パスワード'}>
                    <Text text={passResult} />
                  </TableRow>
                </table>
              </div>
              <div
                className={'flex justify-around mb-16 lg:w-1/2 mx-auto pt-6'}
              >
                <div className={'w-2/5 lg:w-1/3'}>
                  <RoundedButton
                    colorType={RoundedDivColorType.Negative}
                    size={RoundedDivSize.M}
                    onClick={fixOnClickHandler}
                    isDisabled={false}
                    text={'修正する'}
                  />
                </div>
                <div className={'w-2/5 lg:w-1/3'}>
                  <RoundedButton
                    colorType={RoundedDivColorType.Positive}
                    size={RoundedDivSize.M}
                    onClick={submitOnClickHandler}
                    isDisabled={false}
                    text={'送信する'}
                  />
                </div>
              </div>
            </section>
          </main>
        </>
      );
    case SignUpStatus.Loading:
      return (
        <>
          <div className="w-full flex justify-center">
            <LoadingCircle className="w-30 h-30 p-20 m-20" />
          </div>
        </>
      );
    case SignUpStatus.Submitted:
      return (
        <>
          <main>
            <Toast
              type={toastType}
              text={toastMessage}
              isShow={toastState}
              isShowSetter={setToastState}
            />
            <section className={'container mx-auto p-inner lg:px-48 py-4'}>
              <H2 text={'無料会員登録'} className={'mb-6'} />
              <div className={'mb-10  lg:w-1/2 mx-auto'}>
                <SmallText
                  text={
                    '会員登録確認用のメールを送りました。'
                  }
                />
                <SmallText
                  text={
                    'メール内容をご確認の上、会員登録を完了させてからログインしてください。'
                  }
                />
              </div>
            </section>
          </main>
        </>
      );
  }
};
export default Index;

###2.次にログイン画面の作成をしました。

SNSでのログインボタン2つとメールアドレスとパスワード入力の入力欄と
パスワードを忘れた方用のボタンを追加しました。
※2番と3番は同じファイルで作成したので3番の後にコード掲載しています

###3.そしてパスワードお忘れの方用の画面とその送信完了画面の作成をしました。

ここではメールアドレスの入力欄と送信ボタンを作成しました。
ここで送信ボタンを押すと入力されたメールアドレスに再設定用メールが送信されます。
そのメールの中にあるURLをクリックするとパスワード再設定画面に遷移します。
※この画面を作成しなくても送られてきたメールの中で再設定もできますが
今回はアプリ上で再設定する形で進めていきます
※ログインページとパスワードお忘れ用のページは
Stateで管理しているのでlogin/indexで作っています。

type Props = {
  setIsSignedIn: Function;
};

enum LoginStatus {
  Login,
  Forget,
  Loading,
  Submitted,
}

const Index: React.FC<Props> = ({ setIsSignedIn }) => {
  const [toastMessage, setToastMessage] = useState<string>('');
  const [toastType, setToastType] = useState<ToastType>(ToastType.Notification);
  const [toastState, setToastState] = useState<boolean>(false);
  const router = useRouter();
  //メールアドレスのバリデーション用
  const [email, setEmail] = useState<string>();
  const [emailError, setEmailError] = useState<string[]>([]);

  // ログイン画面・パスワードお忘れ画面・パスワード再設定画面
  const [status, setStatus] = useState<LoginStatus>(LoginStatus.Login);
  const [password, setPassword] = useState<string>('');
  const [passwordError, setPasswordError] = useState<string[]>([]);

  // 確認ボタンのisDisabled
  const [loginIsDisabled, setLoginIsDisabled] = useState<boolean>(true);
  const [forgetIsDisabled, setForgetIsDisabled] = useState<boolean>(true);
  const emailValidateHandler = () => {
    let errors: string[] = [];

  //SNSログインかどうかのハンドラー
  const signInHandler = (type: number) => {
    UserPresenterInstance.signIn({ type: type }).then(async (response) => {
      if (response.body != undefined) {
  //SNSログインだったら専用ページに遷移
        setIsSignedIn(true);
        router.push('/mypage');
      } else {
        //問題があればエラートースト表示
        setToastType(ToastType.Error);
        setToastMessage(response.error as string);
      }
    });
  };

  //メールアドレスかパスワードのどちらかでエラー出ていた場合
  useEffect(() => {
   //バリデーションにエラーがないときにログインボタン表示
    LoginIsDisableHandler();
    ForgetIsDisabledHandler();
  }, [emailError, passwordError]);
  

  const LoginIsDisableHandler = () => {
   //省略
  };

  const ForgetIsDisabledHandler = () => {
   //省略
  };

  const ForgetOnClickHandler = () => {
   //省略
  };
 //メールアドレスのログインハンドラー
  const emailSignInHandler = () => {
    UserPresenterInstance.emailSignIn(email!, password!).then((response) => {
      if (response.body != undefined) {
        setIsSignedIn(true);
        router.push('/mypage');
      } else {
        setToastType(ToastType.Error);
        setToastMessage('メールアドレス、もしくはパスワードが間違っています');
        setToastState(true);
      }
    });
  };
  //パスワード忘れのEmail用ハンドラー
  const sendEmailHandler = () => {
    UserPresenterInstance.changePassword(
      email!,
      changePasswordStage.sendEmail
    ).then((response) => {
      //通れば2秒間のローディング後にStateで画面切り替え
      if (response.body != undefined) {
        setStatus(LoginStatus.Loading);
        setTimeout(() => {
          setStatus(LoginStatus.Submitted);
        }, 2000);
      } else {
        //エラートースト
        setToastType(ToastType.Error);
        setToastMessage('このメールアドレスは登録されていません');
        setToastState(true);
      }
    });
  };
  //メール送信完了後のトースト処理
    useEffect(() => {
    if (status == LoginStatus.Submitted) {
      setToastType(ToastType.Notification);
      setToastMessage('パスワード再設定メールを送信しました');
      setToastState(true);
    }
  }, [status]);

  switch (status) {
    //ログインページ
    case LoginStatus.Login:
      return (
        <>
          <main>
            <Toast
              type={toastType}
              text={toastMessage}
              isShow={toastState}
              isShowSetter={setToastState}
            />
            <section className="container mx-auto p-inner my-4">
              <H2 text="ログイン" className={'p-inner'} />
              <div className={'lg:pt-16  lg:flex'}>
                <div className="w-full lg:w-3/5">
                  <H2
                    text="SNSアカウントでログイン"
                    className={'mx-auto text-center mt-6'}
                  />
                  <div className=" mx-auto w-full lg:w-100 ">
                    <div className="px-8 mx-auto max-w-2xl mb-5 relative">
                      <Icon
                        type={IconType.FacebookLogin}
                        className="absolute pt-4 pl-5"
                      />
                      <RoundedButton
                        text={'Facebookで登録'}
                        isDisabled={false}
                        colorType={RoundedDivColorType.Facebook}
                        size={RoundedDivSize.M}
                        onClick={() => signInHandler(SignInType.Facebook)}
                      />
                    </div>
                    <div className="px-8 mx-auto max-w-2xl mb-5 relative">
                      <Icon
                        type={IconType.TwitterLogin}
                        className="absolute pt-4 pl-5"
                      />
                      <RoundedButton
                        text={'Twitterで登録'}
                        isDisabled={false}
                        colorType={RoundedDivColorType.Twitter}
                        size={RoundedDivSize.M}
                        onClick={() => signInHandler(SignInType.Twitter)}
                      />
                    </div>
                  </div>
                </div>
                <div className={'w-full lg:w-3/5 mt-12 lg:mt-6'}>
                  <H2
                    text={'メールアドレスでログイン'}
                    className={'mb-6 mx-auto text-center'}
                  />
                  <div className=" mx-auto sm:w-10/12">
                    <table className={'w-full'}>
                      <tbody>
                        <tr>
                          <ContactListItem
                            title={'メールアドレス'}
                            form={
                              <Input
                                holder={'yourmail@subhika.jp'}
                                setter={setEmail}
                                value={email}
                                onBlur={emailValidateHandler}
                              />
                            }
                            error={emailError}
                          />
                        </tr>
                        <tr>
                          <ContactListItem
                            title={'パスワード'}
                            form={
                              <Input
                                setter={setPassword}
                                value={password}
                                onBlur={passwordValidateHandler}
                                type="password"
                              />
                            }
                            error={passwordError}
                          />
                        </tr>
                        <p className="text-center lg:text-left mb-2">
                          <TextButton
                            onClick={ForgetOnClickHandler}
                            text={'パスワードをお忘れの方はこちら'}
                            textClassName={'text-xs'}
                          />
                        </p>
                      </tbody>
                    </table>
                  </div>
                  <div className=" mx-auto w-4/5 sm:w-8/12">
                    <RoundedButton
                      colorType={RoundedDivColorType.Positive}
                      size={RoundedDivSize.M}
                      onClick={emailSignInHandler}
                      isDisabled={loginIsDisabled}
                      text={'ログイン'}
                    />
                  </div>
                </div>
              </div>
            </section>
          </main>
        </>
      );
    //パスワード忘れ画面
    case LoginStatus.Forget:
      return (
        <>
          <main>
            <Toast
              type={toastType}
              text={toastMessage}
              isShow={toastState}
              isShowSetter={setToastState}
            />
            <section className="container mx-auto py-4">
              <H2 text="パスワードお忘れの方" className="p-inner" />
              <div className="text-justify lg:w-1/2 w-11/12 mx-auto mt-4 mb-6">
                <Text text="パスワードを設定してください。" />
              </div>
              <table className={'lg:w-1/2 w-11/12 mx-auto'}>
                <tbody>
                  <tr>
                    <ContactListItem
                      title={'メールアドレス'}
                      form={
                        <Input
                          holder={'yourmail@subhika.jp'}
                          setter={setEmail}
                          value={email}
                          onBlur={emailValidateHandler}
                        />
                      }
                      error={emailError}
                    />
                    <div className={'my-10 lg:w-1/2 w-3/4 mx-auto'}>
                      <RoundedButton
                        colorType={RoundedDivColorType.Positive}
                        size={RoundedDivSize.M}
                        onClick={sendEmailHandler}
                        isDisabled={forgetIsDisabled}
                        text={'パスワード再設定メールを送信'}
                      />
                    </div>
                  </tr>
                </tbody>
              </table>
            </section>
          </main>
        </>
      );
    //ページ間のローディングに使用
    case LoginStatus.Loading:
      return (
        <>
          <div className="w-full flex justify-center">
            <LoadingCircle className="w-30 h-30 p-20 m-20" />
          </div>
        </>
      );
    //確認メール送信完了画面
    case LoginStatus.Submitted:
      return (
        <>
          <main>
            <section className="container mx-auto py-4">
              <Toast
                type={toastType}
                text={toastMessage}
                isShow={toastState}
                isShowSetter={setToastState}
              />
              <H2 text="パスワードお忘れの方" className="p-inner" />
              <div className="text-justify lg:w-1/2 w-11/12 mx-auto my-10">
                <Text text="パスワード再設定用のメールをお送りしました。" />
              </div>
            </section>
          </main>
        </>
      );
  }
};

###4.最後にパスワード再設定画面を作成しました。

パスワード再設定用のURLからここに遷移してきます。
ここではパスワードと確認用パスワードの入力欄と登録ボタンを作成しました。
※ここで確認メール画面、パスワード再設定後の完了画面もStateで管理しています。

type Props = {
  setIsSignedIn: Function;
};
//ここではパスワードリセットの再設定ページとローディングと
// 送信完了ページと新規登録の確認メールタップ後の遷移画面をstateで管理
//確認メールかパスワード再設定メールかはクエリのmode(verifyEmailかresetPassword)を見て判断する

enum MailStatus {
  Input,
  Loading,
  Submitted,
  SignUpVerified,
}

const Index: React.FC<Props> = ({ setIsSignedIn }) => {
  const [toastMessage, setToastMessage] = useState<string>('');
  const [toastType, setToastType] = useState<ToastType>(ToastType.Notification);
  const [toastState, setToastState] = useState<boolean>(false);
  //追加
  const [password, setPassword] = useState<string>();
  const [passwordError, setPasswordError] = useState<string[]>([]);
  const [rePassword, setRePassword] = useState<string>();
  const [rePasswordError, setRePasswordError] = useState<string[]>([]);
  const [status, setStatus] = useState<MailStatus>(MailStatus.Input);

  const router = useRouter();

  const passwordValidateHandler = () => {
  //省略
  };

  const rePasswordValidateHandler = () => {
  //省略
  };

  const submitOnClickHandler = () => {
    UserPresenterInstance.changePassword(
        //この/login/verifiedではメールアドレスのデータがないので
        //クエリで前の画面からこのページに渡している
      String(router.query.email), //←ここのemailはfirebaseコンソール(webサイト)で設定しています
      changePasswordStage.saveNewPassword,
      String(router.query.oobCode),
      rePassword
    ).then((response) => {
      if (response.body != undefined) {
        setStatus(MailStatus.Loading);
        setTimeout(() => {
          setStatus(MailStatus.Submitted);
        }, 2000);
      }
    });
  };

  useEffect(() => {
    //送信完了のとき
    if (status == MailStatus.Submitted) {
      setToastType(ToastType.Notification);
      setToastMessage('パスワードの再設定が完了しました。');
      setToastState(true);
      UserPresenterInstance.emailSignIn(String(router.query.email), password!);
      setIsSignedIn(true);
      setTimeout(() => {
        setIsSignedIn(true);
        router.push('/mypage');
      }, 2000);
    }
    //確認メールが送信された後
    if (status == MailStatus.SignUpVerified) {
      setToastType(ToastType.Notification);
      setToastMessage('メールアドレスの本人認証が完了しました。');
      setToastState(true);
    }
  }, [status]);

  useEffect(() => {
    //メールのURLタップ後のURLのmodeが確認メールのmodeだったら
    if (String(router.query.mode) === 'verifyEmail') {
      setStatus(MailStatus.Loading);
      setTimeout(() => {
        setIsSignedIn(true);
        setStatus(MailStatus.SignUpVerified);
      }, 2000);
    }
    //メールのURLタップ後のURLのmodeがパスワード再設定メールのmodeだったら
    if (String(router.query.mode) === 'resetPassword') {
      UserPresenterInstance.changePassword(
        String(router.query.email), ////←ここのemailはfirebaseコンソール(webサイト)で設定しています
        changePasswordStage.resetNewPassword,
        String(router.query.oobCode)
      );
    }
  }, [router.query.mode]);

  switch (status) {
    case MailStatus.Input:
      return (
        <>
          <main>
            <section className="container mx-auto py-4">
              <H2 text="パスワード再設定" className="p-inner" />

              <div className="lg:flex justify-center items-center py-4">
                <div className="w-full">
                  <div className="px-8 mx-auto max-w-2xl mb-5">
                    <table className={'sm:w-4/5 mx-auto'}>
                      <tbody>
                        <tr>
                          <div className={'py-12'}>
                            <Text
                              text={
                                'パスワードを再設定'
                              }
                            />
                          </div>
                        </tr>
                        <tr>
                          <ContactListItem
                            title={'新しいパスワード'}
                            form={
                              <Input
                                type="password"
                                setter={setPassword}
                                value={password}
                                onBlur={passwordValidateHandler}
                              />
                            }
                            require={false}
                            error={passwordError}
                          />
                        </tr>
                        <tr>
                          <ContactListItem
                            title={'新しいパスワード(確認用)'}
                            form={
                              <Input
                                type="password"
                                setter={setRePassword}
                                value={rePassword}
                                onBlur={rePasswordValidateHandler}
                              />
                            }
                            require={false}
                            error={rePasswordError}
                          />
                        </tr>
                      </tbody>
                    </table>
                    <div className={'mx-auto mt-12 w-8/12'}>
                      <RoundedButton
                        text={'パスワードを設定'}
                        isDisabled={
                          !password ||
                          password !== rePassword ||
                          passwordError.length !== 0 ||
                          rePasswordError.length !== 0
                        }
                        colorType={RoundedDivColorType.Positive}
                        size={RoundedDivSize.M}
                        onClick={submitOnClickHandler}
                      />
                    </div>
                  </div>
                </div>
              </div>
            </section>
          </main>
        </>
      );

    case MailStatus.Loading:
      return (
        <>
          <div className="w-full flex justify-center">
            <LoadingCircle className="w-30 h-30 p-20 m-20" />
          </div>
        </>
      );

    case MailStatus.Submitted:
      return (
        <>
          <main>
            <section className="container mx-auto py-4">
              <Toast
                type={toastType}
                text={toastMessage}
                isShow={toastState}
                isShowSetter={setToastState}
              />
              <H2 text="パスワード再設定" className="p-inner" />
              <div className="lg:flex justify-center items-center py-4">
                <div className="w-full lg:w-100">
                  <div className="px-8 mx-auto max-w-2xl mb-5 relative">
                    <div className={'py-12'}>
                      <Text text={'パスワードの再設定が完了しました'} />
                    </div>
                  </div>
                </div>
              </div>
            </section>
          </main>
        </>
      );
    case MailStatus.SignUpVerified:
      return (
        <>
          <main>
            <Toast
              type={toastType}
              text={toastMessage}
              isShow={toastState}
              isShowSetter={setToastState}
            />
            <section className="container mx-auto py-4">
              <H2 text="メールアドレス認証" className="p-inner" />
              <div className="lg:flex justify-center items-center py-4">
                <div className="w-full lg:w-100">
                  <div className="px-8 mx-auto max-w-2xl mb-5 relative">
                    <div className={'py-12'}>
                      <Text text={'メールアドレスの本人認証が完了しました。'} />
                    </div>
                  </div>
                </div>
              </div>
            </section>
          </main>
        </>
      );
  }
};

export default Index;

##ロジック編
そしてここでfirebaseのロジックについてです!
firebaseを使えば簡単に新規会員登録やログインを実装できます。
oobCodeとは自動発行されたワンタイムパスワードのことです。

###SNS登録


  /**
   * ユーザサインイン
   * @param type: 認証プロバイダタイプ
   * @return Promise<any>: ステータスデータ
   */
  async signIn(type: SignInType): Promise<any> {
    const facebookProvider = new firebase.auth.FacebookAuthProvider();
    const twitterProvider = new firebase.auth.TwitterAuthProvider();
    switch (type) {
      case SignInType.Facebook:
        return await firebase.auth().signInWithPopup(facebookProvider);
      case SignInType.Twitter:
        return await firebase.auth().signInWithPopup(twitterProvider);
      default:
        throw new Error('Not exist type is selected');
    }
  }

  /**
   * ユーザサインアウト
   * @return Promise<any>: ステータスデータ
   */
  async signOut(): Promise<any> {
    return await firebase.auth().signOut();
  }

###メールアドレスでサインイン、ログイン

  /**
   * emailによる新規会員登録
   * 新規のメールアドレスの場合、~/signUp/verified へのリダイレクトリンクがメールに送られる。
   * @param email: メールアドレス
   * @param pass: パスワード
   * @return Promise<any>: ステータスデータ
   */
  async emailSignUp(email: string, pass: string): Promise<any> {
    try {
      await firebase.auth().fetchSignInMethodsForEmail(email);
      await firebase.auth().createUserWithEmailAndPassword(email, pass);
      await firebase.auth().currentUser?.sendEmailVerification({
        url: `${process.env.NEXT_PUBLIC_URL}/URL名`,
        handleCodeInApp: false,
      });
    } catch (error) {
      return {
        statusCode: HttpStatusCode.BAD_REQUEST,
        error: error.message,
      };
    }
  }

  /**
   * emailによるログイン
   * @param email: メールアドレス
   * @param pass: パスワード
   * @return Promise<any>: ステータスデータ
   */
  async emailSignIn(email: string, pass: string): Promise<any> {
    return await firebase.auth().signInWithEmailAndPassword(email, pass);
  }

  /**
   * パスワード変更
   * @param email: パスワードを変更するアカウントのemail
   * @param stage: パスワード変更の段階(sendEmail: パスワード変更の際のemail送信, resetNewPassword: パスワードをリセット, saveNewPassword: パスワードを登録し、更新)
   * @param oobCode: oobCode(クエリからとってくる)
   * @param newPassword: 入力された新規パスワード
   */
  async changePassword(
    email: string,
    stage: changePasswordStage,
    oobCode?: string,
    newPassword?: string
  ): Promise<any> {
    switch (stage) {
      case changePasswordStage.sendEmail:
        await firebase.auth().sendPasswordResetEmail(email);
        //このメールの遷移先urlはfirebaseコンソールのAuthenticationのtemplateで/login/verifiedにクエリoobcodeとemailを持つようにしている
        break;
      case changePasswordStage.resetNewPassword:
        await firebase.auth().verifyPasswordResetCode(oobCode!).catch((error) => {
          return error.message;
        });
        break;
      case changePasswordStage.saveNewPassword:
        try {
          await firebase.auth().confirmPasswordReset(oobCode!, newPassword!);
          const credential = firebase.auth.EmailAuthProvider.credential(
            email,
            newPassword!
          );
          return await firebase.auth().signInWithCredential(credential);
        } catch (error) {
          return error.message;
        }
    }
  }

##Firebase Console編
そして最後にfirebaseConsoleの設定です。
firebaseConsoleを使えばメール内容の変更、新規登録したことのあるユーザーの確認、削除などができます。メールに添付するURLの内容も変更できるので今回はこれを使ってE-mailのデータをクエリに追加します。jまた使うログイン方法を設定する必要もあるのでそれもやっていきます。

###ログインプロバイダの設定
まずはログイン方法の設定です。
方法はいたって簡単です!firebaseConsoleに移動し
Authentication→Sign-in method
をクリックします。
するとログインプロバイダが一覧表示されるのでこれを有効にします。
今回はメール/パスワード、Twitter,Facebookを使用するのでこれらを有効にしておきます。

2020-12-11 (5).png

###メールアドレスURL内のクエリ変更
次にメールアドレスに添付するURLのクエリにそのメールアドレスの情報を入れる方法です。

これも簡単で
Authentication→Templates→パスワードの再設定
に移動し右側の編集ボタンを押します。
すると以下のような画面が出ますので%LINK%を以下のように変更すれば完了です。
<p><a href='%LINK%&email=%EMAIL%'>%LINK%&email=%EMAIL%</a></p>

2020-12-11 (6).png

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?