diff --git a/packages/ui/src/containers/SocialSignIn/PrimarySocialSignIn.tsx b/packages/ui/src/containers/SocialSignIn/PrimarySocialSignIn.tsx index 5b8e5375b..7dba293e1 100644 --- a/packages/ui/src/containers/SocialSignIn/PrimarySocialSignIn.tsx +++ b/packages/ui/src/containers/SocialSignIn/PrimarySocialSignIn.tsx @@ -17,7 +17,7 @@ type Props = { const PrimarySocialSignIn = ({ className, isPopup = false, onSocialSignInCallback }: Props) => { const [showAll, setShowAll] = useState(false); - const { invokeSocialSignIn, socialConnectors } = useSocial({ onSocialSignInCallback }); + const { invokeSocialSignIn, socialConnectors } = useSocial(); const isOverSize = socialConnectors.length > defaultSize; const fullDisplay = isPopup || !isOverSize; @@ -37,7 +37,7 @@ const PrimarySocialSignIn = ({ className, isPopup = false, onSocialSignInCallbac className={styles.socialLinkButton} connector={connector} onClick={() => { - void invokeSocialSignIn(connector.id); + void invokeSocialSignIn(connector.id, onSocialSignInCallback); }} /> ))} diff --git a/packages/ui/src/containers/SocialSignIn/SecondarySocialSignIn.test.tsx b/packages/ui/src/containers/SocialSignIn/SecondarySocialSignIn.test.tsx index e5e010458..cfa0bfb6d 100644 --- a/packages/ui/src/containers/SocialSignIn/SecondarySocialSignIn.test.tsx +++ b/packages/ui/src/containers/SocialSignIn/SecondarySocialSignIn.test.tsx @@ -28,7 +28,7 @@ describe('SecondarySocialSignIn', () => { platform: 'web', getPostMessage: jest.fn(() => jest.fn()), callbackLink: '/logto:', - supportedSocialConnectors: socialConnectors.map(({ id }) => id), + supportedSocialConnectorIds: socialConnectors.map(({ id }) => id), }; /* eslint-enable @silverhand/fp/no-mutation */ }); diff --git a/packages/ui/src/hooks/use-api.ts b/packages/ui/src/hooks/use-api.ts index 954598994..66f2946ed 100644 --- a/packages/ui/src/hooks/use-api.ts +++ b/packages/ui/src/hooks/use-api.ts @@ -9,7 +9,7 @@ import { PageContext } from '@/hooks/use-page-context'; type UseApi = { result?: U; error: RequestErrorBody | undefined; - run: (...args: T) => Promise; + run: (...args: T) => Promise; }; export type ErrorHandlers = { @@ -38,6 +38,8 @@ function useApi( try { const result = await api(...args); setResult(result); + + return result; } catch (error: unknown) { if (error instanceof HTTPError && error.response.body) { const kyError = await error.response.json(); diff --git a/packages/ui/src/hooks/use-social.ts b/packages/ui/src/hooks/use-social.ts index d2d5fe770..eac819f36 100644 --- a/packages/ui/src/hooks/use-social.ts +++ b/packages/ui/src/hooks/use-social.ts @@ -23,10 +23,6 @@ type State = { callbackLink?: string; }; -type Options = { - onSocialSignInCallback?: () => void; -}; - const storageKeyPrefix = 'social_auth_state'; const getLogtoNativeSdk = () => { @@ -69,7 +65,7 @@ const isNativeWebview = () => { return ['ios', 'android'].includes(platform); }; -const useSocial = (options?: Options) => { +const useSocial = () => { const { setToast, experienceSettings } = useContext(PageContext); const { termsValidation } = useTerms(); const parameters = useParams(); @@ -86,31 +82,26 @@ const useSocial = (options?: Options) => { }, }); } - setToast(error.message); }, }), - [navigate, parameters.connector, setToast] + [navigate, parameters.connector] ); // Filter native supported social connectors const socialConnectors = useMemo( () => (experienceSettings?.socialConnectors ?? []).filter(({ id }) => { - return !isNativeWebview() || getLogtoNativeSdk()?.supportedSocialConnectors.includes(id); + return !isNativeWebview() || getLogtoNativeSdk()?.supportedSocialConnectorIds.includes(id); }), [experienceSettings?.socialConnectors] ); - const { result: invokeSocialSignInResult, run: asyncInvokeSocialSignIn } = - useApi(invokeSocialSignIn); + const { run: asyncInvokeSocialSignIn } = useApi(invokeSocialSignIn); - const { result: signInWithSocialResult, run: asyncSignInWithSocial } = useApi( - signInWithSocial, - signInWithSocialErrorHandlers - ); + const { run: asyncSignInWithSocial } = useApi(signInWithSocial, signInWithSocialErrorHandlers); const invokeSocialSignInHandler = useCallback( - async (connectorId: string) => { + async (connectorId: string, callback?: () => void) => { if (!termsValidation()) { return; } @@ -120,23 +111,52 @@ const useSocial = (options?: Options) => { const { origin } = window.location; - return asyncInvokeSocialSignIn(connectorId, state, `${origin}/callback/${connectorId}`); + const result = await asyncInvokeSocialSignIn( + connectorId, + state, + `${origin}/callback/${connectorId}` + ); + + if (!result?.redirectTo) { + return; + } + + // Callback hook to close the social sign in modal + callback?.(); + + // Invoke Native Social Sign In flow + if (isNativeWebview()) { + getLogtoNativeSdk()?.getPostMessage()({ + callbackUri: `${origin}/callback/${connectorId}`, + redirectTo: result.redirectTo, + }); + + return; + } + + // Invoke Web Social Sign In flow + window.location.assign(result.redirectTo); }, [asyncInvokeSocialSignIn, termsValidation] ); const signInWithSocialHandler = useCallback( - (connectorId: string, state: string, code: string) => { + async (connectorId: string, state: string, code: string) => { if (!stateValidation(state, connectorId)) { setToast(t('error.invalid_connector_auth')); return; } - void asyncSignInWithSocial({ + + const result = await asyncSignInWithSocial({ connectorId, code, redirectUri: `${origin}/callback/${connectorId}`, }); + + if (result?.redirectTo) { + window.location.assign(result.redirectTo); + } }, [asyncSignInWithSocial, setToast, t] ); @@ -181,38 +201,6 @@ const useSocial = (options?: Options) => { window.location.assign(new URL(`${callbackLink}${window.location.search}`)); }, [parameters.connector, setToast, t]); - // InvokeSocialSignIn Callback - useEffect(() => { - const { redirectTo } = invokeSocialSignInResult ?? {}; - - if (!redirectTo) { - return; - } - - // Callback hook to close the social sign in modal - options?.onSocialSignInCallback?.(); - - // Invoke Native Social Sign In flow - if (isNativeWebview()) { - getLogtoNativeSdk()?.getPostMessage()({ - callbackUri: redirectTo.replace('/callback', '/sign-in/callback'), - redirectTo, - }); - - return; - } - - // Invoke Web Social Sign In flow - window.location.assign(redirectTo); - }, [invokeSocialSignInResult, options]); - - // SignInWithSocial Callback - useEffect(() => { - if (signInWithSocialResult?.redirectTo) { - window.location.assign(signInWithSocialResult.redirectTo); - } - }, [signInWithSocialResult]); - // Social Sign-In Callback Handler useEffect(() => { if (!location.pathname.includes('/sign-in/callback') || !parameters.connector) { @@ -225,7 +213,7 @@ const useSocial = (options?: Options) => { return; } - signInWithSocialHandler(parameters.connector, state, code); + void signInWithSocialHandler(parameters.connector, state, code); }, [parameters.connector, signInWithSocialHandler]); // Monitor Native Error Message diff --git a/packages/ui/src/include.d/global.d.ts b/packages/ui/src/include.d/global.d.ts index c78a937e8..2678ef257 100644 --- a/packages/ui/src/include.d/global.d.ts +++ b/packages/ui/src/include.d/global.d.ts @@ -4,7 +4,7 @@ type LogtoNativeSdkInfo = { platform: 'ios' | 'android'; callbackLink: string; getPostMessage: () => (data: { callbackUri?: string; redirectTo?: string }) => void; - supportedSocialConnectors: string[]; + supportedSocialConnectorIds: string[]; }; declare const logtoNativeSdk: LogtoNativeSdkInfo | undefined;