From 5f83471295099750b14850b9c0a07d9f993a56ea Mon Sep 17 00:00:00 2001
From: Malte Finsterwalder <malte@holi.team>
Date: Wed, 2 Apr 2025 19:25:43 +0200
Subject: [PATCH] HOLI-10744 fix social login during registration

---
 .../{test_helpers.test.ts => helpers.test.ts} |  0
 core/auth/helpers/auth/common.ts              | 10 ++++++++
 core/auth/helpers/axios.ts                    |  5 +++-
 core/auth/screens/Login.tsx                   | 13 +++-------
 .../onboarding/__tests__/Onboarding.test.tsx  |  1 +
 .../onboarding/steps/CreateAccount.tsx        | 25 +++++++++++++------
 .../steps/__tests__/CreateAccount.test.tsx    | 21 +++++++++++-----
 jest.config.cjs                               |  2 +-
 8 files changed, 53 insertions(+), 24 deletions(-)
 rename core/auth/helpers/__tests__/{test_helpers.test.ts => helpers.test.ts} (100%)

diff --git a/core/auth/helpers/__tests__/test_helpers.test.ts b/core/auth/helpers/__tests__/helpers.test.ts
similarity index 100%
rename from core/auth/helpers/__tests__/test_helpers.test.ts
rename to core/auth/helpers/__tests__/helpers.test.ts
diff --git a/core/auth/helpers/auth/common.ts b/core/auth/helpers/auth/common.ts
index 9cf7893b2b..58c8ac4fe1 100644
--- a/core/auth/helpers/auth/common.ts
+++ b/core/auth/helpers/auth/common.ts
@@ -18,6 +18,10 @@ import {
 } from '@holi/core/auth/helpers/auth/helpers'
 import type { VerificationFlowData, VerificationFlowState } from '@holi/core/auth/helpers/useVerificationFlowData'
 import { HoliError } from '@holi/core/errors/classes/HoliError'
