You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
spa_instat_tv/src/features/AuthStore/hooks/useAuth.tsx

268 lines
6.9 KiB

import {
useCallback,
useState,
useMemo,
useEffect,
} from 'react'
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'
import { PAGES } from 'config'
import {
addLanguageUrlParam,
} from 'helpers/languageUrlParam'
import {
writeToken,
removeToken,
readToken,
} from 'helpers/token'
import {
setCookie,
removeCookie,
} from 'helpers/cookie'
import { useLocalStore, useToggle } 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 { getClientSettings, needCheckNewDeviсe } from '../helpers'
import { getTokenVirtualUser } from '../../../requests'
export const useAuth = () => {
const { changeLang, lang } = useLexicsStore()
const history = useHistory()
const {
close: markUserLoaded,
isOpen: loadingUser,
} = useToggle(true)
const [user, setUser] = useState<User>()
const [isNewDeviceLogin, setIsNewDeviceLogin] = useState(false)
const [userInfo, setUserInfo] = useState<UserInfo>()
const userManager = useMemo(() => new UserManager(getClientSettings()), [])
const login = useCallback(async () => {
userManager.signinRedirect({ extraQueryParams: { lang } })
}, [userManager, lang])
const logout = useCallback((key?: string) => {
setPage(history.location.pathname)
userManager.clearStaleState()
userManager.createSigninRequest().then(({ url }) => {
const urlWithLang = addLanguageUrlParam(lang, url)
userManager.signoutRedirect({ post_logout_redirect_uri: urlWithLang })
})
removeToken()
if (key !== 'saveToken') {
removeCookie('access_token')
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userManager, lang])
const storeUser = useCallback((loadedUser: User) => {
setUser(loadedUser)
writeToken(loadedUser.access_token)
setCookie({
exdays: 1,
name: 'access_token',
value: loadedUser.access_token,
})
}, [])
const checkUser = useCallback(async () => {
const loadedUser = await userManager.getUser()
if (!loadedUser) {
if (!readToken()) {
const token = await getTemporaryToken()
token && await fetchUserInfo()
return Promise.resolve()
}
return Promise.reject()
}
storeUser(loadedUser)
markUserLoaded()
return loadedUser
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
userManager,
storeUser,
markUserLoaded,
])
const [page, setPage] = useLocalStore({
clearOnUnmount: true,
defaultValue: '',
key: 'matchBackLocation',
validator: isString,
})
const [search, setSearch] = useLocalStore({
clearOnUnmount: true,
defaultValue: '',
key: 'searchBack',
validator: isString,
})
const getTemporaryToken = async () => {
try {
const { access_token } = await getTokenVirtualUser()
writeToken(access_token)
setCookie({
exdays: 1,
name: 'access_token',
value: access_token,
})
return access_token
// eslint-disable-next-line no-empty
} catch {
return ''
}
}
const signinRedirectCallback = useCallback(() => {
userManager.signinRedirectCallback()
.then((loadedUser) => {
storeUser(loadedUser)
queryParamStorage.clear()
if (page.includes(PAGES.useraccount)) {
history.push(PAGES.home)
} else {
const route = `${page}${search}`
history.push(route)
}
markUserLoaded()
setPage('')
setSearch('')
// }
}).catch(login)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
userManager,
login,
storeUser,
markUserLoaded,
])
useEffect(() => {
const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect'
isRedirectedBackFromAuthProvider ? signinRedirectCallback() : checkUser()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
checkUser,
signinRedirectCallback,
login,
])
const reChekNewDevice = useCallback(async () => {
const loadedUser = await userManager.getUser()
if (!loadedUser) return
checkDevice(loadedUser.access_token).catch((er:FailedResponse) => {
if (er.error) return
if (isBoolean(er.ok) && !er.ok) {
setIsNewDeviceLogin(true)
setTimeout(logout, 10000)
}
})
}, [logout, userManager])
const checkNewDevice = useCallback(async () => {
const loadedUser = await userManager.getUser()
if (!loadedUser) return
checkDevice(loadedUser.access_token).catch(() => {
setTimeout(reChekNewDevice, 2000)
})
}, [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,
])
useEffect(() => {
// попытаемся обновить токен используя refresh_token
const tryRenewToken = () => {
userManager.signinSilent()
.catch(() => user && logout())
}
// если запросы вернули 401 | 403
window.addEventListener('FORBIDDEN_REQUEST', tryRenewToken)
// и если токен истек
userManager.events.addAccessTokenExpired(tryRenewToken)
return () => {
window.removeEventListener('FORBIDDEN_REQUEST', tryRenewToken)
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])
const fetchUserInfo = useCallback(async () => {
try {
const userInfoFetched = await getUserInfo()
setUserInfo(userInfoFetched)
userInfoFetched.language.iso && changeLang(userInfoFetched.language.iso)
// eslint-disable-next-line no-empty
} catch (error) {}
}, [changeLang])
useEffect(() => {
fetchUserInfo()
}, [fetchUserInfo, user])
const auth = useMemo(() => ({
fetchUserInfo,
isNewDeviceLogin,
loadingUser,
login,
logout,
setSearch,
setUserInfo,
user,
userInfo,
}), [
fetchUserInfo,
isNewDeviceLogin,
logout,
user,
userInfo,
login,
loadingUser,
setSearch,
setUserInfo,
])
return auth
}