fix: user has to login every time chrome sidepanel is opened (#5544)

We can pass the auth tokens to our front app via post message, which
will also allow us to pass route names to navigate on it
This commit is contained in:
Aditya Pimpalkar
2024-05-30 11:58:45 +01:00
committed by GitHub
parent d770e56e31
commit a12c1aad5e
30 changed files with 511 additions and 231 deletions

View File

@ -0,0 +1,58 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { tokenPairState } from '@/auth/states/tokenPairState';
import { isLoadingTokensFromExtensionState } from '@/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState';
import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState';
import { isDefined } from '~/utils/isDefined';
import { isInFrame } from '~/utils/isInIframe';
export const ChromeExtensionSidecarEffect = () => {
const navigate = useNavigate();
const setTokenPair = useSetRecoilState(tokenPairState);
const chromeExtensionId = useRecoilValue(chromeExtensionIdState);
const setIsLoadingTokensFromExtension = useSetRecoilState(
isLoadingTokensFromExtensionState,
);
useEffect(() => {
if (isInFrame() && isDefined(chromeExtensionId)) {
window.parent.postMessage(
'loaded',
`chrome-extension://${chromeExtensionId}`,
);
const handleWindowEvents = (event: MessageEvent<any>) => {
if (event.origin === `chrome-extension://${chromeExtensionId}`) {
switch (event.data.type) {
case 'tokens': {
setTokenPair(event.data.value);
setIsLoadingTokensFromExtension(true);
break;
}
case 'navigate':
navigate(event.data.value);
break;
default:
break;
}
} else {
setIsLoadingTokensFromExtension(false);
return;
}
};
window.addEventListener('message', handleWindowEvents);
return () => {
window.removeEventListener('message', handleWindowEvents);
};
}
}, [
chromeExtensionId,
setIsLoadingTokensFromExtension,
setTokenPair,
navigate,
]);
return <></>;
};

View File

@ -0,0 +1,56 @@
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { isLoadingTokensFromExtensionState } from '@/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState';
import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState';
import { isDefined } from '~/utils/isDefined';
import { isInFrame } from '~/utils/isInIframe';
const StyledContainer = styled.div`
align-items: center;
display: flex;
flex-direction: column;
height: 100vh;
justify-content: center;
`;
const AppInaccessible = ({ message }: { message: string }) => {
return (
<StyledContainer>
<img
src="/images/integrations/twenty-logo.svg"
alt="twenty-icon"
height={40}
width={40}
/>
<h3>{message}</h3>
</StyledContainer>
);
};
export const ChromeExtensionSidecarProvider: React.FC<
React.PropsWithChildren
> = ({ children }) => {
const isLoadingTokensFromExtension = useRecoilValue(
isLoadingTokensFromExtensionState,
);
const chromeExtensionId = useRecoilValue(chromeExtensionIdState);
if (!isInFrame()) return <>{children}</>;
if (!isDefined(chromeExtensionId))
return (
<AppInaccessible message={`Twenty is not accessible inside an iframe.`} />
);
if (isDefined(isLoadingTokensFromExtension) && !isLoadingTokensFromExtension)
return (
<AppInaccessible
message={`Unauthorized access from iframe origin. If you're trying to access from chrome extension,
please check your chrome extension ID on your server.
`}
/>
);
return isLoadingTokensFromExtension && <>{children}</>;
};

View File

@ -0,0 +1,6 @@
import { createState } from 'twenty-ui';
export const isLoadingTokensFromExtensionState = createState<boolean | null>({
key: 'isLoadingTokensFromExtensionState',
defaultValue: null,
});

View File

@ -4,6 +4,7 @@ import { useRecoilState, useSetRecoilState } from 'recoil';
import { authProvidersState } from '@/client-config/states/authProvidersState';
import { billingState } from '@/client-config/states/billingState';
import { captchaProviderState } from '@/client-config/states/captchaProviderState';
import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState';
import { isClientConfigLoadedState } from '@/client-config/states/isClientConfigLoadedState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
@ -32,6 +33,8 @@ export const ClientConfigProviderEffect = () => {
const setCaptchaProvider = useSetRecoilState(captchaProviderState);
const setChromeExtensionId = useSetRecoilState(chromeExtensionIdState);
const { data, loading } = useGetClientConfigQuery({
skip: isClientConfigLoaded,
});
@ -63,6 +66,8 @@ export const ClientConfigProviderEffect = () => {
provider: data?.clientConfig?.captcha?.provider,
siteKey: data?.clientConfig?.captcha?.siteKey,
});
setChromeExtensionId(data?.clientConfig?.chromeExtensionId);
}
}, [
data,
@ -77,6 +82,7 @@ export const ClientConfigProviderEffect = () => {
loading,
setIsClientConfigLoaded,
setCaptchaProvider,
setChromeExtensionId,
]);
return <></>;

View File

@ -33,6 +33,7 @@ export const GET_CLIENT_CONFIG = gql`
provider
siteKey
}
chromeExtensionId
}
}
`;

View File

@ -0,0 +1,6 @@
import { createState } from 'twenty-ui';
export const chromeExtensionIdState = createState<string | null | undefined>({
key: 'chromeExtensionIdState',
defaultValue: null,
});