From 4ace78872eeeae7b1e03d367591c8e2ff34ac1ee Mon Sep 17 00:00:00 2001 From: CHEYNE Date: Thu, 29 Feb 2024 14:16:54 +0800 Subject: [PATCH] [#2099] fix(web): redirect to login page if token has expired (#2312) ### What changes were proposed in this pull request? Remove refresh token and redirect to login page if token has expired. ### Why are the changes needed? Fix: #2099 ### Does this PR introduce _any_ user-facing change? N/A ### How was this patch tested? N/A --- web/app/ui/login/page.js | 3 ++- web/lib/api/auth/index.js | 13 +++++++------ web/lib/layout/Logout.js | 15 +++++++++++---- web/lib/provider/session.js | 30 +++++------------------------- web/lib/store/auth/index.js | 22 +++++++++++++++++----- web/lib/utils/axios/checkStatus.ts | 6 +++--- web/lib/utils/axios/index.ts | 12 ++++++------ 7 files changed, 51 insertions(+), 50 deletions(-) diff --git a/web/app/ui/login/page.js b/web/app/ui/login/page.js index fcf9c74776d..7ba6510ff43 100644 --- a/web/app/ui/login/page.js +++ b/web/app/ui/login/page.js @@ -48,7 +48,8 @@ const LoginPage = () => { const onSubmit = async data => { dispatch(loginAction({ params: data, router })) - reset() + + reset({ ...data }) } const onError = errors => { diff --git a/web/lib/api/auth/index.js b/web/lib/api/auth/index.js index 6fdb04ce572..30ba1de9d54 100644 --- a/web/lib/api/auth/index.js +++ b/web/lib/api/auth/index.js @@ -3,7 +3,6 @@ * This software is licensed under the Apache License version 2. */ -import axios from 'axios' import { defHttp } from '@/lib/utils/axios' export const getAuthConfigsApi = () => { @@ -13,9 +12,11 @@ export const getAuthConfigsApi = () => { } export const loginApi = (url, params) => { - return axios({ - url, - method: 'post', - params - }) + return defHttp.post( + { + url, + params + }, + { withToken: false } + ) } diff --git a/web/lib/layout/Logout.js b/web/lib/layout/Logout.js index eeb00460ccb..3f63e4d5102 100644 --- a/web/lib/layout/Logout.js +++ b/web/lib/layout/Logout.js @@ -5,20 +5,27 @@ 'use client' +import { useRouter } from 'next/navigation' + import { Box, IconButton } from '@mui/material' import Icon from '@/components/Icon' -import { useAuth } from '../provider/session' -import { useAppSelector } from '../hooks/useStore' +import { useAppDispatch, useAppSelector } from '../hooks/useStore' +import { logoutAction } from '../store/auth' const LogoutButton = () => { - const auth = useAuth() + const router = useRouter() + const dispatch = useAppDispatch() const authStore = useAppSelector(state => state.auth) + const handleLogout = () => { + dispatch(logoutAction({ router })) + } + return ( {authStore.authToken ? ( - auth.logout()}> + handleLogout()}> ) : null} diff --git a/web/lib/provider/session.js b/web/lib/provider/session.js index 5fe559f934b..a00986ade25 100644 --- a/web/lib/provider/session.js +++ b/web/lib/provider/session.js @@ -9,18 +9,16 @@ import { createContext, useEffect, useState, useContext } from 'react' import { useRouter } from 'next/navigation' -import { useAppDispatch, useAppSelector } from '@/lib/hooks/useStore' -import { initialVersion, setVersion as setStoreVersion } from '@/lib/store/sys' +import { useAppDispatch } from '@/lib/hooks/useStore' +import { initialVersion } from '@/lib/store/sys' import { to } from '../utils' -import { getAuthConfigs, setAuthToken, setIntervalId, loginAction } from '../store/auth' +import { getAuthConfigs, setAuthToken } from '../store/auth' const authProvider = { version: '', loading: true, - setLoading: () => Boolean, - login: () => Promise.resolve(), - logout: () => Promise.resolve() + setLoading: () => Boolean } const AuthContext = createContext(authProvider) @@ -34,23 +32,8 @@ const AuthProvider = ({ children }) => { const token = (typeof window !== 'undefined' && localStorage.getItem('accessToken')) || null const version = (typeof window !== 'undefined' && localStorage.getItem('version')) || null - const authStore = useAppSelector(state => state.auth) const dispatch = useAppDispatch() - const handleLogin = async params => { - dispatch(loginAction({ params, router })) - } - - const handleLogout = () => { - localStorage.removeItem('version') - localStorage.removeItem('accessToken') - localStorage.removeItem('authParams') - - dispatch(setStoreVersion('')) - dispatch(setAuthToken('')) - router.push('/ui/login') - } - useEffect(() => { const initAuth = async () => { const [authConfigsErr, resAuthConfigs] = await to(dispatch(getAuthConfigs())) @@ -60,7 +43,6 @@ const AuthProvider = ({ children }) => { dispatch(initialVersion()) } else if (authType === 'oauth') { if (token) { - dispatch(setIntervalId()) dispatch(setAuthToken(token)) dispatch(initialVersion()) } else { @@ -76,9 +58,7 @@ const AuthProvider = ({ children }) => { const values = { version, token, - loading, - login: handleLogin, - logout: handleLogout + loading } return {children} diff --git a/web/lib/store/auth/index.js b/web/lib/store/auth/index.js index 34e6cbbcdfe..d6f5f7acd29 100644 --- a/web/lib/store/auth/index.js +++ b/web/lib/store/auth/index.js @@ -19,12 +19,15 @@ export const getAuthConfigs = createAsyncThunk('auth/getAuthConfigs', async () = let authType = null const [err, res] = await to(getAuthConfigsApi()) - if (!err && res) { - oauthUrl = `${res['gravitino.authenticator.oauth.serverUri']}${res['gravitino.authenticator.oauth.tokenPath']}` - - authType = res['gravitino.authenticator'] + if (err || !res) { + throw new Error(err) } + oauthUrl = `${res['gravitino.authenticator.oauth.serverUri']}${res['gravitino.authenticator.oauth.tokenPath']}` + authType = res['gravitino.authenticator'] + + localStorage.setItem('oauthUrl', oauthUrl) + return { oauthUrl, authType } }) @@ -58,7 +61,7 @@ export const loginAction = createAsyncThunk('auth/loginAction', async ({ params, throw new Error(err) } - const { access_token, expires_in } = res?.data + const { access_token, expires_in } = res localStorage.setItem('accessToken', access_token) localStorage.setItem('expiredIn', expires_in) @@ -72,6 +75,15 @@ export const loginAction = createAsyncThunk('auth/loginAction', async ({ params, return { token: access_token, expired: expires_in } }) +export const logoutAction = createAsyncThunk('auth/logoutAction', async ({ router }, { getState, dispatch }) => { + localStorage.removeItem('accessToken') + localStorage.removeItem('authParams') + dispatch(setAuthToken('')) + await router.push('/ui/login') + + return { token: null } +}) + export const setIntervalId = createAsyncThunk('auth/setIntervalId', async (expiredIn, { dispatch }) => { const localExpiredIn = localStorage.getItem('expiredIn') diff --git a/web/lib/utils/axios/checkStatus.ts b/web/lib/utils/axios/checkStatus.ts index afc1715506d..084987392d7 100644 --- a/web/lib/utils/axios/checkStatus.ts +++ b/web/lib/utils/axios/checkStatus.ts @@ -28,7 +28,6 @@ SOFTWARE. import type { ErrorMessageMode } from '@/types/axios' import toast from 'react-hot-toast' -import { useRouter as Router } from 'next/navigation' export function checkStatus(status: number, msg: string, errorMessageMode: ErrorMessageMode = 'message'): void { let errMessage = '' @@ -39,12 +38,13 @@ export function checkStatus(status: number, msg: string, errorMessageMode: Error break case 401: + // ** reserve error message errMessage = msg || 'The user does not have permission (token, user name, password error or expired)!' localStorage.removeItem('accessToken') - localStorage.removeItem('version') + localStorage.removeItem('authParams') - Router().push('/ui/login') + window.location.href = '/ui/login' break case 403: diff --git a/web/lib/utils/axios/index.ts b/web/lib/utils/axios/index.ts index 3fd432c8e76..410a339c450 100644 --- a/web/lib/utils/axios/index.ts +++ b/web/lib/utils/axios/index.ts @@ -146,8 +146,8 @@ const transform: AxiosTransform = { config.params = params } else { // ** If no data is provided for non-GET requests, the params will be treated as data - config.data = params - config.params = undefined + // config.data = params + // config.params = undefined } if (joinParamsToUrl) { config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data)) @@ -190,8 +190,8 @@ const transform: AxiosTransform = { * @description: Error Response Handling */ responseInterceptorsCatch: (axiosInstance: AxiosInstance, error: any) => { - const { response, code, message, config } = error || {} - const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none' + const { response, code, message, config: originConfig } = error || {} + const errorMessageMode = originConfig?.requestOptions?.errorMessageMode || 'none' const msg: string = response?.data?.error?.message ?? response?.data?.message ?? '' const err: string = error?.toString?.() ?? '' let errMessage = '' @@ -224,8 +224,8 @@ const transform: AxiosTransform = { checkStatus(error?.response?.status, msg, errorMessageMode) const retryRequest = new AxiosRetry() - const { isOpenRetry } = config.requestOptions.retryRequest - config.method?.toUpperCase() === RequestEnum.GET && isOpenRetry && retryRequest.retry(axiosInstance, error) + const { isOpenRetry } = originConfig.requestOptions.retryRequest + originConfig.method?.toUpperCase() === RequestEnum.GET && isOpenRetry && retryRequest.retry(axiosInstance, error) return Promise.reject(error) }