diff --git a/centrifuge-app/src/components/OnboardingAuthProvider.tsx b/centrifuge-app/src/components/OnboardingAuthProvider.tsx index 307ddb15fc..4150778476 100644 --- a/centrifuge-app/src/components/OnboardingAuthProvider.tsx +++ b/centrifuge-app/src/components/OnboardingAuthProvider.tsx @@ -11,11 +11,14 @@ export const OnboardingAuthContext = React.createContext<{ session?: { signed: string; payload: any } | null login: () => void isLoggingIn: boolean + isOnboardingExternally: boolean + setIsOnboardingExternally: React.Dispatch> }>(null as any) const AUTHORIZED_ONBOARDING_PROXY_TYPES = ['Any', 'Invest', 'NonTransfer', 'NonProxy'] export function OnboardingAuthProvider({ children }: { children: React.ReactNode }) { + const [isOnboardingExternally, setIsOnboardingExternally] = React.useState(false) const { substrate: { selectedWallet, selectedProxies, selectedAccount, evmChainId }, evm: { selectedAddress, ...evm }, @@ -24,13 +27,20 @@ export function OnboardingAuthProvider({ children }: { children: React.ReactNode const cent = useCentrifuge() const utils = useCentrifugeUtils() const provider = useEvmProvider() + // onboarding-api expects the wallet address in the native substrate format const address = selectedAccount?.address ? utils.formatAddress(selectedAccount?.address) : selectedAddress const proxy = selectedProxies?.[0] const { data: session, refetch: refetchSession } = useQuery( - ['session', selectedAccount?.address, proxy?.delegator, selectedAddress], + ['session', selectedAccount?.address, proxy?.delegator, selectedAddress, isOnboardingExternally], () => { + // if user comes from external app + if (isOnboardingExternally) { + const externalSignatureSession = sessionStorage.getItem('external-centrifuge-onboarding-auth') + if (externalSignatureSession) return JSON.parse(externalSignatureSession) + } + if (address) { if (proxy) { const rawItem = sessionStorage.getItem(`centrifuge-onboarding-auth-${address}-${proxy.delegator}`) @@ -45,7 +55,7 @@ export function OnboardingAuthProvider({ children }: { children: React.ReactNode } } }, - { enabled: !!selectedAccount?.address || !!selectedAddress } + { enabled: !!selectedAccount?.address || !!selectedAddress || isOnboardingExternally } ) const { mutate: login, isLoading: isLoggingIn } = useMutation(async () => { @@ -69,8 +79,10 @@ export function OnboardingAuthProvider({ children }: { children: React.ReactNode session, login, isLoggingIn, + isOnboardingExternally, + setIsOnboardingExternally, }), - [session, login, isLoggingIn] + [session, login, isLoggingIn, isOnboardingExternally, setIsOnboardingExternally] ) return {children} @@ -83,7 +95,7 @@ export function useOnboardingAuth() { } = useWallet() const ctx = React.useContext(OnboardingAuthContext) if (!ctx) throw new Error('useOnboardingAuth must be used within OnboardingAuthProvider') - const { session } = ctx + const { session, isOnboardingExternally } = ctx const authToken = session?.signed ? session.signed : '' const { @@ -116,7 +128,7 @@ export function useOnboardingAuth() { } }, { - enabled: (!!selectedAccount?.address || !!selectedAddress) && !!authToken, + enabled: (!!selectedAccount?.address || !!selectedAddress || isOnboardingExternally) && !!authToken, retry: 1, } ) @@ -128,6 +140,8 @@ export function useOnboardingAuth() { refetchAuth, isAuthFetched: isFetched, isLoading: ctx.isLoggingIn, + isOnboardingExternally: ctx.isOnboardingExternally, + setIsOnboardingExternally: ctx.setIsOnboardingExternally, } } diff --git a/centrifuge-app/src/components/OnboardingProvider.tsx b/centrifuge-app/src/components/OnboardingProvider.tsx index a7009ee2c8..ddccdbe2ca 100644 --- a/centrifuge-app/src/components/OnboardingProvider.tsx +++ b/centrifuge-app/src/components/OnboardingProvider.tsx @@ -25,6 +25,8 @@ interface OnboardingContextType { previousStep: () => void isLoadingStep: boolean setPool: React.Dispatch> + setIsOnboardingExternally: React.Dispatch> + isOnboardingExternally: boolean } const OnboardingContext = React.createContext | null>(null) @@ -36,7 +38,7 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) evm: { selectedAddress: evmAddress }, connectedType, } = useWallet() - const { isAuth, isAuthFetched, authToken } = useOnboardingAuth() + const { isAuth, isAuthFetched, authToken, setIsOnboardingExternally, isOnboardingExternally } = useOnboardingAuth() const [activeStep, setActiveStep] = React.useState(0) const [pool, setPool] = React.useState() @@ -76,14 +78,14 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) }, { refetchOnWindowFocus: false, - enabled: connectedType === 'evm' ? !!evmAddress : !!substrateAccount, + enabled: (connectedType === 'evm' ? !!evmAddress : !!substrateAccount) || isOnboardingExternally, retry: 1, } ) React.useEffect(() => { // tried to connect but no wallet is connected - if (!isConnecting && !(substrateAccount || evmAddress)) { + if (!isConnecting && !(substrateAccount || evmAddress || isOnboardingExternally)) { return setActiveStep(1) } // wallet finished connection attempt, authentication was attempted, and user is not authenticated @@ -91,8 +93,14 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) return setActiveStep(1) } + const isIframe = window.self !== window.top + // wallet finished connection attempt, user was fetched - if (!isConnecting && isOnboardingUserFetched) { + if ( + !isConnecting && + isOnboardingUserFetched && + (!isIframe || onboardingUser?.globalSteps?.verifyIdentity?.completed) + ) { const isPendingManualKybReview = onboardingUser?.manualKybStatus === 'review.pending' const activeOnboardingStep = getActiveOnboardingStep( @@ -104,7 +112,17 @@ export function OnboardingProvider({ children }: { children: React.ReactNode }) return setActiveStep(activeOnboardingStep) } - }, [onboardingUser, isConnecting, isOnboardingUserFetched, isAuth, isAuthFetched, substrateAccount, evmAddress, pool]) + }, [ + onboardingUser, + isConnecting, + isOnboardingUserFetched, + isAuth, + isAuthFetched, + substrateAccount, + evmAddress, + pool, + isOnboardingExternally, + ]) return ( {children} diff --git a/centrifuge-app/src/pages/Onboarding/CompleteExternalOnboarding.tsx b/centrifuge-app/src/pages/Onboarding/CompleteExternalOnboarding.tsx new file mode 100644 index 0000000000..48e82fdd03 --- /dev/null +++ b/centrifuge-app/src/pages/Onboarding/CompleteExternalOnboarding.tsx @@ -0,0 +1,49 @@ +import { Button } from '@centrifuge/fabric' +import * as React from 'react' +import { ActionBar, Container, Content, ContentHeader, Header, Layout, PoolBranding } from '../../components/Onboarding' +import { useOnboarding } from '../../components/OnboardingProvider' + +type Props = { + openNewTab: () => void + poolId: string | null + poolSymbol?: string +} + +export const CompleteExternalOnboarding = ({ openNewTab, poolId, poolSymbol }: Props) => { + const { refetchOnboardingUser, isOnboardingExternally } = useOnboarding() + + const onFocus = () => { + refetchOnboardingUser() + } + + React.useEffect(() => { + window.addEventListener('focus', onFocus) + + return () => { + window.removeEventListener('focus', onFocus) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return ( + +
+ {!!poolId && } +
+ + + <> + + + + + + + + +
+ ) +} diff --git a/centrifuge-app/src/pages/Onboarding/GlobalStatus.tsx b/centrifuge-app/src/pages/Onboarding/GlobalStatus.tsx index 5fd6fa4b55..8153c6981b 100644 --- a/centrifuge-app/src/pages/Onboarding/GlobalStatus.tsx +++ b/centrifuge-app/src/pages/Onboarding/GlobalStatus.tsx @@ -6,7 +6,7 @@ import { useOnboarding } from '../../components/OnboardingProvider' export const GlobalStatus = () => { const history = useHistory() - const { onboardingUser, refetchOnboardingUser } = useOnboarding() + const { onboardingUser, refetchOnboardingUser, isOnboardingExternally } = useOnboarding() const onFocus = () => { refetchOnboardingUser() @@ -56,19 +56,25 @@ export const GlobalStatus = () => { - - - + {!isOnboardingExternally && ( + + + + )} ) } diff --git a/centrifuge-app/src/pages/Onboarding/LinkWallet.tsx b/centrifuge-app/src/pages/Onboarding/LinkWallet.tsx index 754ecef35a..56a37f0864 100644 --- a/centrifuge-app/src/pages/Onboarding/LinkWallet.tsx +++ b/centrifuge-app/src/pages/Onboarding/LinkWallet.tsx @@ -66,21 +66,28 @@ export const LinkWallet = ({ globalOnboardingStatus }: Props) => { checked={isAuth || formik.values.isAgreedToDataSharingAgreement} disabled={isAuth} errorMessage={formik.errors.isAgreedToDataSharingAgreement} - label={I agree to the} + label={ + + I agree to the + + + } /> - + { + if (externalSignature) { + disconnect() + + const decodedExternalSignature = decodeURIComponent(externalSignature) + + sessionStorage.clear() + sessionStorage.setItem( + 'external-centrifuge-onboarding-auth', + JSON.stringify({ signed: decodedExternalSignature }) + ) + + setIsOnboardingExternally(true) + } + const isTinlakePool = poolId?.startsWith('0x') const trancheName = trancheId?.split('-')[1] === '0' ? 'junior' : 'senior' const canOnboard = isTinlakePool && metadata?.pool?.newInvestmentsStatus?.[trancheName] !== 'closed' @@ -53,9 +83,19 @@ export default function OnboardingPage() { setPool(null) return history.push('/onboarding') - }, [poolId, setPool, trancheId, history, poolDetails, metadata]) + }, [ + poolId, + setPool, + trancheId, + history, + poolDetails, + metadata, + disconnect, + setIsOnboardingExternally, + externalSignature, + ]) - const { data: signedAgreementData, isFetched: isSignedAgreementFetched } = useSignedAgreement() + const { data: signedAgreementData } = useSignedAgreement() React.useEffect(() => { if (onboardingUser?.investorType) { @@ -63,11 +103,28 @@ export default function OnboardingPage() { } }, [onboardingUser?.investorType]) + const isIframe = window.self !== window.top + + const openNewTab = () => { + const origin = window.location.origin + + const encodedExternalSignature = encodeURIComponent(authToken) + + window.open(`${origin}/onboarding?externalSignature=${encodedExternalSignature}`, '_blank') + } + + if (isIframe && isAuth && !onboardingUser?.globalSteps?.verifyIdentity?.completed) { + return + } + return ( -
{!!poolId && }
+
+ {!!poolId && } +