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

228 lines
6.0 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 } from 'helpers/token'
import { setCookie, removeCookie } from 'helpers/cookie'
import { isMatchPage, isMatchPageRFEF } from 'helpers/isMatchPage'
import { useLocalStore, useToggle } from 'hooks'
import { useLexicsStore } from 'features/LexicsStore'
import { queryParamStorage } from 'features/QueryParamsStorage'
import { getUserInfo } from 'requests/getUserInfo'
import { checkDevice, FailedResponse } from 'requests/checkDevice'
import { getClientSettings, needCheckNewDeviсe } from '../helpers'
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 userManager = useMemo(() => new UserManager(getClientSettings()), [])
const login = useCallback(async () => (
userManager.signinRedirect({ extraQueryParams: { lang } })
), [userManager, lang])
const logout = useCallback(() => {
userManager.clearStaleState()
userManager.createSigninRequest().then(({ url }) => {
const urlWithLang = addLanguageUrlParam(lang, url)
userManager.signoutRedirect({ post_logout_redirect_uri: urlWithLang })
})
removeToken()
removeCookie('access_token')
}, [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) return Promise.reject()
storeUser(loadedUser)
markUserLoaded()
return loadedUser
}, [
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 signinRedirectCallback = useCallback(() => {
userManager.signinRedirectCallback()
.then((loadedUser) => {
storeUser(loadedUser)
queryParamStorage.clear()
history.replace(PAGES.home)
markUserLoaded()
if (page) {
const route = `${page}${page === '/' ? search : ''}`
history.push(route)
setPage('')
setSearch('')
}
}).catch(login)
}, [
userManager,
login,
storeUser,
history,
markUserLoaded,
page,
search,
setPage,
setSearch,
])
useEffect(() => {
const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect'
if (isRedirectedBackFromAuthProvider) {
signinRedirectCallback()
} else {
checkUser().catch(() => {
if (!isMatchPage() && !isMatchPageRFEF()) {
login()
}
if (history.location.pathname === '/') {
setSearch(history.location.search)
}
setPage(history.location.pathname)
})
}
}, [
checkUser,
signinRedirectCallback,
login,
setPage,
setSearch,
history,
])
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) return undefined
const startCheckDevice = setInterval(checkNewDevice, 20000)
isNewDeviceLogin && clearInterval(startCheckDevice)
return () => clearInterval(startCheckDevice)
}, [checkNewDevice,
isNewDeviceLogin,
setIsNewDeviceLogin,
])
useEffect(() => {
// попытаемся обновить токен используя refresh_token
const tryRenewToken = () => {
userManager.signinSilent().catch(logout)
}
// если запросы вернули 401 | 403
window.addEventListener('FORBIDDEN_REQUEST', tryRenewToken)
// и если токен истек
userManager.events.addAccessTokenExpired(tryRenewToken)
return () => {
window.removeEventListener('FORBIDDEN_REQUEST', tryRenewToken)
userManager.events.removeAccessTokenExpired(tryRenewToken)
}
}, [userManager, logout])
useEffect(() => {
// событие срабатывает после получения токена(первый
// логин и обновление токена)
userManager.events.addUserLoaded(storeUser)
return () => userManager.events.removeUserLoaded(storeUser)
}, [userManager, storeUser])
const fetchUserInfo = useCallback(async () => {
const userInfoFetched = await getUserInfo()
userInfoFetched.language.iso && changeLang(userInfoFetched.language.iso)
}, [changeLang])
useEffect(() => {
if (user) {
fetchUserInfo()
}
}, [fetchUserInfo, user])
const auth = useMemo(() => ({
isNewDeviceLogin,
loadingUser,
login,
logout,
user,
}), [
isNewDeviceLogin,
logout,
user,
login,
loadingUser,
])
return auth
}