From 0722e690401f3c9b53c3be8925c5da09bfcc8e38 Mon Sep 17 00:00:00 2001 From: Alex Cheung Date: Wed, 24 Jan 2024 16:19:59 +0800 Subject: [PATCH] update token with gld user within 240min and non gld user within 60 --- src/auth/index.js | 89 ++++++++++++++++++-------- src/components/AutoLogoutProvider.js | 59 +++++++++++------ src/components/RefreshTokenProvider.js | 72 +++++++++++++++++++++ src/index.js | 13 ++-- src/utils/ApiPathConst.js | 2 + 5 files changed, 183 insertions(+), 52 deletions(-) create mode 100644 src/components/RefreshTokenProvider.js diff --git a/src/auth/index.js b/src/auth/index.js index e92350c..7392f0c 100644 --- a/src/auth/index.js +++ b/src/auth/index.js @@ -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) - }, + } ) } diff --git a/src/components/AutoLogoutProvider.js b/src/components/AutoLogoutProvider.js index 8387458..fb9128c 100644 --- a/src/components/AutoLogoutProvider.js +++ b/src/components/AutoLogoutProvider.js @@ -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"){ diff --git a/src/components/RefreshTokenProvider.js b/src/components/RefreshTokenProvider.js new file mode 100644 index 0000000..0978d79 --- /dev/null +++ b/src/components/RefreshTokenProvider.js @@ -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 {children}; +}; + +export { RefreshTokenContext, RefreshTokenProvider }; diff --git a/src/index.js b/src/index.js index 2c85d6f..c58b3ca 100644 --- a/src/index.js +++ b/src/index.js @@ -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( - - - - - + + + + + + + diff --git a/src/utils/ApiPathConst.js b/src/utils/ApiPathConst.js index 76f0519..5b6c13d 100644 --- a/src/utils/ApiPathConst.js +++ b/src/utils/ApiPathConst.js @@ -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';