Merge branch 'develop' into ccb

This commit is contained in:
x.yumoto.k 2024-02-09 17:37:38 +09:00
commit cd277f3f9a
8 changed files with 53 additions and 79 deletions

View File

@ -1,8 +1,6 @@
import AppRouter from "AppRouter"; import AppRouter from "AppRouter";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser"; import { useMsal } from "@azure/msal-react";
import { MsalProvider, useMsal } from "@azure/msal-react";
import { msalConfig } from "common/msalConfig";
import { useEffect, useLayoutEffect } from "react"; import { useEffect, useLayoutEffect } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import globalAxios, { AxiosError, AxiosResponse } from "axios"; import globalAxios, { AxiosError, AxiosResponse } from "axios";
@ -19,7 +17,6 @@ const App = (): JSX.Element => {
const { instance } = useMsal(); const { instance } = useMsal();
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [t, i18n] = useTranslation(); const [t, i18n] = useTranslation();
const pca = new PublicClientApplication(msalConfig);
useEffect(() => { useEffect(() => {
const id = globalAxios.interceptors.response.use( const id = globalAxios.interceptors.response.use(
(response: AxiosResponse) => response, (response: AxiosResponse) => response,
@ -70,11 +67,9 @@ const App = (): JSX.Element => {
dispatch(closeSnackbar()); dispatch(closeSnackbar());
}} }}
/> />
<MsalProvider instance={pca}> <BrowserRouter>
<BrowserRouter> <AppRouter />
<AppRouter /> </BrowserRouter>
</BrowserRouter>
</MsalProvider>
</> </>
); );
}; };

View File

@ -76,3 +76,16 @@ export const getIdTokenFromLocalStorage = (
} }
return null; return null;
}; };
// JWTが有効期限切れかどうかを判定する
export const isTokenExpired = (token: string | null): boolean => {
if (token == null) {
return true;
}
const tokenObject = JSON.parse(atob(token.split(".")[1]));
if (isToken(tokenObject)) {
const now = Math.floor(Date.now() / 1000);
return tokenObject.exp < now;
}
return true;
};

View File

@ -3,18 +3,25 @@ import React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { I18nextProvider } from "react-i18next"; import { I18nextProvider } from "react-i18next";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { PublicClientApplication } from "@azure/msal-browser";
import { msalConfig } from "common/msalConfig";
import { MsalProvider } from "@azure/msal-react";
import App from "./App"; import App from "./App";
import * as serviceWorker from "./serviceWorker"; import * as serviceWorker from "./serviceWorker";
import i18n from "./i18n"; import i18n from "./i18n";
const pca = new PublicClientApplication(msalConfig);
const container = document.getElementById("root"); const container = document.getElementById("root");
if (container) { if (container) {
const root = createRoot(container); const root = createRoot(container);
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<Provider store={store}> <Provider store={store}>
<I18nextProvider i18n={i18n} /> <MsalProvider instance={pca}>
<App /> <I18nextProvider i18n={i18n} />
<App />
</MsalProvider>
</Provider> </Provider>
</React.StrictMode> </React.StrictMode>
); );

View File

