diff --git a/openmetadata-ui/src/main/resources/ui/src/authentication/auth-provider/AuthProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/authentication/auth-provider/AuthProvider.tsx index 7faf1b725c3..b86bb959ed8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/authentication/auth-provider/AuthProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/authentication/auth-provider/AuthProvider.tsx @@ -17,7 +17,6 @@ import { MsalProvider } from '@azure/msal-react'; import { LoginCallback } from '@okta/okta-react'; import { AxiosError } from 'axios'; import { CookieStorage } from 'cookie-storage'; -import jwtDecode, { JwtPayload } from 'jwt-decode'; import { isEmpty, isNil } from 'lodash'; import { observer } from 'mobx-react'; import React, { @@ -51,6 +50,7 @@ import { AuthTypes } from '../../enums/signin.enum'; import { User } from '../../generated/entity/teams/user'; import jsonData from '../../jsons/en'; import { + extractDetailsFromToken, getAuthConfig, getNameFromEmail, getUrlPathnameExpiry, @@ -109,10 +109,11 @@ export const AuthProvider = ({ authenticatorRef.current?.invokeLogin(); }; - const onLogoutHandler = () => { + const onLogoutHandler = useCallback(() => { + clearTimeout(timeoutId); authenticatorRef.current?.invokeLogout(); setLoading(false); - }; + }, [timeoutId]); const onRenewIdTokenHandler = () => { return authenticatorRef.current?.renewIdToken(); @@ -292,31 +293,21 @@ export const AuthProvider = ({ * This method will be call upon successful signIn */ const startTokenExpiryTimer = () => { - const token: string | void = localStorage.getItem(oidcTokenKey) || ''; - // If token is not present do nothing - if (token) { - try { - // Extract expiry - const { exp } = jwtDecode(token); - if (exp && exp * 1000 > Date.now()) { - // Check if token isn't expired yet - const diff = exp * 1000 - Date.now(); /* Convert to MS */ + // Extract expiry + const { exp, isExpired, diff, timeoutExpiry } = extractDetailsFromToken(); - // Have 50s buffer before start trying for silent signIn - // If token is about to expire then start silentSignIn - // else just set timer to try for silentSignIn before token expires - if (diff > 50000) { - const timerId = setTimeout(() => { - trySilentSignIn(); - }, diff); - setTimeoutId(Number(timerId)); - } else { - trySilentSignIn(); - } - } - } catch (error) { - // eslint-disable-next-line no-console - console.error('Error parsing id token.', error); + if (!isExpired && exp && diff && timeoutExpiry) { + // Have 2m buffer before start trying for silent signIn + // If token is about to expire then start silentSignIn + // else just set timer to try for silentSignIn before token expires + if (diff > 120000) { + clearTimeout(timeoutId); + const timerId = setTimeout(() => { + trySilentSignIn(); + }, timeoutExpiry); + setTimeoutId(Number(timerId)); + } else { + trySilentSignIn(); } } }; diff --git a/openmetadata-ui/src/main/resources/ui/src/hooks/authHooks.ts b/openmetadata-ui/src/main/resources/ui/src/hooks/authHooks.ts index 0f1171b5907..dcc28a3d0ab 100644 --- a/openmetadata-ui/src/main/resources/ui/src/hooks/authHooks.ts +++ b/openmetadata-ui/src/main/resources/ui/src/hooks/authHooks.ts @@ -21,7 +21,8 @@ export const useAuth = (pathname = '') => { const isAuthenticatedRoute = pathname !== ROUTES.SIGNUP && pathname !== ROUTES.SIGNIN && - pathname !== ROUTES.CALLBACK; + pathname !== ROUTES.CALLBACK && + pathname !== ROUTES.SILENT_CALLBACK; const isTourRoute = pathname === ROUTES.TOUR; return { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts b/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts index a6ee752714e..24d477839bb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.ts @@ -18,15 +18,19 @@ import { PopupRequest, PublicClientApplication, } from '@azure/msal-browser'; +import jwtDecode, { JwtPayload } from 'jwt-decode'; import { isNil } from 'lodash'; import { WebStorageStateStore } from 'oidc-client'; -import { ROUTES } from '../constants/constants'; +import { oidcTokenKey, ROUTES } from '../constants/constants'; import { validEmailRegEx } from '../constants/regex.constants'; import { AuthTypes } from '../enums/signin.enum'; import { isDev } from './EnvironmentUtils'; export let msalInstance: IPublicClientApplication; +const DATE_NOW = Date.now(); +const EXPIRY_THRESHOLD_MILLES = 2 * 60 * 100; + export const getOidcExpiry = () => { return new Date(Date.now() + 60 * 60 * 24 * 1000); }; @@ -184,7 +188,8 @@ export const isProtectedRoute = (pathname: string) => { return ( pathname !== ROUTES.SIGNUP && pathname !== ROUTES.SIGNIN && - pathname !== ROUTES.CALLBACK + pathname !== ROUTES.CALLBACK && + pathname !== ROUTES.SILENT_CALLBACK ); }; @@ -195,3 +200,38 @@ export const isTourRoute = (pathname: string) => { export const getUrlPathnameExpiry = () => { return new Date(Date.now() + 60 * 60 * 1000); }; + +/** + * @exp expiry of token + * @isExpired wether token is already expired or not + * @diff Difference between token expiry & current time in ms + * @timeoutExpiry time in ms for try to silent sign-in + * @returns exp, isExpired, diff, timeoutExpiry + */ +export const extractDetailsFromToken = () => { + const token = localStorage.getItem(oidcTokenKey) || ''; + + try { + const { exp } = jwtDecode(token); + + const diff = exp && exp * 1000 - DATE_NOW; + const timeoutExpiry = diff && diff - EXPIRY_THRESHOLD_MILLES; + + return { + exp, + isExpired: exp && DATE_NOW >= exp * 1000, + diff, + timeoutExpiry, + }; + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error parsing id token.', error); + } + + return { + exp: 0, + isExpired: true, + diff: 0, + timeoutExpiry: 0, + }; +};