109 lines
4.3 KiB
TypeScript
109 lines
4.3 KiB
TypeScript
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";
|
||
import Header from "components/header";
|
||
import { loadAccessToken, loadRefreshToken } from "features/auth/utils";
|
||
import { loginAsync, selectLoginApiCallStatus } from "features/login";
|
||
import React, { useCallback, useEffect } from "react";
|
||
import { useTranslation } from "react-i18next";
|
||
import { useDispatch, useSelector } from "react-redux";
|
||
import { useNavigate } from "react-router-dom";
|
||
|
||
const LoginPage: React.FC = (): JSX.Element => {
|
||
const { instance } = useMsal();
|
||
const dispatch: AppDispatch = useDispatch();
|
||
const navigate = useNavigate();
|
||
const [, i18n] = useTranslation();
|
||
const status = useSelector(selectLoginApiCallStatus);
|
||
|
||
const login = useCallback(
|
||
async (idToken: string) => {
|
||
// ログイン処理呼び出し
|
||
const { meta } = await dispatch(loginAsync({ idToken }));
|
||
|
||
// ログイン失敗した場合、B2Cをログアウトしてからエラーページに遷移する
|
||
if (meta.requestStatus === "rejected") {
|
||
instance.logoutRedirect({
|
||
postLogoutRedirectUri: "/AuthError",
|
||
});
|
||
}
|
||
if (meta.requestStatus === "fulfilled") {
|
||
const accessToken = loadAccessToken();
|
||
const refreshToken = loadRefreshToken();
|
||
const url = `${
|
||
import.meta.env.VITE_DESK_TOP_APP_SCHEME
|
||
}:login?accessToken=${accessToken}&refreshToken=${refreshToken}&language=${
|
||
i18n.language
|
||
}`; // カスタムURLスキーム
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
navigate("/xxx");
|
||
}
|
||
},
|
||
[dispatch, i18n.language, instance, navigate]
|
||
);
|
||
|
||
// TODO 将来的にトークンの取得処理をoperations.ts側に移動させたい。useEffect内で非同期処理を行いたくない。
|
||
useEffect(() => {
|
||
if (status !== "none") {
|
||
// ログイン処理で、何回か本画面が描画される契機があるが、認証処理は一度だけ実施すればよいため認証処理実行済みであれば何もしない
|
||
return;
|
||
}
|
||
|
||
(async () => {
|
||
try {
|
||
const loginResult = await instance.handleRedirectPromise();
|
||
|
||
// eslint-disable-next-line
|
||
console.log({ loginResult }); // TODO:loading画面から遷移できない事象の調査用ログ。事象解消後削除(eslint-disable含めて)する。
|
||
|
||
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) {
|
||
const idTokenObject = JSON.parse(idTokenString);
|
||
if (isIdToken(idTokenObject)) {
|
||
await login(idTokenObject.secret);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
// eslint-disable-next-line
|
||
console.log({ e }); // TODO:loading画面から遷移できない事象の調査用ログ。事象解消後削除(eslint-disable含めて)する。
|
||
|
||
// AAD B2Cの多要素認証画面やパスワードリセット画面で「cancel」をクリックすると、handleRedirectPromise()にてエラーが発生するため、
|
||
// それをハンドリングして適切な画面遷移処理を行う。
|
||
if (e instanceof AuthError) {
|
||
// エラーコードはerrorMessageの中の一部として埋め込まれており完全一致で取得するのは筋が悪いため、部分一致で取得する。
|
||
// TODO 他にもAADB2Cのエラーコードを使用する箇所が出てきた場合、定数化すること
|
||
if (e.errorMessage.startsWith("AADB2C90091")) {
|
||
navigate("/");
|
||
}
|
||
}
|
||
}
|
||
})();
|
||
}, [instance, login, navigate, status]);
|
||
|
||
return (
|
||
<>
|
||
<Header />
|
||
<h3>loading ...</h3>
|
||
<Footer />
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default LoginPage;
|