Firebaseに作成したアプリに登録したユーザのパスワードを変更するためのReactのコードを解説

パスワードを入力 プログラミング

Firebaseに作成したReactのアプリに登録したユーザのパスワードを変更するための処理にフォーカスを当てて解説します。

※ ソースはこちら:Github

※ アプリはこちら:「Firebaseに認証するだけのアプリ」

※ パッケージのバージョン:”firebase”: “^9.22.1″、”react”: “^18.2.0″ (詳細はGithubのpackage.jsonを参照してください。)

※ Firebaseの認証周り全般の解説はこちらを参照してください。

ホーム画面

ホーム画面は、

  • Home.js(メインの画面)
  • HomeChild.js(ボタンをクリックしたときに出力)

によって構成されています。

ソースコードはこちらです。

// Home.js

import { signOut } from "firebase/auth";
import { useNavigate } from "react-router-dom";
import { useState } from "react";
import { createPortal } from "react-dom";
import { HomePasswordChangeModal, HomeWithdrawalChild } from "./HomeChild";
import { auth } from "../service/firebase/firebase";
import { useAuthContext } from "../context/AuthContext";

import Title from "./Title";
import { Button } from "@mui/material";
import { Logout } from "@mui/icons-material";
import PersonRemoveIcon from "@mui/icons-material/PersonRemove";
import PublishedWithChangesIcon from '@mui/icons-material/PublishedWithChanges';

const ModalPortal = ({ children }) => {
  const target = document.querySelector(".modalContainer");
  return createPortal(children, target);
};

const Home = () => {
  const [withdrawalModalOpen, setWithdrawalModalOpen] = useState(false);
  const [passwordChangeModalOpen, setPasswordChangeModalOpen] = useState(false);
  const navigate = useNavigate();
  const { user } = useAuthContext();
  const handleLogout = () => {
    signOut(auth);
    navigate("/login");
  };

  return (
    <>
      <div>
        <Title />
        <div className="modalContainer"></div>
        <p>現在ログインしているユーザーの情報</p>
        <p>email : {user.email}</p>
        <p>
          Provider :
          {user.providerData[0].providerId === "password"
            ? "email Login"
            : user.providerData[0].providerId === "google.com"
            ? "Google"
            : "invalid provider"}
        </p>
        <p>
          {!user.emailVerified &&
            "アドレス未認証のためTodoは入力できません。(メール認証が終了したら、リロードしてください。)"}
        </p>
          <Button
            onClick={handleLogout}
            variant="outlined"
            startIcon={<Logout />}
            sx={{ mr:1 }}
          >
            ログアウト
          </Button>
          <Button
            onClick={() => setWithdrawalModalOpen(true)}
            variant="outlined"
            startIcon={<PersonRemoveIcon />}
            sx={{ mr:1 }}
          >
            退会
          </Button>
          {user.providerData[0].providerId === "password"
          && <Button onClick={() => setPasswordChangeModalOpen(true)}
            variant="outlined"
            startIcon={<PublishedWithChangesIcon />}
          >
            パスワード変更
          </Button> }
      </div>
      {withdrawalModalOpen && (
        <ModalPortal>
          <HomeWithdrawalChild
            setWithdrawalModalOpen={setWithdrawalModalOpen}
          />
        </ModalPortal>
      )}
      {passwordChangeModalOpen && (
        <ModalPortal>
          <HomePasswordChangeModal
            setPasswordChangeModalOpen={setPasswordChangeModalOpen}
          />
        </ModalPortal>
      )}
    </>
  );
};

export default Home;

// HomeChild.js
import { signOut } from "firebase/auth";
import { useNavigate } from "react-router-dom";
import { useState } from "react";
import { createPortal } from "react-dom";
import { HomePasswordChangeModal, HomeWithdrawalChild } from "./HomeChild";
import { auth } from "../service/firebase/firebase";
import { useAuthContext } from "../context/AuthContext";