@ -10,14 +10,6 @@ import {
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import {
clearToken,
isAdminUser,
isApproveTier,
isStandardUser,
loadAccessToken,
} from "features/auth";
import { TIERS } from "components/auth/constants";
const AuthPage: React.FC = (): JSX.Element => { const AuthPage: React.FC = (): JSX.Element => {
const { instance } = useMsal(); const { instance } = useMsal();
@ -34,38 +26,7 @@ const AuthPage: React.FC = (): JSX.Element => {
(async () => { (async () => {
try { try {
// ログイン済みの場合、ログイン後の遷移先を決定する // idTokenが有効セットされているかを確認する
if (loadAccessToken()) {
// 第一~第四階層の管理者はライセンス画面へ遷移
if (
isApproveTier([
TIERS.TIER1,
TIERS.TIER2,
TIERS.TIER3,
TIERS.TIER4,
]) &&
isAdminUser()
) {
navigate("/license");
return;
}
// 第五階層の管理者はユーザー画面へ遷移
if (isApproveTier([TIERS.TIER5]) && isAdminUser()) {
navigate("/user");
return;
}
// 一般ユーザーはdictationPageへ遷移
if (isStandardUser()) {
navigate("/dictations");
return;
}
// それ以外は認証エラー画面へ遷移
instance.logoutRedirect({
postLogoutRedirectUri: "/AuthError",
});
clearToken();
return;
}
const loginResult = await instance.handleRedirectPromise(); const loginResult = await instance.handleRedirectPromise();
if (loginResult && loginResult.account) { if (loginResult && loginResult.account) {
const { homeAccountId, idTokenClaims } = loginResult.account; const { homeAccountId, idTokenClaims } = loginResult.account;

View File

@ -1,8 +1,10 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom";
export const AuthErrorPage = (): JSX.Element => ( export const AuthErrorPage = (): JSX.Element => (
<div> <div>
<p></p> <p>login failed</p>
<br /> <br />
<Link to="/">return to TopPage</Link>
</div> </div>
); );

View File

@ -1,6 +1,6 @@
import { useMsal } from "@azure/msal-react"; import { useMsal } from "@azure/msal-react";
import { AppDispatch } from "app/store"; import { AppDispatch } from "app/store";
import { isIdToken } from "common/token"; import { isIdToken, isTokenExpired } from "common/token";
import { import {
clearToken, clearToken,
isAdminUser, isAdminUser,
@ -52,10 +52,10 @@ const LoginPage: React.FC = (): JSX.Element => {
instance.logoutRedirect({ instance.logoutRedirect({
postLogoutRedirectUri: "/AuthError", postLogoutRedirectUri: "/AuthError",
}); });
clearToken(); dispatch(clearToken());
}, [instance, navigate]); }, [instance, navigate, dispatch]);
const tokenSet = useCallback( const tokenSetAndNavigate = useCallback(
async (idToken: string) => { async (idToken: string) => {
// ログイン処理呼び出し // ログイン処理呼び出し
const { meta, payload } = await dispatch(loginAsync({ idToken })); const { meta, payload } = await dispatch(loginAsync({ idToken }));
@ -96,31 +96,32 @@ const LoginPage: React.FC = (): JSX.Element => {
useEffect(() => { useEffect(() => {
// idTokenStringがあるか⇒認証中 // idTokenStringがあるか⇒認証中
// accessTokenがある場合⇒ログイン済み // accessTokenがある場合⇒ログイン済みなのにブラウザバックでログイン画面に戻ってきた場合
// どちらもなければ直打ち // どちらもなければURL直打ち
(async () => { (async () => {
if (loadAccessToken()) { // ローカルストレージにidTokenがある場合は取得する
navigateToLoginedPage(); let idTokenString: string | null = null;
return; if (localStorageKeyforIdToken !== null) {
idTokenString = localStorage.getItem(localStorageKeyforIdToken);
} }
// AADB2Cのログイン画面とLoginPageを経由していない場合はトップページに遷移する // idTokenがない(=正常なログインプロセス中でない)場合は有効なアクセストークンを所持しているか確認し、
if (!localStorageKeyforIdToken) { // 有効であればログイン画面に遷移 or 無効であればトップページに遷移
navigate("/");
return;
}
const idTokenString = localStorage.getItem(localStorageKeyforIdToken);
if (idTokenString === null) { if (idTokenString === null) {
navigate("/"); const token = loadAccessToken();
// アクセストークンがない or 有効期限切れ場合はトップページに遷移
if (isTokenExpired(token)) {
navigate("/");
} else {
// 有効なアクセストークンがある場合はログイン画面に遷移
navigateToLoginedPage();
}
return; return;
} }
if (idTokenString) { const idTokenObject = JSON.parse(idTokenString);
const idTokenObject = JSON.parse(idTokenString); if (isIdToken(idTokenObject)) {
if (isIdToken(idTokenObject)) { await tokenSetAndNavigate(idTokenObject.secret);
await tokenSet(idTokenObject.secret);
}
} }
})(); })();
// 画面描画後のみ実行するため引数を設定しない // 画面描画後のみ実行するため引数を設定しない

View File

@ -40,9 +40,6 @@ export class User {
@Column({ default: true }) @Column({ default: true })
auto_renew: boolean; auto_renew: boolean;
@Column({ default: true })
license_alert: boolean;
@Column({ default: true }) @Column({ default: true })
notification: boolean; notification: boolean;

View File

@ -48,7 +48,6 @@ export const makeTestUser = async (
accepted_dpa_version: d?.accepted_dpa_version ?? "1.0", accepted_dpa_version: d?.accepted_dpa_version ?? "1.0",
email_verified: d?.email_verified ?? true, email_verified: d?.email_verified ?? true,
auto_renew: d?.auto_renew ?? true, auto_renew: d?.auto_renew ?? true,
license_alert: d?.license_alert ?? true,
notification: d?.notification ?? true, notification: d?.notification ?? true,
encryption: d?.encryption ?? true, encryption: d?.encryption ?? true,
encryption_password: d?.encryption_password, encryption_password: d?.encryption_password,
@ -117,7 +116,6 @@ export const makeTestAccount = async (
accepted_dpa_version: d?.accepted_dpa_version ?? "1.0", accepted_dpa_version: d?.accepted_dpa_version ?? "1.0",
email_verified: d?.email_verified ?? true, email_verified: d?.email_verified ?? true,
auto_renew: d?.auto_renew ?? true, auto_renew: d?.auto_renew ?? true,
license_alert: d?.license_alert ?? true,
notification: d?.notification ?? true, notification: d?.notification ?? true,
encryption: d?.encryption ?? true, encryption: d?.encryption ?? true,
encryption_password: d?.encryption_password ?? "password", encryption_password: d?.encryption_password ?? "password",