diff --git a/public/silent-refresh.html b/public/silent-refresh.html
deleted file mode 100644
index 852cc138..00000000
--- a/public/silent-refresh.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/src/features/AuthStore/config.tsx b/src/features/AuthStore/config.tsx
new file mode 100644
index 00000000..61072f62
--- /dev/null
+++ b/src/features/AuthStore/config.tsx
@@ -0,0 +1,7 @@
+import { UserManager } from 'oidc-client'
+
+import { getClientSettings } from './helpers'
+
+export const userManager = new UserManager(getClientSettings())
+
+export const channel = new BroadcastChannel('access_token')
diff --git a/src/features/AuthStore/helpers.tsx b/src/features/AuthStore/helpers.tsx
index 50818c4f..e938180b 100644
--- a/src/features/AuthStore/helpers.tsx
+++ b/src/features/AuthStore/helpers.tsx
@@ -61,7 +61,6 @@ const redirectUrl = () => {
export const getClientSettings = (): Settings => ({
authority: AUTH_SERVICE,
- automaticSilentRenew: true,
client_id: client.auth.clientId,
filterProtocolClaims: false,
loadUserInfo: false,
@@ -70,7 +69,6 @@ export const getClientSettings = (): Settings => ({
response_mode: 'query',
response_type: 'id_token token',
scope: 'openid',
- silent_redirect_uri: `${window.location.origin ?? window.origin}/silent-refresh.html`,
userStore: new WebStorageStateStore({ store: window.localStorage }),
})
diff --git a/src/features/AuthStore/hooks/useAuth.tsx b/src/features/AuthStore/hooks/useAuth.tsx
index 9025b74e..92f994c7 100644
--- a/src/features/AuthStore/hooks/useAuth.tsx
+++ b/src/features/AuthStore/hooks/useAuth.tsx
@@ -7,7 +7,6 @@ import {
import { useHistory } from 'react-router'
import type { User } from 'oidc-client'
-import { UserManager } from 'oidc-client'
import isString from 'lodash/isString'
import isBoolean from 'lodash/isBoolean'
@@ -16,34 +15,33 @@ import { PAGES } from 'config'
import {
addLanguageUrlParam,
-} from 'helpers/languageUrlParam'
-
-import {
writeToken,
removeToken,
readToken,
-} from 'helpers/token'
-import {
setCookie,
removeCookie,
-} from 'helpers/cookie'
-import { isMatchPage } from 'helpers/isMatchPage'
+ isMatchPage,
+} from 'helpers'
import {
useLocalStore,
useSessionStore,
useToggle,
+ useEventListener,
} from 'hooks'
import { useLexicsStore } from 'features/LexicsStore'
import { queryParamStorage } from 'features/QueryParamsStorage'
-import { getUserInfo, UserInfo } from 'requests/getUserInfo'
-import { checkDevice, FailedResponse } from 'requests/checkDevice'
-import { getTokenVirtualUser } from 'requests/getTokenVirtualUser'
+import type { UserInfo, FailedResponse } from 'requests'
+import {
+ getUserInfo,
+ checkDevice,
+ getTokenVirtualUser,
+} from 'requests'
-// eslint-disable-next-line
-import { getClientSettings, needCheckNewDeviсe } from '../helpers'
+import { userManager, channel } from '../config'
+import { needCheckNewDeviсe } from '../helpers'
export const useAuth = () => {
const { changeLang, lang } = useLexicsStore()
@@ -55,11 +53,10 @@ export const useAuth = () => {
const [user, setUser] = useState()
const [isNewDeviceLogin, setIsNewDeviceLogin] = useState(false)
const [userInfo, setUserInfo] = useState()
- const userManager = useMemo(() => new UserManager(getClientSettings()), [])
const login = useCallback(async () => {
userManager.signinRedirect({ extraQueryParams: { lang } })
- }, [userManager, lang])
+ }, [lang])
const logout = useCallback((key?: string) => {
setPage(history.location.pathname)
@@ -73,7 +70,7 @@ export const useAuth = () => {
removeCookie('access_token')
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [userManager, lang])
+ }, [lang])
const storeUser = useCallback((loadedUser: User) => {
setUser(loadedUser)
@@ -83,6 +80,8 @@ export const useAuth = () => {
name: 'access_token',
value: loadedUser.access_token,
})
+ // обновляем токен в других окнах/вкладках
+ channel.postMessage({ token: loadedUser.access_token })
}, [])
const checkUser = useCallback(async () => {
@@ -102,7 +101,6 @@ export const useAuth = () => {
return loadedUser
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
- userManager,
storeUser,
markUserLoaded,
])
@@ -173,12 +171,31 @@ export const useAuth = () => {
}).catch(login)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
- userManager,
login,
storeUser,
markUserLoaded,
])
+ useEventListener({
+ callback: useCallback(async (e: MessageEvent<{ token: string }>) => {
+ const loadedUser = await userManager.getUser()
+
+ if (
+ !e.data.token
+ || !loadedUser
+ || loadedUser.access_token === e.data.token
+ ) return
+
+ userManager.storeUser({
+ ...loadedUser,
+ access_token: e.data.token,
+ toStorageString: loadedUser.toStorageString,
+ })
+ }, []),
+ event: 'message',
+ target: channel,
+ })
+
useEffect(() => {
const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect'
isRedirectedBackFromAuthProvider ? signinRedirectCallback() : checkUser()
@@ -200,34 +217,41 @@ export const useAuth = () => {
setTimeout(logout, 10000)
}
})
- }, [logout, userManager])
+ }, [logout])
- // eslint-disable-next-line
const checkNewDevice = useCallback(async () => {
const loadedUser = await userManager.getUser()
if (!loadedUser) return
checkDevice(loadedUser.access_token).catch(() => {
- setTimeout(reChekNewDevice, 2000)
+ setTimeout(reChekNewDevice, 5000)
})
- }, [reChekNewDevice, userManager])
-
- // useEffect(() => {
- // if (!needCheckNewDeviсe && !user) return undefined
- // const startCheckDevice = setInterval(checkNewDevice, 20000)
- // isNewDeviceLogin && clearInterval(startCheckDevice)
- // return () => clearInterval(startCheckDevice)
- //
- // // eslint-disable-next-line react-hooks/exhaustive-deps
- // }, [
- // checkNewDevice,
- // isNewDeviceLogin,
- // setIsNewDeviceLogin,
- // ])
+ }, [reChekNewDevice])
+
+ useEffect(() => {
+ if (!needCheckNewDeviсe && !user) return undefined
+ const startCheckDevice = setInterval(checkNewDevice, 20000)
+ isNewDeviceLogin && clearInterval(startCheckDevice)
+ return () => clearInterval(startCheckDevice)
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [
+ checkNewDevice,
+ isNewDeviceLogin,
+ setIsNewDeviceLogin,
+ ])
useEffect(() => {
// попытаемся обновить токен используя refresh_token
const tryRenewToken = () => {
+ const tokenLastUpdated = Number(localStorage.getItem('token_updated'))
+ // предотвращаем одновременное обновление токена в разных окнах/вкладках
+ const needRenewToken = Date.now() - tokenLastUpdated >= userManager.settings.clockSkew! * 1e3
+
+ if (!needRenewToken) return
+
+ localStorage.setItem('token_updated', String(Date.now()))
+
userManager.signinSilent()
.catch(() => user && logout())
}
@@ -241,14 +265,7 @@ export const useAuth = () => {
userManager.events.removeAccessTokenExpired(tryRenewToken)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [userManager, logout])
-
- useEffect(() => {
- // событие срабатывает после получения токена(первый
- // логин и обновление токена)
- userManager.events.addUserLoaded(storeUser)
- return () => userManager.events.removeUserLoaded(storeUser)
- }, [userManager, storeUser])
+ }, [logout])
const fetchUserInfo = useCallback(async () => {
try {
diff --git a/src/helpers/index.tsx b/src/helpers/index.tsx
index f1e5376d..ce271b5d 100644
--- a/src/helpers/index.tsx
+++ b/src/helpers/index.tsx
@@ -11,3 +11,6 @@ export * from './selectedApi'
export * from './openSubscribePopup'
export * from './getCurrentYear'
export * from './getTeamAbbr'
+export * from './cookie'
+export * from './isMatchPage'
+export * from './languageUrlParam'
diff --git a/src/hooks/useEventListener.tsx b/src/hooks/useEventListener.tsx
index 3d0e4680..4325e25c 100644
--- a/src/hooks/useEventListener.tsx
+++ b/src/hooks/useEventListener.tsx
@@ -4,7 +4,7 @@ import { useEffect, useRef } from 'react'
type EventMap = HTMLElementEventMap & WindowEventMap
-type Target = RefObject | HTMLElement | Window
+type Target = RefObject | HTMLElement | Window | BroadcastChannel
type Args = {
callback: (e: EventMap[E]) => void,
@@ -28,7 +28,7 @@ export const useEventListener = ({
}, [callback])
useEffect(() => {
- const windowOrElement: HTMLElement | Window | null = 'current' in target
+ const windowOrElement: HTMLElement | Window | BroadcastChannel | null = 'current' in target
? target.current
: target
diff --git a/src/requests/index.tsx b/src/requests/index.tsx
index d96b03e2..494e9a3a 100644
--- a/src/requests/index.tsx
+++ b/src/requests/index.tsx
@@ -34,3 +34,5 @@ export * from './getTeamsStats'
export * from './getPlayersStats'
export * from './getMatchParticipants'
export * from './getStatsEvents'
+export * from './getTokenVirtualUser'
+export * from './checkDevice'