From 97ea3608bd4fd7162bdef83ff3dcd39c873cc4d1 Mon Sep 17 00:00:00 2001 From: karanh37 <33024356+karanh37@users.noreply.github.com> Date: Mon, 30 Jan 2023 23:49:58 +0530 Subject: [PATCH] fix: google refresh token silent callback (#10010) * fix: google refresh token silent callback * refactor: update axios interceptor reference in context * refactor: address review comments --- .../auth-provider/AuthProvider.tsx | 79 +++++++++---------- .../authenticators/OidcAuthenticator.tsx | 7 +- .../ui/src/utils/AuthProvider.util.ts | 11 ++- 3 files changed, 48 insertions(+), 49 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx index fe9a2962a56..84307612dd5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx @@ -18,7 +18,7 @@ import { MsalProvider } from '@azure/msal-react'; import { LoginCallback } from '@okta/okta-react'; import { AxiosError } from 'axios'; import { CookieStorage } from 'cookie-storage'; -import { isEmpty, isNil } from 'lodash'; +import { isEmpty, isNil, isNumber } from 'lodash'; import { observer } from 'mobx-react'; import React, { ComponentType, @@ -43,7 +43,6 @@ import { AuthenticationConfiguration } from '../../../generated/configuration/au import { AuthType, User } from '../../../generated/entity/teams/user'; import jsonData from '../../../jsons/en'; import { - EXPIRY_THRESHOLD_MILLES, extractDetailsFromToken, getAuthConfig, getNameFromEmail, @@ -87,6 +86,9 @@ const userAPIQueryFields = 'profile,teams,roles'; const isEmailVerifyField = 'isEmailVerified'; +let requestInterceptor: number | null = null; +let responseInterceptor: number | null = null; + export const AuthProvider = ({ childComponentType, children, @@ -242,26 +244,15 @@ export const AuthProvider = ({ * Renew Id Token handler for all the SSOs. * This method will be called when the id token is about to expire. */ - const renewIdToken = (): Promise => { - const onRenewIdTokenHandlerPromise = onRenewIdTokenHandler(); + const renewIdToken = async () => { + try { + const onRenewIdTokenHandlerPromise = onRenewIdTokenHandler(); + onRenewIdTokenHandlerPromise && (await onRenewIdTokenHandlerPromise); + } catch (error) { + console.error((error as AxiosError).message); + } - return new Promise((resolve, reject) => { - if (onRenewIdTokenHandlerPromise) { - onRenewIdTokenHandlerPromise - .then(() => { - resolve(localState.getOidcToken() || ''); - }) - .catch((error) => { - if (error.message !== 'Frame window timed out') { - reject(error); - } else { - resolve(localState.getOidcToken() || ''); - } - }); - } else { - reject('RenewIdTokenHandler is undefined'); - } - }); + return localState.getOidcToken(); }; /** @@ -291,28 +282,24 @@ export const AuthProvider = ({ }; /** - * It will set an timer for 50 secs before Token will expire - * If time if less then 50 secs then it will try to SilentSignIn + * It will set an timer for 5 mins before Token will expire + * If time if less then 5 mins then it will try to SilentSignIn * It will also ensure that we have time left for token expiry * This method will be call upon successful signIn */ const startTokenExpiryTimer = () => { // Extract expiry - const { exp, isExpired, diff, timeoutExpiry } = extractDetailsFromToken(); + const { isExpired, timeoutExpiry } = extractDetailsFromToken(); - if (!isExpired && exp && diff && timeoutExpiry) { - // Have 2m buffer before start trying for silent signIn + if (!isExpired && isNumber(timeoutExpiry)) { + // Have 5m 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 > EXPIRY_THRESHOLD_MILLES) { - clearTimeout(timeoutId); - const timerId = setTimeout(() => { - trySilentSignIn(); - }, timeoutExpiry); - setTimeoutId(Number(timerId)); - } else { + clearTimeout(timeoutId); + const timerId = setTimeout(() => { trySilentSignIn(); - } + }, timeoutExpiry); + setTimeoutId(Number(timerId)); } }; @@ -324,12 +311,6 @@ export const AuthProvider = ({ clearTimeout(timeoutId); }, [timeoutId]); - useEffect(() => { - startTokenExpiryTimer(); - - return cleanup; - }, []); - const handleFailedLogin = () => { setIsSigningIn(false); setIsUserAuthenticated(false); @@ -398,7 +379,17 @@ export const AuthProvider = ({ */ const initializeAxiosInterceptors = () => { // Axios Request interceptor to add Bearer tokens in Header - axiosClient.interceptors.request.use(async function (config) { + if (requestInterceptor != null) { + axiosClient.interceptors.request.eject(requestInterceptor); + } + + if (responseInterceptor != null) { + axiosClient.interceptors.response.eject(responseInterceptor); + } + + requestInterceptor = axiosClient.interceptors.request.use(async function ( + config + ) { const token: string = localState.getOidcToken() || ''; if (token) { if (config.headers) { @@ -414,7 +405,7 @@ export const AuthProvider = ({ }); // Axios response interceptor for statusCode 401,403 - axiosClient.interceptors.response.use( + responseInterceptor = axiosClient.interceptors.response.use( (response) => response, (error) => { if (error.response) { @@ -573,6 +564,9 @@ export const AuthProvider = ({ useEffect(() => { fetchAuthConfig(); + startTokenExpiryTimer(); + + return cleanup; }, []); useEffect(() => { @@ -625,6 +619,7 @@ export const AuthProvider = ({ setLoadingIndicator, handleSuccessfulLogin, handleUserCreated, + updateAxiosInterceptors: initializeAxiosInterceptors, jwtPrincipalClaims, }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/authentication/authenticators/OidcAuthenticator.tsx b/openmetadata-ui/src/main/resources/ui/src/components/authentication/authenticators/OidcAuthenticator.tsx index 0b7cc21a93b..ac953b666b0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/authentication/authenticators/OidcAuthenticator.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/authentication/authenticators/OidcAuthenticator.tsx @@ -74,6 +74,7 @@ const OidcAuthenticator = forwardRef( isSigningIn, setIsSigningIn, setLoadingIndicator, + updateAxiosInterceptors, } = useAuthContext(); const history = useHistory(); const { userDetails, newUser } = AppState; @@ -94,7 +95,10 @@ const OidcAuthenticator = forwardRef( // Performs silent signIn and returns with IDToken const signInSilently = async () => { - return userManager.signinSilent().then((user) => user.id_token); + const user = await userManager.signinSilent(); + localState.setOidcToken(user.id_token); + + return user.id_token; }; useImperativeHandle(ref, () => ({ @@ -158,6 +162,7 @@ const OidcAuthenticator = forwardRef( }} onSuccess={(user) => { localState.setOidcToken(user.id_token); + updateAxiosInterceptors(); }} /> 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 095416bc61b..d918878d9fc 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 @@ -30,11 +30,7 @@ import { isDev } from './EnvironmentUtils'; export let msalInstance: IPublicClientApplication; -export const EXPIRY_THRESHOLD_MILLES = 2 * 60 * 1000; - -export const getOidcExpiry = () => { - return new Date(Date.now() + 60 * 60 * 24 * 1000); -}; +export const EXPIRY_THRESHOLD_MILLES = 5 * 60 * 1000; export const getRedirectUri = (callbackUrl: string) => { return isDev() @@ -279,7 +275,10 @@ export const extractDetailsFromToken = () => { const dateNow = Date.now(); const diff = exp && exp * 1000 - dateNow; - const timeoutExpiry = diff && diff - EXPIRY_THRESHOLD_MILLES; + const timeoutExpiry = + diff && diff > EXPIRY_THRESHOLD_MILLES + ? diff - EXPIRY_THRESHOLD_MILLES + : 0; return { exp,