This is an automated email from the ASF dual-hosted git repository.

jshao pushed a commit to branch branch-0.9
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-0.9 by this push:
     new 8a8a2062b8 [#7426] fix(web): fix refresh by oauth mode (#7453)
8a8a2062b8 is described below

commit 8a8a2062b8b99248dede8c8f3f9733c84a824e35
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Jun 23 13:54:57 2025 +0800

    [#7426] fix(web): fix refresh by oauth mode (#7453)
    
    ### What changes were proposed in this pull request?
    The Gravitino web UI was refreshes constantly when using OAuth
    authentication
    
    ### Why are the changes needed?
    
    
    Fix: #7426
    
    ### Does this PR introduce _any_ user-facing change?
    N/A
    
    ### How was this patch tested?
    manually test
    
    Co-authored-by: Qian Xia <[email protected]>
---
 web/web/src/app/login/page.js        | 16 ++++++++---
 web/web/src/lib/api/github/index.js  |  2 +-
 web/web/src/lib/store/auth/index.js  | 32 ++++++++++++++-------
 web/web/src/lib/utils/axios/index.js | 56 ++++++------------------------------
 4 files changed, 44 insertions(+), 62 deletions(-)

diff --git a/web/web/src/app/login/page.js b/web/web/src/app/login/page.js
index cc7e225e18..2dca5f0514 100644
--- a/web/web/src/app/login/page.js
+++ b/web/web/src/app/login/page.js
@@ -22,7 +22,7 @@
 import { useRouter } from 'next/navigation'
 import Image from 'next/image'
 import { Roboto } from 'next/font/google'
-
+import { useEffect } from 'react'
 import { Box, Card, Grid, Button, CardContent, Typography, TextField, 
FormControl, FormHelperText } from '@mui/material'
 
 import clsx from 'clsx'
@@ -30,8 +30,8 @@ import * as yup from 'yup'
 import { useForm, Controller } from 'react-hook-form'
 import { yupResolver } from '@hookform/resolvers/yup'
 
-import { useAppDispatch } from '@/lib/hooks/useStore'
-import { loginAction } from '@/lib/store/auth'
+import { useAppDispatch, useAppSelector } from '@/lib/hooks/useStore'
+import { loginAction, setIntervalIdAction, clearIntervalId } from 
'@/lib/store/auth'
 
 const fonts = Roboto({ subsets: ['latin'], weight: ['400'], display: 'swap' })
 
@@ -52,6 +52,7 @@ const schema = yup.object().shape({
 const LoginPage = () => {
   const router = useRouter()
   const dispatch = useAppDispatch()
+  const store = useAppSelector(state => state.auth)
 
   const {
     control,
@@ -64,8 +65,15 @@ const LoginPage = () => {
     resolver: yupResolver(schema)
   })
 
+  useEffect(() => {
+    if (store.intervalId) {
+      clearIntervalId()
+    }
+  }, [store.intervalId])
+
   const onSubmit = async data => {
-    dispatch(loginAction({ params: data, router }))
+    await dispatch(loginAction({ params: data, router }))
+    await dispatch(setIntervalIdAction())
 
     reset({ ...data })
   }
diff --git a/web/web/src/lib/api/github/index.js 
b/web/web/src/lib/api/github/index.js
index 456316221d..9a981519a4 100644
--- a/web/web/src/lib/api/github/index.js
+++ b/web/web/src/lib/api/github/index.js
@@ -19,7 +19,7 @@
 
 import { defHttp } from '@/lib/utils/axios'
 
-const githubApis = {
+export const githubApis = {
   GET: 'https://api.github.com/repos/apache/gravitino'
 }
 
diff --git a/web/web/src/lib/store/auth/index.js 
b/web/web/src/lib/store/auth/index.js
index ec0f1f5212..2f50480a2d 100644
--- a/web/web/src/lib/store/auth/index.js
+++ b/web/web/src/lib/store/auth/index.js
@@ -84,9 +84,7 @@ export const loginAction = 
createAsyncThunk('auth/loginAction', async ({ params,
   localStorage.setItem('isIdle', false)
   dispatch(setAuthToken(access_token))
   dispatch(setExpiredIn(expires_in))
-
   await dispatch(initialVersion())
-
   router.push('/metalakes')
 
   return { token: access_token, expired: expires_in }
@@ -95,23 +93,32 @@ export const loginAction = 
createAsyncThunk('auth/loginAction', async ({ params,
 export const logoutAction = createAsyncThunk('auth/logoutAction', async ({ 
router }, { getState, dispatch }) => {
   localStorage.removeItem('accessToken')
   localStorage.removeItem('authParams')
+  dispatch(clearIntervalId())
   dispatch(setAuthToken(''))
   await router.push('/login')
 
   return { token: null }
 })
 
-export const setIntervalId = createAsyncThunk('auth/setIntervalId', async 
(expiredIn, { dispatch }) => {
+export const setIntervalIdAction = 
createAsyncThunk('auth/setIntervalIdAction', async (expiredIn, { dispatch }) => 
{
   const localExpiredIn = localStorage.getItem('expiredIn')
-
-  // ** the expired time obtained from the backend is in seconds, default 
value is 299 seconds
   const expired = (expiredIn ?? Number(localExpiredIn)) * (2 / 3) * 1000
   const defaultExpired = 299 * (2 / 3) * 1000
 
   let intervalId = setInterval(() => {
+    if (localStorage.getItem('isIdle') === 'true') {
+      localStorage.removeItem('accessToken')
+      localStorage.removeItem('authParams')
+      dispatch(clearIntervalId())
+      dispatch(setAuthToken(''))
+
+      return
+    }
     dispatch(refreshToken())
   }, expired || defaultExpired)
 
+  dispatch(setIntervalId(intervalId))
+
   return {
     intervalId
   }
@@ -129,11 +136,13 @@ export const authSlice = createSlice({
   },
   reducers: {
     setIntervalId(state, action) {
-      state.intervalId = this.setIntervalId()
+      state.intervalId = action.payload
     },
-    clearIntervalId(state, action) {
-      clearInterval(state.intervalId)
-      state.intervalId = null
+    clearIntervalId(state) {
+      if (state.intervalId) {
+        clearInterval(state.intervalId)
+        state.intervalId = null
+      }
     },
     setAuthToken(state, action) {
       state.authToken = action.payload
@@ -151,12 +160,15 @@ export const authSlice = createSlice({
       state.authType = action.payload.authType
     })
     builder.addCase(refreshToken.fulfilled, (state, action) => {
+      localStorage.setItem('accessToken', action.payload.token)
+      localStorage.setItem('expiredIn', action.payload.expiredIn)
+      localStorage.setItem('isIdle', false)
       state.authToken = action.payload.token
       state.expiredIn = action.payload.expiredIn
     })
   }
 })
 
-export const { setAuthToken, setAuthParams, setExpiredIn } = authSlice.actions
+export const { setAuthToken, setAuthParams, setExpiredIn, clearIntervalId } = 
authSlice.actions
 
 export default authSlice.reducer
diff --git a/web/web/src/lib/utils/axios/index.js 
b/web/web/src/lib/utils/axios/index.js
index 5bd2350bbc..3980f4d825 100644
--- a/web/web/src/lib/utils/axios/index.js
+++ b/web/web/src/lib/utils/axios/index.js
@@ -38,23 +38,8 @@ import { AxiosCanceler } from './axiosCancel'
 import { NextAxios } from './Axios'
 import { checkStatus } from './checkStatus'
 import { useAuth as Auth } from '../../provider/session'
-
-let isRefreshing = false
-
-const refreshToken = async () => {
-  const url = localStorage.getItem('oauthUrl')
-  const params = localStorage.getItem('authParams')
-
-  const res = await defHttp.post({ url: 
`${url}?${qs.stringify(JSON.parse(params))}` }, { withToken: false })
-
-  return res
-}
-
-const resetToLoginState = () => {
-  localStorage.removeItem('accessToken')
-  localStorage.removeItem('authParams')
-  window.location.href = '/login'
-}
+import { githubApis } from '@/lib/api/github'
+import { isProdEnv } from '@/lib/utils'
 
 /**
  * @description: Data processing to facilitate the distinction of multiple 
processing methods
@@ -246,37 +231,14 @@ const transform = {
 
     checkStatus(error?.response?.status, msg, errorMessageMode)
 
-    if (response?.status === 401 && !originConfig._retry) {
-      // Log out directly if idle for more than 30 minutes
-      const isIdle = localStorage.getItem('isIdle') && 
JSON.parse(localStorage.getItem('isIdle'))
-      if (isIdle) {
-        console.error('User is idle')
-        resetToLoginState()
-      }
+    if (response?.status === 401 && !originConfig._retry && 
response.config.url !== githubApis.GET) {
+      localStorage.removeItem('accessToken')
+      localStorage.removeItem('authParams')
 
-      originConfig._retry = true
-
-      if (!isRefreshing) {
-        isRefreshing = true
-
-        try {
-          refreshToken()
-            .then(res => {
-              const { access_token } = res
-              localStorage.setItem('accessToken', access_token)
-
-              return defHttp.request(originConfig)
-            })
-            .catch(err => {
-              console.error('refreshToken error =>', err)
-              resetToLoginState()
-            })
-        } catch (err) {
-          console.error(err)
-        } finally {
-          isRefreshing = false
-          location.reload()
-        }
+      if (isProdEnv) {
+        window.location.href = '/ui/login'
+      } else {
+        window.location.href = '/login'
       }
     }
 

Reply via email to