@@ -12,6 +12,7 @@ export const predictUsageCount = 'predictUsageCount' | |||
export const windowCount = 'windowCount' | |||
import {useNavigate} from "react-router-dom"; | |||
import {useDispatch} from "react-redux"; | |||
import { REFRESH_TOKEN } from 'utils/ApiPathConst'; | |||
// ** Handle User Login | |||
export const handleLogin = data => { | |||
@@ -104,6 +105,8 @@ export const SetupAxiosInterceptors = () => { | |||
const navigate = useNavigate() | |||
const dispatch = useDispatch(); | |||
//const updateLastRequestTime = useContext(TimerContext); | |||
let isRefreshToken= false; | |||
axios.interceptors.request.use( | |||
config => { | |||
// ** Get token from localStorage | |||
@@ -126,39 +129,73 @@ export const SetupAxiosInterceptors = () => { | |||
//updateLastRequestTime(Date.now()); | |||
return response; | |||
}, | |||
error => { | |||
async (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) | |||
}, | |||
} | |||
) | |||
} |
@@ -1,17 +1,29 @@ | |||
import React, { createContext, useState, useEffect } from 'react'; | |||
//import {useNavigate} from "react-router-dom"; | |||
import {useNavigate} from "react-router-dom"; | |||
//import axios from "axios"; | |||
import {getUserData} from "../auth/utils"; | |||
import {isObjEmpty} from "../utils/Utils"; | |||
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 AutoLogoutProvider = ({ children }) => { | |||
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 dispatch = useDispatch() | |||
const onIdle = () => { | |||
setLastRequestTime(Date.now()); | |||
@@ -30,7 +42,7 @@ const AutoLogoutProvider = ({ children }) => { | |||
} = useIdleTimer({ | |||
onIdle, | |||
onActive, | |||
timeout: 10_000, | |||
timeout: 1_000, | |||
throttle: 500, | |||
crossTab: true, | |||
syncTimers: 200, | |||
@@ -43,19 +55,23 @@ const AutoLogoutProvider = ({ children }) => { | |||
const userData = getUserData(); | |||
if(!isObjEmpty(userData)){ | |||
//TODO: get auto logout time here | |||
if(isGLDLoggedIn()){ | |||
setLogoutInterval(240); | |||
}else{ | |||
setLogoutInterval(1); | |||
} | |||
// 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{ | |||
//navigate('/login'); | |||
@@ -68,13 +84,14 @@ const AutoLogoutProvider = ({ children }) => { | |||
getRemainingTime(); | |||
if(state !== "Active" && lastActiveTab){ | |||
const timeElapsed = currentTime - lastRequestTime; | |||
// console.log(timeElapsed); | |||
// console.log(parseInt(timeElapsed/1000)); | |||
// console.log(logoutInterval* 60); | |||
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"){ | |||
@@ -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 {I18nProvider} from "./components/I18nProvider"; | |||
import {AutoLogoutProvider} from "./components/AutoLogoutProvider"; | |||
import {RefreshTokenProvider} from "./components/RefreshTokenProvider"; | |||
// ==============================|| MAIN - REACT DOM RENDER ||============================== // | |||
@@ -29,11 +30,13 @@ root.render( | |||
<StrictMode> | |||
<ReduxProvider store={store}> | |||
<I18nProvider> | |||
<BrowserRouter basename="/"> | |||
<AutoLogoutProvider> | |||
<App /> | |||
</AutoLogoutProvider> | |||
</BrowserRouter> | |||
<BrowserRouter basename="/"> | |||
<RefreshTokenProvider> | |||
<AutoLogoutProvider> | |||
<App /> | |||
</AutoLogoutProvider> | |||
</RefreshTokenProvider> | |||
</BrowserRouter> | |||
</I18nProvider> | |||
</ReduxProvider> | |||
</StrictMode> | |||
@@ -1,6 +1,8 @@ | |||
import {apiPath} from "../auth/utils"; | |||
// GET request | |||
export const REFRESH_TOKEN = "/refresh-token" | |||
export const CHANGE_PASSWORD_PATH = "/user/change-password" | |||
//Group Config | |||
export const GET_GROUP_LIST_PATH = '/group'; | |||