import Title from "./Title";
import { Button } from "@mui/material";
import { Logout } from "@mui/icons-material";
import PersonRemoveIcon from "@mui/icons-material/PersonRemove";
import PublishedWithChangesIcon from '@mui/icons-material/PublishedWithChanges';

const ModalPortal = ({ children }) => {
  const target = document.querySelector(".modalContainer");
  return createPortal(children, target);
};

const Home = () => {
  const [withdrawalModalOpen, setWithdrawalModalOpen] = useState(false);
  const [passwordChangeModalOpen, setPasswordChangeModalOpen] = useState(false);
  const navigate = useNavigate();
  const { user } = useAuthContext();
  const handleLogout = () => {
    signOut(auth);
    navigate("/login");
  };

  return (
    <>
      <div>
        <Title />
        <div className="modalContainer"></div>
        <p>現在ログインしているユーザーの情報</p>
        <p>email : {user.email}</p>
        <p>
          Provider :
          {user.providerData[0].providerId === "password"
            ? "email Login"
            : user.providerData[0].providerId === "google.com"
            ? "Google"
            : "invalid provider"}
        </p>
        <p>
          {!user.emailVerified &&
            "アドレス未認証のためTodoは入力できません。(メール認証が終了したら、リロードしてください。)"}
        </p>
          <Button
            onClick={handleLogout}
            variant="outlined"
            startIcon={<Logout />}
            sx={{ mr:1 }}
          >
            ログアウト
          </Button>
          <Button
            onClick={() => setWithdrawalModalOpen(true)}
            variant="outlined"
            startIcon={<PersonRemoveIcon />}
            sx={{ mr:1 }}
          >
            退会
          </Button>
          {user.providerData[0].providerId === "password"
          && <Button onClick={() => setPasswordChangeModalOpen(true)}
            variant="outlined"
            startIcon={<PublishedWithChangesIcon />}
          >
            パスワード変更
          </Button> }
      </div>
      {withdrawalModalOpen && (
        <ModalPortal>
          <HomeWithdrawalChild
            setWithdrawalModalOpen={setWithdrawalModalOpen}
          />
        </ModalPortal>
      )}
      {passwordChangeModalOpen && (
        <ModalPortal>
          <HomePasswordChangeModal
            setPasswordChangeModalOpen={setPasswordChangeModalOpen}
          />
        </ModalPortal>
      )}
    </>
  );
};

export default Home;

ホーム画面の「パスワードの変更」ボタン

メールとパスワードで登録したユーザーのホーム画面
「パスワードの変更」ボタンのある画面
// Home.js抜粋
~
          {user.providerData[0].providerId === "password"
          && <Button onClick={() => setPasswordChangeModalOpen(true)}
            variant="outlined"
            startIcon={<PublishedWithChangesIcon />}
          >
            パスワード変更
          </Button> }
~

ホーム画面の「パスワードの変更」ボタンをクリックすることによってpasswordChangeModalOpenがtrueになり、パスワード変更のためのモーダル画面が表示されます。

パスワード変更は当該アプリにメールとパスワードで登録したユーザーのみなので、Google IDでユーザー登録したユーザーのホーム画面では、「パスワード変更」のボタンは表示しません。

「パスワード変更」のためのモーダル画面