+import { ApolloClient } from '@apollo/client'
+import type { OrySessionContext } from '@holi/core/auth/helpers/sdk'
+import { setLoginStateLoggedIn } from '@holi/core/auth/loginState'
+import { clearGuestMode } from '@holi/core/screens/onboarding/helpers/guestModeAsyncStore'
 
 export const hasIdentifier = (
   loginFlowBody: VerificationFlowData | UpdateLoginFlowWithPasswordMethod
@@ -69,6 +73,12 @@ export const login = async ({
   }
 }
 
+export const doLogin = async (client: ApolloClient<object>, data: OrySessionContext) => {
+  await client.clearStore()
+  await setLoginStateLoggedIn(data)
+  await clearGuestMode()
+}
+
 export const getOrCreateVerificationFlow = async (flowData?: VerificationFlowData) => {
   if (!flowData) throw new HoliError('[Onboarding] no verification flow has been found')
 
diff --git a/core/auth/helpers/axios.ts b/core/auth/helpers/axios.ts
index ff80f1435c..9b6c830a53 100644
--- a/core/auth/helpers/axios.ts
+++ b/core/auth/helpers/axios.ts
@@ -21,7 +21,10 @@ export const resilience = (axios: AxiosInstance) => {
 
       if (
         error.response &&
-        (error.response.status == 400 || error.response.status == 401 || error.response.status == 403)
+        (error.response.status == 400 ||
+          error.response.status == 401 ||
+          error.response.status == 403 ||
+          error.response.status == 422) // 422 is used by Ory for browser redirects
       ) {
         logger.debug('auth', 'network request failed with response code ' + error.response.status, {
           error,
diff --git a/core/auth/screens/Login.tsx b/core/auth/screens/Login.tsx
index 3aff21589c..c9b86f081f 100644
--- a/core/auth/screens/Login.tsx
+++ b/core/auth/screens/Login.tsx
@@ -6,11 +6,11 @@ import { Platform, StyleSheet, View } from 'react-native'
 
 import FormBuilder from '@holi/core/auth/components/FormBuilder'
 import RegistrationButton from '@holi/core/auth/components/RegistrationButton'
-import { doBrowserLogin, hasIdentifier, initLoginFlow, submitLoginFlow } from '@holi/core/auth/helpers/auth'
+import { doBrowserLogin, doLogin, hasIdentifier, initLoginFlow, submitLoginFlow } from '@holi/core/auth/helpers/auth'
 import { handleFormSubmitError, isUnverifiedUserError } from '@holi/core/auth/helpers/form'
 import type { OrySessionContext } from '@holi/core/auth/helpers/sdk'
 import { setVerificationFlowData } from '@holi/core/auth/helpers/useVerificationFlowData'
-import { setLoginStateLoggedIn, useLoginState } from '@holi/core/auth/loginState'
+import { useLoginState } from '@holi/core/auth/loginState'
 import DefaultLayout from '@holi/core/components/DefaultLayout'
 import { HoliTextLinkTracked } from '@holi/core/components/Trackable'
 import { useErrorHandling } from '@holi/core/errors/hooks'
@@ -28,7 +28,6 @@ import HoliLoader, { useLoader } from '@holi/ui/components/molecules/HoliLoader'
 import { Screen } from '@holi/core/components/Screen'
 import { useRedirect } from '@holi/core/navigation/hooks/useRedirect'
 import HoliContainer from '@holi/ui/components/atoms/HoliContainer'
-import { clearGuestMode } from '@holi/core/screens/onboarding/helpers/guestModeAsyncStore'
 import useRouting from '@holi/core/navigation/hooks/useRouting'
 import { getBrowserRedirect } from '@holi/core/auth/helpers/helper'
 
@@ -89,9 +88,7 @@ const Login = () => {
 
     try {
       const { data }: { data: OrySessionContext } = await submitLoginFlow(flow, payload, undefined)
-      await client.clearStore()
-      await setLoginStateLoggedIn(data)
-      await clearGuestMode()
+      await doLogin(client, data)
       storeOnboardingState('finished')
       redirect(true)
     } catch (err) {
@@ -102,9 +99,7 @@ const Login = () => {
         } else {
           const data = await doBrowserLogin(url, flow)
           if (data) {
-            await client.clearStore()
-            await setLoginStateLoggedIn(data)
-            await clearGuestMode()
+            await doLogin(client, data)
             storeOnboardingState('finished')
             redirect(true)
           }
diff --git a/core/screens/onboarding/__tests__/Onboarding.test.tsx b/core/screens/onboarding/__tests__/Onboarding.test.tsx
index e83470a0fe..8443e2190b 100644
--- a/core/screens/onboarding/__tests__/Onboarding.test.tsx
+++ b/core/screens/onboarding/__tests__/Onboarding.test.tsx
@@ -87,6 +87,7 @@ jest.mock('@holi/core/auth/helpers/auth', () => ({
   getOrCreateVerificationFlow: jest.fn(),
   submitVerificationFlow: jest.fn(),
   hasIdentifier: jest.fn(),
+  doLogin: jest.fn(),
 }))
 
 const mockNewKratosSdk = newKratosSdk as jest.Mock
diff --git a/core/screens/onboarding/steps/CreateAccount.tsx b/core/screens/onboarding/steps/CreateAccount.tsx
index b66d11df0d..59f5f77d44 100644
--- a/core/screens/onboarding/steps/CreateAccount.tsx
+++ b/core/screens/onboarding/steps/CreateAccount.tsx
@@ -6,11 +6,16 @@ import { Keyboard, Platform, StyleSheet, View } from 'react-native'
 
 import AlreadyHaveAccountText from '@holi/core/auth/components/AlreadyHaveAccountText'
 import FormBuilder from '@holi/core/auth/components/FormBuilder'
-import { getVerificationFlowFromRegistration, initRegistrationFlow } from '@holi/core/auth/helpers/auth'
+import {
+  doBrowserLogin,
+  doLogin,
+  getVerificationFlowFromRegistration,
+  initRegistrationFlow,
+} from '@holi/core/auth/helpers/auth'
 import { handleFormSubmitError } from '@holi/core/auth/helpers/form'
 import { newKratosSdk, type OrySessionContext } from '@holi/core/auth/helpers/sdk'
 import { setVerificationFlowData } from '@holi/core/auth/helpers/useVerificationFlowData'
-import { setLoginStateLoggedIn, useLoginState } from '@holi/core/auth/loginState'
+import { useLoginState } from '@holi/core/auth/loginState'
 import { ButtonTracked } from '@holi/core/components/Trackable'
 import { HoliOnboardingError } from '@holi/core/errors/classes/HoliOnboardingError'
 import { useErrorHandling } from '@holi/core/errors/hooks'
@@ -26,7 +31,7 @@ import HoliLogo from '@holi/ui/components/atoms/HoliLogo'
 import HoliLoader, { useLoader } from '@holi/ui/components/molecules/HoliLoader'
 import { type HoliTheme, useTheme } from '@holi/ui/styles/theme'
 import { Text } from 'holi-bricks/components/text'
-import { clearGuestMode, storeGuestMode } from '@holi/core/screens/onboarding/helpers/guestModeAsyncStore'
+import { storeGuestMode } from '@holi/core/screens/onboarding/helpers/guestModeAsyncStore'
 import useRouting from '@holi/core/navigation/hooks/useRouting'
 import { getBrowserRedirect } from '@holi/core/auth/helpers/helper'
 
@@ -108,9 +113,7 @@ export const CreateAccount = ({
         const verificationFlowData = getVerificationFlowFromRegistration(data)
         setVerificationFlowData(verificationFlowData)
 
-        await client.clearStore()
-        await clearGuestMode()
-        await setLoginStateLoggedIn({
+        await doLogin(client, {
           session: data.session,
           session_token: data.session_token,
         } as OrySessionContext)
@@ -119,7 +122,15 @@ export const CreateAccount = ({
       } catch (err) {
         const url = getBrowserRedirect(err)
         if (url) {
-          navigate(url)
+          if (Platform.OS === 'web') {
+            navigate(url)
+          } else {
+            const data = await doBrowserLogin(url, flow)
+            if (data) {
+              await doLogin(client, data)
+              onRegister()
+            }
+          }
         } else {
           handleFormSubmitError(
             setFlow,
diff --git a/core/screens/onboarding/steps/__tests__/CreateAccount.test.tsx b/core/screens/onboarding/steps/__tests__/CreateAccount.test.tsx
index 33a51d3e46..4f0520e262 100644
--- a/core/screens/onboarding/steps/__tests__/CreateAccount.test.tsx
+++ b/core/screens/onboarding/steps/__tests__/CreateAccount.test.tsx
@@ -1,5 +1,10 @@
 import { MockedProvider } from '@apollo/client/testing'
-import { getVerificationFlowFromRegistration, initRegistrationFlow } from '@holi/core/auth/helpers/auth'
+import {
+  doBrowserLogin,
+  doLogin,
+  getVerificationFlowFromRegistration,
+  initRegistrationFlow,
+} from '@holi/core/auth/helpers/auth'
 import { setLoginStateLoggedIn, useLoginState } from '@holi/core/auth/loginState'
 import { useErrorHandling } from '@holi/core/errors/hooks'
 import { CreateAccount, type CreateAccountProps } from '@holi/core/screens/onboarding/steps/CreateAccount'
@@ -10,8 +15,9 @@ import { TrackingEvent } from '@holi/core/tracking'
 import useTracking from '@holi/core/tracking/hooks/useTracking'
 import { newKratosSdk } from '@holi/core/auth/helpers/sdk'
 import { setVerificationFlowData } from '@holi/core/auth/helpers/useVerificationFlowData'
-import { clearGuestMode, storeGuestMode } from '@holi/core/screens/onboarding/helpers/guestModeAsyncStore'
+import { storeGuestMode } from '@holi/core/screens/onboarding/helpers/guestModeAsyncStore'
 import useRouting from '@holi/core/navigation/hooks/useRouting'
+import { ApolloClient } from '@apollo/client'
 
 const mockUseErrorHandling = useErrorHandling as jest.Mock
 jest.mock('@holi/core/errors/hooks', () => ({
@@ -33,9 +39,13 @@ jest.mock('@holi/core/auth/loginState', () => ({
 
 const mockInitRegistrationFlow = initRegistrationFlow as jest.Mock
 const mockGetVerificationFlowFromRegistration = getVerificationFlowFromRegistration as jest.Mock
+const mockDoBrowserLogin = doBrowserLogin as jest.Mock
+const mockDoLogin = doLogin as jest.Mock
 jest.mock('@holi/core/auth/helpers/auth', () => ({
   initRegistrationFlow: jest.fn(),
   getVerificationFlowFromRegistration: jest.fn(),
+  doBrowserLogin: jest.fn(),
+  doLogin: jest.fn(),
 }))
 
 const mockSetVerificationFlowData = setVerificationFlowData as unknown as jest.Mock
@@ -48,7 +58,6 @@ jest.mock('@holi/core/auth/helpers/sdk', () => ({
   newKratosSdk: jest.fn(),
 }))
 
-const mockClearGuestMode = clearGuestMode as jest.Mock
 const mockStoreGuestMode = storeGuestMode as jest.Mock
 jest.mock('@holi/core/screens/onboarding/helpers/guestModeAsyncStore', () => ({
   clearGuestMode: jest.fn(),
@@ -165,7 +174,7 @@ describe('CreateAccount', () => {
     it('should clear guest mode', async () => {
       await submitForm()
 
-      expect(mockClearGuestMode).toHaveBeenCalled()
+      expect(mockDoLogin).toHaveBeenCalled()
     })
 
     it('should handle misconfiguration', async () => {
@@ -206,7 +215,7 @@ describe('CreateAccount', () => {
 
       await submitForm()
 
-      expect(navigate).toHaveBeenCalledWith('someUrl')
+      expect(mockDoBrowserLogin).toHaveBeenCalledWith('someUrl', registrationFlow)
     })
 
     it('should set verification flow data', async () => {
@@ -218,7 +227,7 @@ describe('CreateAccount', () => {
     it('should set the login state', async () => {
       await submitForm()
 
-      expect(mockSetLoginStateLoggedIn).toHaveBeenCalledWith(registrationResponse)
+      expect(mockDoLogin).toHaveBeenCalledWith(expect.any(ApolloClient), registrationResponse)
     })
 
     it('should call onRegister callback', async () => {
diff --git a/jest.config.cjs b/jest.config.cjs
index 8a2a3b4a4f..a9f78e1074 100644
--- a/jest.config.cjs
+++ b/jest.config.cjs
@@ -9,7 +9,7 @@ module.exports = {
   },
   moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
   modulePathIgnorePatterns: ['e2e', 'testData', 'mocks.ts', 'TestBed.tsx', 'testutils.ts'],
-  testPathIgnorePatterns: ['/node_modules/', '__tests__/helpers'],
+  testPathIgnorePatterns: ['/node_modules/', '__tests__/helpers.ts', '__tests__/helpers/.*'],
   moduleNameMapper: {
     '\\.(css|scss)$': '<rootDir>/jest.styleMock.cjs',
     // Force module uuid to resolve with the CJS entry point, because Jest does not support package.json.exports. See https://github.com/uuidjs/uuid/issues/451
-- 
GitLab