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'
}
}