fix: google refresh token silent callback (#10010)

* fix: google refresh token silent callback

* refactor: update axios interceptor reference in context

* refactor: address review comments
This commit is contained in:
karanh37 2023-01-30 23:49:58 +05:30 committed by GitHub
parent c81446e2be
commit 97ea3608bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 49 deletions

View File

@ -18,7 +18,7 @@ import { MsalProvider } from '@azure/msal-react';
import { LoginCallback } from '@okta/okta-react'; import { LoginCallback } from '@okta/okta-react';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { CookieStorage } from 'cookie-storage'; import { CookieStorage } from 'cookie-storage';
import { isEmpty, isNil } from 'lodash'; import { isEmpty, isNil, isNumber } from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import React, { import React, {
ComponentType, ComponentType,
@ -43,7 +43,6 @@ import { AuthenticationConfiguration } from '../../../generated/configuration/au
import { AuthType, User } from '../../../generated/entity/teams/user'; import { AuthType, User } from '../../../generated/entity/teams/user';
import jsonData from '../../../jsons/en'; import jsonData from '../../../jsons/en';
import { import {
EXPIRY_THRESHOLD_MILLES,
extractDetailsFromToken, extractDetailsFromToken,
getAuthConfig, getAuthConfig,
getNameFromEmail, getNameFromEmail,
@ -87,6 +86,9 @@ const userAPIQueryFields = 'profile,teams,roles';
const isEmailVerifyField = 'isEmailVerified'; const isEmailVerifyField = 'isEmailVerified';
let requestInterceptor: number | null = null;
let responseInterceptor: number | null = null;
export const AuthProvider = ({ export const AuthProvider = ({
childComponentType, childComponentType,
children, children,
@ -242,26 +244,15 @@ export const AuthProvider = ({
* Renew Id Token handler for all the SSOs. * Renew Id Token handler for all the SSOs.
* This method will be called when the id token is about to expire. * This method will be called when the id token is about to expire.
*/ */
const renewIdToken = (): Promise<string> => { const renewIdToken = async () => {
const onRenewIdTokenHandlerPromise = onRenewIdTokenHandler(); try {
const onRenewIdTokenHandlerPromise = onRenewIdTokenHandler();
onRenewIdTokenHandlerPromise && (await onRenewIdTokenHandlerPromise);
} catch (error) {
console.error((error as AxiosError).message);
}
return new Promise((resolve, reject) => { return localState.getOidcToken();
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');
}
});
}; };
/** /**
@ -291,28 +282,24 @@ export const AuthProvider = ({
}; };
/** /**
* It will set an timer for 50 secs before Token will expire * It will set an timer for 5 mins before Token will expire
* If time if less then 50 secs then it will try to SilentSignIn * 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 * It will also ensure that we have time left for token expiry
* This method will be call upon successful signIn * This method will be call upon successful signIn
*/ */
const startTokenExpiryTimer = () => { const startTokenExpiryTimer = () => {
// Extract expiry // Extract expiry
const { exp, isExpired, diff, timeoutExpiry } = extractDetailsFromToken(); const { isExpired, timeoutExpiry } = extractDetailsFromToken();
if (!isExpired && exp && diff && timeoutExpiry) { if (!isExpired && isNumber(timeoutExpiry)) {
// Have 2m buffer before start trying for silent signIn // Have 5m buffer before start trying for silent signIn
// If token is about to expire then start silentSignIn // If token is about to expire then start silentSignIn
// else just set timer to try for silentSignIn before token expires // else just set timer to try for silentSignIn before token expires
if (diff > EXPIRY_THRESHOLD_MILLES) { clearTimeout(timeoutId);
clearTimeout(timeoutId); const timerId = setTimeout(() => {
const timerId = setTimeout(() => {
trySilentSignIn();
}, timeoutExpiry);
setTimeoutId(Number(timerId));
} else {
trySilentSignIn(); trySilentSignIn();
} }, timeoutExpiry);
setTimeoutId(Number(timerId));
} }
}; };
@ -324,12 +311,6 @@ export const AuthProvider = ({
clearTimeout(timeoutId); clearTimeout(timeoutId);
}, [timeoutId]); }, [timeoutId]);
useEffect(() => {
startTokenExpiryTimer();
return cleanup;
}, []);
const handleFailedLogin = () => { const handleFailedLogin = () => {
setIsSigningIn(false); setIsSigningIn(false);
setIsUserAuthenticated(false); setIsUserAuthenticated(false);
@ -398,7 +379,17 @@ export const AuthProvider = ({
*/ */
const initializeAxiosInterceptors = () => { const initializeAxiosInterceptors = () => {
// Axios Request interceptor to add Bearer tokens in Header // 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() || ''; const token: string = localState.getOidcToken() || '';
if (token) { if (token) {
if (config.headers) { if (config.headers) {
@ -414,7 +405,7 @@ export const AuthProvider = ({
}); });
// Axios response interceptor for statusCode 401,403 // Axios response interceptor for statusCode 401,403
axiosClient.interceptors.response.use( responseInterceptor = axiosClient.interceptors.response.use(
(response) => response, (response) => response,
(error) => { (error) => {
if (error.response) { if (error.response) {
@ -573,6 +564,9 @@ export const AuthProvider = ({
useEffect(() => { useEffect(() => {
fetchAuthConfig(); fetchAuthConfig();
startTokenExpiryTimer();
return cleanup;
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -625,6 +619,7 @@ export const AuthProvider = ({
setLoadingIndicator, setLoadingIndicator,
handleSuccessfulLogin, handleSuccessfulLogin,
handleUserCreated, handleUserCreated,
updateAxiosInterceptors: initializeAxiosInterceptors,
jwtPrincipalClaims, jwtPrincipalClaims,
}; };

View File

@ -74,6 +74,7 @@ const OidcAuthenticator = forwardRef<AuthenticatorRef, Props>(
isSigningIn, isSigningIn,
setIsSigningIn, setIsSigningIn,
setLoadingIndicator, setLoadingIndicator,
updateAxiosInterceptors,
} = useAuthContext(); } = useAuthContext();
const history = useHistory(); const history = useHistory();
const { userDetails, newUser } = AppState; const { userDetails, newUser } = AppState;
@ -94,7 +95,10 @@ const OidcAuthenticator = forwardRef<AuthenticatorRef, Props>(
// Performs silent signIn and returns with IDToken // Performs silent signIn and returns with IDToken
const signInSilently = async () => { 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, () => ({ useImperativeHandle(ref, () => ({
@ -158,6 +162,7 @@ const OidcAuthenticator = forwardRef<AuthenticatorRef, Props>(
}} }}
onSuccess={(user) => { onSuccess={(user) => {
localState.setOidcToken(user.id_token); localState.setOidcToken(user.id_token);
updateAxiosInterceptors();
}} }}
/> />
</> </>

View File

@ -30,11 +30,7 @@ import { isDev } from './EnvironmentUtils';
export let msalInstance: IPublicClientApplication; export let msalInstance: IPublicClientApplication;
export const EXPIRY_THRESHOLD_MILLES = 2 * 60 * 1000; export const EXPIRY_THRESHOLD_MILLES = 5 * 60 * 1000;
export const getOidcExpiry = () => {
return new Date(Date.now() + 60 * 60 * 24 * 1000);
};
export const getRedirectUri = (callbackUrl: string) => { export const getRedirectUri = (callbackUrl: string) => {
return isDev() return isDev()
@ -279,7 +275,10 @@ export const extractDetailsFromToken = () => {
const dateNow = Date.now(); const dateNow = Date.now();
const diff = exp && exp * 1000 - dateNow; 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 { return {
exp, exp,