Firebaseに作成したReactのアプリに登録したユーザのパスワードを変更するための処理にフォーカスを当てて解説します。
※ ソースはこちら:Github
※ アプリはこちら:「Firebaseに認証するだけのアプリ」
※ パッケージのバージョン:”firebase”: “^9.22.1″、”react”: “^18.2.0″ (詳細はGithubのpackage.jsonを参照してください。)
※ Firebaseの認証周り全般の解説はこちらを参照してください。
ホーム画面
ホーム画面は、
によって構成されています。
ソースコードはこちらです。
// 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
このブログに掲載されているコードは、著作権者(私)に帰属します。コードを自由に使用、複製、改変、配布、販売、サブライセンスできます。また、コードを使用した結果、何らかの損害が発生した場合でも、著作権者は一切責任を負いません。