@@ -12,6 +12,7 @@ export const predictUsageCount = 'predictUsageCount' | |||||
export const windowCount = 'windowCount' | export const windowCount = 'windowCount' | ||||
import {useNavigate} from "react-router-dom"; | import {useNavigate} from "react-router-dom"; | ||||
import {useDispatch} from "react-redux"; | import {useDispatch} from "react-redux"; | ||||
import { REFRESH_TOKEN } from 'utils/ApiPathConst'; | |||||
// ** Handle User Login | // ** Handle User Login | ||||
export const handleLogin = data => { | export const handleLogin = data => { | ||||
@@ -104,6 +105,8 @@ export const SetupAxiosInterceptors = () => { | |||||
const navigate = useNavigate() | const navigate = useNavigate() | ||||
const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
//const updateLastRequestTime = useContext(TimerContext); | //const updateLastRequestTime = useContext(TimerContext); | ||||
let isRefreshToken= false; | |||||
axios.interceptors.request.use( | axios.interceptors.request.use( | ||||
config => { | config => { | ||||
// ** Get token from localStorage | // ** Get token from localStorage | ||||
@@ -126,39 +129,73 @@ export const SetupAxiosInterceptors = () => { | |||||
//updateLastRequestTime(Date.now()); | //updateLastRequestTime(Date.now()); | ||||
return response; | return response; | ||||
}, | }, | ||||
error => { | |||||
async (error) => { | |||||
// ** const { config, response: { status } } = error | // ** const { config, response: { status } } = error | ||||
const {response} = error | |||||
if (error.response.status === 401) { | |||||
if (localStorage.getItem("expiredAlertShown") === null) { | |||||
localStorage.setItem("expiredAlertShown", true) | |||||
alert("登入驗證已過期,請重新登入。") | |||||
if (error.response.status === 401 && error.config.url !== apiPath + REFRESH_TOKEN) { | |||||
// Make a request to refresh the access token | |||||
const refreshToken = localStorage.getItem('refreshToken'); | |||||
if (isRefreshToken) { | |||||
return; | |||||
} | } | ||||
} | |||||
// ** if (status === 401) { | |||||
if (response.status === 401) { | |||||
if (localStorage.getItem("expiredAlertShown") === null) { | |||||
localStorage.setItem("expiredAlertShown", true) | |||||
alert("登入驗證已過期,請重新登入。") | |||||
isRefreshToken = true; | |||||
return axios | |||||
.post(`${apiPath}${REFRESH_TOKEN}`, { | |||||
refreshToken: refreshToken // Replace with your refresh token | |||||
}) | |||||
.then((response) => { | |||||
if (response.status === 200) { | |||||
const newAccessToken = response.data.accessToken; | |||||
const newRefreshToken = response.data.refreshToken; | |||||
localStorage.setItem('accessToken', newAccessToken); | |||||
localStorage.setItem('refreshToken', newRefreshToken); | |||||
isRefreshToken = false; | |||||
window.location.reload(); | |||||
} | |||||
}) | |||||
.catch((refreshError) => { | |||||
dispatch(handleLogoutFunction()); | |||||
navigate('/login'); | |||||
isRefreshToken = false; | |||||
window.location.reload(); | |||||
throw refreshError; | |||||
}); | |||||
} else { | |||||
// if (error.response.status === 401) { | |||||
// await dispatch(handleLogoutFunction()); | |||||
// await navigate('/login'); | |||||
// await window.location.reload(); | |||||
// } | |||||
if (error.response.status === 401) { | |||||
if (localStorage.getItem("expiredAlertShown") === null) { | |||||
localStorage.setItem("expiredAlertShown", true) | |||||
alert("登入驗證已過期,請重新登入。") | |||||
} | |||||
} | } | ||||
} | |||||
if (response && response.status === 401) { | |||||
if (localStorage.getItem("expiredAlertShown") === null) { | |||||
localStorage.setItem("expiredAlertShown", true) | |||||
alert("登入驗證已過期,請重新登入。") | |||||
// ** if (status === 401) { | |||||
if (response.status === 401) { | |||||
if (localStorage.getItem("expiredAlertShown") === null) { | |||||
localStorage.setItem("expiredAlertShown", true) | |||||
alert("登入驗證已過期,請重新登入。") | |||||
} | |||||
} | |||||
if (response && response.status === 401) { | |||||
if (localStorage.getItem("expiredAlertShown") === null) { | |||||
localStorage.setItem("expiredAlertShown", true) | |||||
alert("登入驗證已過期,請重新登入。") | |||||
} | |||||
} | |||||
if (localStorage.getItem("expiredAlertShown")) { | |||||
dispatch(handleLogoutFunction()); | |||||
navigate('/login'); | |||||
} | } | ||||
} | |||||
if (localStorage.getItem("expiredAlertShown")) { | |||||
dispatch(handleLogoutFunction()); | |||||
navigate('/login'); | |||||
} | } | ||||
return Promise.reject(error) | return Promise.reject(error) | ||||
}, | |||||
} | |||||
) | ) | ||||
} | } |
@@ -1,17 +1,29 @@ | |||||
import React, { createContext, useState, useEffect } from 'react'; | import React, { createContext, useState, useEffect } from 'react'; | ||||
//import {useNavigate} from "react-router-dom"; | |||||
import {useNavigate} from "react-router-dom"; | |||||
//import axios from "axios"; | //import axios from "axios"; | ||||
import {getUserData} from "../auth/utils"; | import {getUserData} from "../auth/utils"; | ||||
import {isObjEmpty} from "../utils/Utils"; | import {isObjEmpty} from "../utils/Utils"; | ||||
import {useIdleTimer} from "react-idle-timer"; | import {useIdleTimer} from "react-idle-timer"; | ||||
import { handleLogoutFunction } from 'auth/index'; | |||||
import { useDispatch } from "react-redux"; | |||||
import { | |||||
isUserLoggedIn, | |||||
isGLDLoggedIn, | |||||
// isPrimaryLoggedIn, | |||||
// isCreditorLoggedIn, | |||||
// isINDLoggedIn, | |||||
// isORGLoggedIn, | |||||
// getUserId | |||||
} from "utils/Utils"; | |||||
const TimerContext = createContext(); | const TimerContext = createContext(); | ||||
const AutoLogoutProvider = ({ children }) => { | const AutoLogoutProvider = ({ children }) => { | ||||
const [lastRequestTime, setLastRequestTime] = useState(Date.now()); | const [lastRequestTime, setLastRequestTime] = useState(Date.now()); | ||||
//const navigate = useNavigate(); | |||||
const [logoutInterval /*, setLogoutInterval*/] = useState(1); | |||||
const navigate = useNavigate(); | |||||
const [logoutInterval, setLogoutInterval] = useState(1); | |||||
const [state, setState] = useState('Active'); | const [state, setState] = useState('Active'); | ||||
const dispatch = useDispatch() | |||||
const onIdle = () => { | const onIdle = () => { | ||||
setLastRequestTime(Date.now()); | setLastRequestTime(Date.now()); | ||||
@@ -30,7 +42,7 @@ const AutoLogoutProvider = ({ children }) => { | |||||
} = useIdleTimer({ | } = useIdleTimer({ | ||||
onIdle, | onIdle, | ||||
onActive, | onActive, | ||||
timeout: 10_000, | |||||
timeout: 1_000, | |||||
throttle: 500, | throttle: 500, | ||||
crossTab: true, | crossTab: true, | ||||
syncTimers: 200, | syncTimers: 200, | ||||
@@ -43,19 +55,23 @@ const AutoLogoutProvider = ({ children }) => { | |||||
const userData = getUserData(); | const userData = getUserData(); | ||||
if(!isObjEmpty(userData)){ | if(!isObjEmpty(userData)){ | ||||
//TODO: get auto logout time here | //TODO: get auto logout time here | ||||
if(isGLDLoggedIn()){ | |||||
setLogoutInterval(240); | |||||
}else{ | |||||
setLogoutInterval(1); | |||||
} | |||||
// axios.get(`${apiPath}${GET_IDLE_LOGOUT_TIME}`, | // axios.get(`${apiPath}${GET_IDLE_LOGOUT_TIME}`, | ||||
// ) | // ) | ||||
// .then((response) => { | |||||
// if (response.status === 200) { | |||||
// setLastRequestTime(Date.now()); | |||||
// setLogoutInterval(parseInt(response.data.data)); | |||||
// } | |||||
// }) | |||||
// .catch(error => { | |||||
// console.log(error); | |||||
// return false; | |||||
// }); | |||||
// .then((response) => { | |||||
// if (response.status === 200) { | |||||
// setLastRequestTime(Date.now()); | |||||
// setLogoutInterval(parseInt(response.data.data)); | |||||
// } | |||||
// }) | |||||
// .catch(error => { | |||||
// console.log(error); | |||||
// return false; | |||||
// }); | |||||
} | } | ||||
else{ | else{ | ||||
//navigate('/login'); | //navigate('/login'); | ||||
@@ -68,13 +84,14 @@ const AutoLogoutProvider = ({ children }) => { | |||||
getRemainingTime(); | getRemainingTime(); | ||||
if(state !== "Active" && lastActiveTab){ | if(state !== "Active" && lastActiveTab){ | ||||
const timeElapsed = currentTime - lastRequestTime; | const timeElapsed = currentTime - lastRequestTime; | ||||
// console.log(timeElapsed); | |||||
// console.log(parseInt(timeElapsed/1000)); | |||||
// console.log(logoutInterval* 60); | |||||
if (timeElapsed >= logoutInterval * 60 * 1000) { | if (timeElapsed >= logoutInterval * 60 * 1000) { | ||||
//TODO: auto logout here | |||||
// console.log("logout"); | |||||
//await dispatch(handleLogoutFunction()); | |||||
//await navigate('/login'); | |||||
//await window.location.reload(); | |||||
if(isUserLoggedIn()){ | |||||
alert("登入驗證已過期,請重新登入。") | |||||
dispatch(handleLogoutFunction()); | |||||
navigate('/login'); | |||||
} | |||||
} | } | ||||
} | } | ||||
else if(state === "Active"){ | else if(state === "Active"){ | ||||
@@ -0,0 +1,72 @@ | |||||
import { createContext, useEffect, useRef, useCallback } from 'react'; | |||||
import { apiPath } from 'auth/utils'; | |||||
import { REFRESH_TOKEN } from 'utils/ApiPathConst'; | |||||
import axios from 'axios'; | |||||
const RefreshTokenContext = createContext(); | |||||
const RefreshTokenProvider = ({ children }) => { | |||||
const token = useRef(localStorage.getItem('accessToken')); | |||||
const isRefresh = useRef(false); | |||||
// handle Refresh Token Logic | |||||
const handleRefreshToken = useCallback(() => { | |||||
if (!isRefresh.current) { | |||||
const refreshToken = localStorage.getItem('refreshToken'); | |||||
isRefresh.current = true; | |||||
axios | |||||
.post(`${apiPath}${REFRESH_TOKEN}`, { | |||||
refreshToken: refreshToken | |||||
}) | |||||
.then((response) => { | |||||
if (response.status === 200) { | |||||
const newAccessToken = response.data.accessToken; | |||||
const newRefreshToken = response.data.refreshToken; | |||||
localStorage.setItem('accessToken', newAccessToken); | |||||
localStorage.setItem('refreshToken', newRefreshToken); | |||||
token.current = newAccessToken; | |||||
isRefresh.current = false; | |||||
} else { | |||||
token.current = null; | |||||
isRefresh.current = false; | |||||
} | |||||
}) | |||||
.catch((refreshError) => { | |||||
console.log('Failed to refresh token'); | |||||
console.log(refreshError) | |||||
token.current = null | |||||
isRefresh.current = false; | |||||
}); | |||||
} | |||||
}, []); | |||||
// Function to check token expiry | |||||
const checkTokenExpiry = useCallback(() => { | |||||
// Check if token is present and its expiry time | |||||
if (token.current) { | |||||
const tokenExp = JSON.parse(atob(token.current.split('.')[1])).exp; | |||||
const currentTime = Math.floor(Date.now() / 1000); | |||||
const expiryTime = tokenExp - 30; // Refresh 30 seconds before expiry | |||||
// console.log("check refresh Token"); | |||||
// console.log(currentTime); | |||||
// console.log(new Date(currentTime*1000).toLocaleString()); | |||||
// console.log(expiryTime); | |||||
// console.log(new Date(expiryTime*1000).toLocaleString()); | |||||
// console.log('accessToken: ' + localStorage.getItem('accessToken')); | |||||
// console.log('refreshToken: ' + localStorage.getItem('refreshToken')); | |||||
if (currentTime >= expiryTime) { | |||||
handleRefreshToken(); | |||||
} | |||||
} | |||||
}, [token]); | |||||
// Start the timer on component mount | |||||
useEffect(() => { | |||||
const timer = setInterval(checkTokenExpiry, 10000); // Check every 10 second | |||||
return () => clearInterval(timer); // Cleanup timer on unmount | |||||
}, [checkTokenExpiry]); | |||||
return <RefreshTokenContext.Provider value={{}}>{children}</RefreshTokenContext.Provider>; | |||||
}; | |||||
export { RefreshTokenContext, RefreshTokenProvider }; |
@@ -18,6 +18,7 @@ import { store } from 'store'; | |||||
import reportWebVitals from './reportWebVitals'; | import reportWebVitals from './reportWebVitals'; | ||||
import {I18nProvider} from "./components/I18nProvider"; | import {I18nProvider} from "./components/I18nProvider"; | ||||
import {AutoLogoutProvider} from "./components/AutoLogoutProvider"; | import {AutoLogoutProvider} from "./components/AutoLogoutProvider"; | ||||
import {RefreshTokenProvider} from "./components/RefreshTokenProvider"; | |||||
// ==============================|| MAIN - REACT DOM RENDER ||============================== // | // ==============================|| MAIN - REACT DOM RENDER ||============================== // | ||||
@@ -29,11 +30,13 @@ root.render( | |||||
<StrictMode> | <StrictMode> | ||||
<ReduxProvider store={store}> | <ReduxProvider store={store}> | ||||
<I18nProvider> | <I18nProvider> | ||||
<BrowserRouter basename="/"> | |||||
<AutoLogoutProvider> | |||||
<App /> | |||||
</AutoLogoutProvider> | |||||
</BrowserRouter> | |||||
<BrowserRouter basename="/"> | |||||
<RefreshTokenProvider> | |||||
<AutoLogoutProvider> | |||||
<App /> | |||||
</AutoLogoutProvider> | |||||
</RefreshTokenProvider> | |||||
</BrowserRouter> | |||||
</I18nProvider> | </I18nProvider> | ||||
</ReduxProvider> | </ReduxProvider> | ||||
</StrictMode> | </StrictMode> | ||||
@@ -1,6 +1,8 @@ | |||||
import {apiPath} from "../auth/utils"; | import {apiPath} from "../auth/utils"; | ||||
// GET request | // GET request | ||||
export const REFRESH_TOKEN = "/refresh-token" | |||||
export const CHANGE_PASSWORD_PATH = "/user/change-password" | |||||
//Group Config | //Group Config | ||||
export const GET_GROUP_LIST_PATH = '/group'; | export const GET_GROUP_LIST_PATH = '/group'; | ||||