Merged PR 151: パスワードリセット画面にて「Cancel」をクリックすると、「loading」画面で止まる問題を解決する

## 概要
[Task1921: パスワードリセット画面にて「Cancel」をクリックすると、「loading」画面で止まる問題を解決する](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1921)

- 「補足」の「実装参考記事」を参考に、ログインリダイレクト時のハンドリング方法を修正
- ログインリダイレクト処理におけるAADB2Cからのエラーハンドリングを実装し、
エラーコードが「ユーザーによる操作キャンセル」だった場合、トップ画面に戻すよう修正

## レビューポイント
- ログインリダイレクト時のハンドリング方法は不適切ではないか
   - とくに、React hookの使い方でアンチパターンになっているようなことはないか

## UIの変更
- なし

## 動作確認状況
- ローカルにて、ログインできることを確認
- ローカルにて、ユーザーフローキャンセル時にトップ画面に戻ることを確認(二要素認証、パスワードリセットともに)

## 補足
- 実装参考記事
   - [その1](https://learn.microsoft.com/ja-jp/azure/active-directory/develop/msal-js-initializing-client-applications#initialize-msaljs-2x-apps)
   - [その2](https://learn.microsoft.com/ja-jp/azure/active-directory/develop/msal-error-handling-js)
- [AADB2Cエラーコードリファレンス](https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/error-codes)
This commit is contained in:
Kentaro Fukunaga 2023-06-22 23:24:35 +00:00
parent 2155539365
commit a2c5c436e1

View File

@ -1,4 +1,5 @@
import { useMsal } from "@azure/msal-react";
import { AuthError } from "@azure/msal-browser";
import { AppDispatch } from "app/store";
import { isIdToken } from "common/token";
import Footer from "components/footer";
@ -11,10 +12,7 @@ import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
const LoginPage: React.FC = (): JSX.Element => {
/* XXX B2CID
1655: ログイン周りの挙動について調査調
*/
const { accounts, instance } = useMsal();
const { instance } = useMsal();
const dispatch: AppDispatch = useDispatch();
const navigate = useNavigate();
const [, i18n] = useTranslation();
@ -34,7 +32,7 @@ const LoginPage: React.FC = (): JSX.Element => {
if (meta.requestStatus === "fulfilled") {
const accessToken = loadAccessToken();
const refreshToken = loadRefreshToken();
/* TODO
/* TODO 1899
*/
const url = `note:login?accessToken=${accessToken}&refreshToken=${refreshToken}&language=${i18n.language}`; // カスタムURLスキーム
@ -51,18 +49,24 @@ const LoginPage: React.FC = (): JSX.Element => {
// TODO 将来的にトークンの取得処理をoperations.ts側に移動させたい。useEffect内で非同期処理を行いたくない。
useEffect(() => {
if (status !== "none") {
// ログイン処理で、何回か本画面が描画される契機があるが、認証処理は一度だけ実施すればよいため認証処理実行済みであれば何もしない
return;
}
(async () => {
if (accounts.length >= 1) {
const { homeAccountId, idTokenClaims } = accounts[0];
if (idTokenClaims) {
if (idTokenClaims.aud) {
try {
const loginResult = await instance.handleRedirectPromise();
if (loginResult && loginResult.account) {
const { homeAccountId, idTokenClaims } = loginResult.account;
if (idTokenClaims && idTokenClaims.aud) {
// IDトークンの取得
const idTokenString = localStorage.getItem(
`${homeAccountId}-${
import.meta.env.VITE_B2C_KNOWNAUTHORITIES
}-idtoken-${idTokenClaims.aud}----`
);
if (idTokenString && status === "none") {
if (idTokenString) {
const idTokenObject = JSON.parse(idTokenString);
if (isIdToken(idTokenObject)) {
await login(idTokenObject.secret);
@ -70,9 +74,20 @@ const LoginPage: React.FC = (): JSX.Element => {
}
}
}
} catch (e) {
// AAD B2Cの多要素認証画面やパスワードリセット画面で「cancel」をクリックすると、handleRedirectPromise()にてエラーが発生するため、
// それをハンドリングして適切な画面遷移処理を行う。
if (e instanceof AuthError) {
// エラーコードはerrorMessageの中の一部として埋め込まれており完全一致で取得するのは筋が悪いため、部分一致で取得する。
// TODO 他にもAADB2Cのエラーコードを使用する箇所が出てきた場合、定数化すること
if (e.errorMessage.startsWith("AADB2C90091")) {
navigate("/");
}
}
}
})();
}, [accounts, login, status]);
}, [instance, login, navigate, status]);
return (
<>
<Header />