fix(#518): multiple tabs logout #192

Merged
roman.rakov merged 2 commits from in-518-multiple-tabs-logout into develop 3 years ago
  1. 27924
      package-lock.json
  2. 1
      package.json
  3. 1
      src/features/AuthStore/helpers.tsx
  4. 47
      src/features/AuthStore/hooks/useAuth.tsx
  5. 4
      src/requests/checkDevice.tsx

27924
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -27,6 +27,7 @@
"@stripe/stripe-js": "^1.13.2", "@stripe/stripe-js": "^1.13.2",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"date-fns": "^2.14.0", "date-fns": "^2.14.0",
"dueljs": "^1.2.7",
roman.rakov marked this conversation as resolved

а мы можем обойтись без того, чтобы не тянуть еще одну библиотеку?

а мы можем обойтись без того, чтобы не тянуть еще одну библиотеку?
"eslint-plugin-typescript-sort-keys": "^2.3.0", "eslint-plugin-typescript-sort-keys": "^2.3.0",
"history": "^4.10.1", "history": "^4.10.1",
"hls.js": "^1.1.1", "hls.js": "^1.1.1",

@ -61,7 +61,6 @@ const redirectUrl = () => {
export const getClientSettings = (): Settings => ({ export const getClientSettings = (): Settings => ({
authority: AUTH_SERVICE, authority: AUTH_SERVICE,
automaticSilentRenew: true,
client_id: client.auth.clientId, client_id: client.auth.clientId,
filterProtocolClaims: false, filterProtocolClaims: false,
loadUserInfo: false, loadUserInfo: false,

@ -7,6 +7,8 @@ import {
import { useHistory } from 'react-router' import { useHistory } from 'react-router'
import type { User } from 'oidc-client' import type { User } from 'oidc-client'
// @ts-expect-error
import duel from 'dueljs'
import isString from 'lodash/isString' import isString from 'lodash/isString'
import isBoolean from 'lodash/isBoolean' import isBoolean from 'lodash/isBoolean'
@ -21,14 +23,12 @@ import {
readToken, readToken,
setCookie, setCookie,
removeCookie, removeCookie,
TOKEN_KEY,
} from 'helpers' } from 'helpers'
import { import {
useLocalStore, useLocalStore,
useSessionStore, useSessionStore,
useToggle, useToggle,
useEventListener,
} from 'hooks' } from 'hooks'
import { useLexicsStore } from 'features/LexicsStore' import { useLexicsStore } from 'features/LexicsStore'
@ -172,27 +172,6 @@ export const useAuth = () => {
markUserLoaded, markUserLoaded,
]) ])
useEventListener({
callback: useCallback(async (e: StorageEvent) => {
const loadedUser = await userManager.getUser()
if (
e.storageArea !== localStorage
|| e.key !== TOKEN_KEY
|| !e.newValue
|| !loadedUser
|| loadedUser.access_token === e.newValue
) return
userManager.storeUser({
...loadedUser,
access_token: e.newValue,
toStorageString: loadedUser.toStorageString,
})
}, []),
event: 'storage',
})
useEffect(() => { useEffect(() => {
const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect' const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect'
isRedirectedBackFromAuthProvider ? signinRedirectCallback() : checkUser() isRedirectedBackFromAuthProvider ? signinRedirectCallback() : checkUser()
@ -238,28 +217,24 @@ export const useAuth = () => {
setIsNewDeviceLogin, setIsNewDeviceLogin,
]) ])
duel.channel('active_page') // поле в LS, определяющее активность вкладки
useEffect(() => { useEffect(() => {
// попытаемся обновить токен используя refresh_token // попытаемся обновить токен используя refresh_token
const tryRenewToken = () => { const tryRenewToken = () => {
const tokenLastUpdated = Number(localStorage.getItem('token_updated')) // библиотека oidc-client не поддерживает обновление токена только на 1 вкладке
// предотвращаем одновременное обновление токена в разных окнах/вкладках // @ts-ignore
const needRenewToken = Date.now() - tokenLastUpdated >= 2 * 1e3 if (window.isMaster()) {
userManager.signinSilent().catch(logout)
if (!needRenewToken) return }
localStorage.setItem('token_updated', String(Date.now()))
userManager.signinSilent()
.catch(logout)
} }
// если запросы вернули 401 | 403 // если запросы вернули 401 | 403
window.addEventListener('FORBIDDEN_REQUEST', tryRenewToken) window.addEventListener('FORBIDDEN_REQUEST', tryRenewToken)
// и если токен истек // и если токен истекает (по дефолту за 60 секунд)
userManager.events.addAccessTokenExpired(tryRenewToken) userManager.events.addAccessTokenExpiring(tryRenewToken)
return () => { return () => {
window.removeEventListener('FORBIDDEN_REQUEST', tryRenewToken) window.removeEventListener('FORBIDDEN_REQUEST', tryRenewToken)
userManager.events.removeAccessTokenExpired(tryRenewToken) userManager.events.removeAccessTokenExpiring(tryRenewToken)
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [logout]) }, [logout])

@ -1,4 +1,4 @@
import { AUTH_SERVICE } from '../config/routes' import { API_ROOT } from 'features/AuthServiceApp/config/routes'
export type FailedResponse = { export type FailedResponse = {
error?: string, error?: string,
@ -10,7 +10,7 @@ export type SuccessResponse = {
} }
export const checkDevice = async (token: string) => { export const checkDevice = async (token: string) => {
const url = `${AUTH_SERVICE}/authorize/check-device?access_token=${token}` const url = `${API_ROOT}/authorize/check-device?access_token=${token}`
const config = { const config = {
method: 'GET', method: 'GET',

Loading…
Cancel
Save