新パスワード入力画面
パスワード変更のためのモーダル画面
// HomeChild.js抜粋
~
export const HomePasswordChangeModal = ({ setPasswordChangeModalOpen }) => {
  const handleChangePassword = async (e) => {
    e.preventDefault();
    const [oldPassword, newPassword, sameNewPassword] = e.target.elements;
    if (!oldPassword.value) {
      issueMsg("「今までのパスワード」が入力されていません");
      return;
    } else if (!newPassword.value) {
      issueMsg("「新しいパスワード」が入力されていません");
      return;
    } else if (!sameNewPassword.value) {
      issueMsg("「新しいパスワードをもう一度」が入力されていません");
      return;
    }
    if (newPassword.value !== sameNewPassword.value) {
      issueMsg(
        "「新しいパスワード」と「新しいパスワードをもう一度」が不一致です"
      );
      return;
    }

    await changePassword(oldPassword.value, newPassword.value);
    setPasswordChangeModalOpen(false);
  };
  return (
    <div className="modal">
      <div className="modal__content">
        <form onSubmit={handleChangePassword}>
          <div>
            <label htmlFor="oldPassword">
              今までのパスワード
              <input type="password" name="oldPassword" id="oldPassword" />
            </label>
          </div>
          <div>
            <label htmlFor="newPassword">
              新しいパスワード
              <input type="password" name="newPassword" id="newPassword" />
            </label>
          </div>
          <div>
            <label htmlFor="sameNewPassword">
              新しいパスワードをもう一度
              <input
                type="password"
                name="sameNewPassword"
                id="sameNewPassword"
              />
            </label>
          </div>
          <div>
            <button>パスワードを変更する</button>
          </div>
        </form>
        <button onClick={() => setPasswordChangeModalOpen(false)}>
          キャンセル
        </button>
      </div>
    </div>
  );
};
~

パスワード変更のためのモーダル画面に、

  • 現在のパスワード
  • 新しいパスワード
  • 新しいパスワードもう一度

を入力し、エラーチェックをして問題ないなら、changePassword()をコールします。

changePassword.js:パスワード変更処理モジュール

// changePassword.js

import getCredential from "../firebase/getCredential";
import { updatePassword } from "firebase/auth";
import { auth } from "../firebase/firebase";
import { issueMsg } from "../common/issueMsg";

export const changePassword = async (oldPassword,newPassword) => {

    const user = auth.currentUser;

    try{
        await getCredential('email',user, oldPassword);
        await updatePassword(user,newPassword);
        issueMsg(`パスワードの変更が完了しました。:${user.email}`);
        return true;
      } catch(error) {
        if (error.code === 'auth/wrong-password') {
        issueMsg('入力したパスワードが不正です。');
        } else {
          issueMsg(`パスワード変更処理で不正が発生しました。${error.code}`);
        }
        console.log(`パスワード変更失敗`);
        console.log(user);
        console.error(error);
        return false;
      }
  }

export default changePassword;

getCredential():再認証処理

パスワード変更は再認証処理が必要になります。

そのためgetCredential()をコールして、再認証を行います。

再認証処理についてはこちらの記事のgetCredentialの所を参照してください。

updatePassword():パスワード更新処理

getCredential()が成功したなら、updatePassword()を発行して、パスワードの変更を行います。

パスワードの変更完了のポップアップ
パスワードの変更が正常に完了

パスワードの変更が正常に終了したなら、ポップアップメッセージを出力して、ユーザーにパスワードの変更が完了したことを知らせます。

以上が、パスワードの変更処理になります。

最後に

当該Reactのプロジェクトのsrc配下のファイル構造

以下が、当該Reactのプロジェクトのsrc配下のファイル構造です。

SRC
│  App.css
│  App.js
│  App.test.js
│  index.css
│  index.js
│  reportWebVitals.js
│  service-worker.js
│  serviceWorkerRegistration.js
│  setupTests.js
│
├─components
│  │  Home.js
│  │  HomeChild.js
│  │  Login.js
│  │  PrivateRoute.js
│  │  PublicRoute.js
│  │  SignUp.js
│  │  Title.js
│  │
│  └─styles
│          portal.css
│
├─context
│      AuthContext.js
│
└─service
    ├─authenticationProcess
    │      changePassword.js
    │      delUser.js
    │
    ├─common
    │      issueMsg.js
    │
    └─firebase
            firebase.js
            getCredential.js
            loginFirebase.js
            registration.js
            sendEmailLink.js
            submitPasswordResetEmail.js

このブログに掲載されているコードは、著作権者(私)に帰属します。コードを自由に使用、複製、改変、配布、販売、サブライセンスできます。また、コードを使用した結果、何らかの損害が発生した場合でも、著作権者は一切責任を負いません。

タイトルとURLをコピーしました