From a937a2bb7a8897afd2fe21eaaff5896c3e897522 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 24 Oct 2025 11:22:29 +0200 Subject: [PATCH 01/12] Prepare setup to remove WebView from SAML implementation --- package-lock.json | 223 ++++++++++++++++++ package.json | 2 + src/App.tsx | 3 + .../signin/SAMLSignInPage/index.native.tsx | 105 +++++---- 4 files changed, 286 insertions(+), 47 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97b480338a9b..c1cf5e1346a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,8 +70,10 @@ "expo-font": "^13.0.4", "expo-image": "3.0.8", "expo-image-manipulator": "^13.1.5", + "expo-linking": "^8.0.8", "expo-modules-core": "2.4.0", "expo-secure-store": "~14.2.4", + "expo-web-browser": "14.2.0", "fast-equals": "^5.2.2", "focus-trap-react": "^11.0.3", "group-ib-fp": "file:modules/group-ib-fp", @@ -21481,6 +21483,217 @@ "react": "*" } }, + "node_modules/expo-linking": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.8.tgz", + "integrity": "sha512-MyeMcbFDKhXh4sDD1EHwd0uxFQNAc6VCrwBkNvvvufUsTYFq3glTA9Y8a+x78CPpjNqwNAamu74yIaIz7IEJyg==", + "license": "MIT", + "dependencies": { + "expo-constants": "~18.0.8", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-linking/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/expo-linking/node_modules/@expo/config": { + "version": "12.0.10", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.10.tgz", + "integrity": "sha512-lJMof5Nqakq1DxGYlghYB/ogSBjmv4Fxn1ovyDmcjlRsQdFCXgu06gEUogkhPtc9wBt9WlTTfqENln5HHyLW6w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "@expo/config-plugins": "~54.0.2", + "@expo/config-types": "^54.0.8", + "@expo/json-file": "^10.0.7", + "deepmerge": "^4.3.1", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4", + "sucrase": "3.35.0" + } + }, + "node_modules/expo-linking/node_modules/@expo/config-plugins": { + "version": "54.0.2", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.2.tgz", + "integrity": "sha512-jD4qxFcURQUVsUFGMcbo63a/AnviK8WUGard+yrdQE3ZrB/aurn68SlApjirQQLEizhjI5Ar2ufqflOBlNpyPg==", + "license": "MIT", + "dependencies": { + "@expo/config-types": "^54.0.8", + "@expo/json-file": "~10.0.7", + "@expo/plist": "^0.4.7", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^2.0.0", + "glob": "^10.4.2", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/expo-linking/node_modules/@expo/config-types": { + "version": "54.0.8", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.8.tgz", + "integrity": "sha512-lyIn/x/Yz0SgHL7IGWtgTLg6TJWC9vL7489++0hzCHZ4iGjVcfZmPTUfiragZ3HycFFj899qN0jlhl49IHa94A==", + "license": "MIT" + }, + "node_modules/expo-linking/node_modules/@expo/env": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.7.tgz", + "integrity": "sha512-BNETbLEohk3HQ2LxwwezpG8pq+h7Fs7/vAMP3eAtFT1BCpprLYoBBFZH7gW4aqGfqOcVP4Lc91j014verrYNGg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "debug": "^4.3.4", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "getenv": "^2.0.0" + } + }, + "node_modules/expo-linking/node_modules/@expo/json-file": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.7.tgz", + "integrity": "sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" + } + }, + "node_modules/expo-linking/node_modules/@expo/plist": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.7.tgz", + "integrity": "sha512-dGxqHPvCZKeRKDU1sJZMmuyVtcASuSYh1LPFVaM1DuffqPL36n6FMEL0iUqq2Tx3xhWk8wCnWl34IKplUjJDdA==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.2.3", + "xmlbuilder": "^15.1.1" + } + }, + "node_modules/expo-linking/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expo-linking/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expo-linking/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expo-linking/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/expo-linking/node_modules/expo-constants": { + "version": "18.0.9", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", + "integrity": "sha512-sqoXHAOGDcr+M9NlXzj1tGoZyd3zxYDy215W6E0Z0n8fgBaqce9FAYQE2bu5X4G629AYig5go7U6sQz7Pjcm8A==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.9", + "@expo/env": "~2.0.7" + }, + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, + "node_modules/expo-linking/node_modules/getenv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", + "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/expo-linking/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expo-linking/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/expo-linking/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/expo-modules-autolinking": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.11.tgz", @@ -21596,6 +21809,16 @@ "expo": "*" } }, + "node_modules/expo-web-browser": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-14.2.0.tgz", + "integrity": "sha512-6S51d8pVlDRDsgGAp8BPpwnxtyKiMWEFdezNz+5jVIyT+ctReW42uxnjRgtsdn5sXaqzhaX+Tzk/CWaKCyC0hw==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, "node_modules/expo/node_modules/@expo/fingerprint": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.13.0.tgz", diff --git a/package.json b/package.json index 8544222e793c..4f73eda73017 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,9 @@ "expo-image": "3.0.8", "expo-image-manipulator": "^13.1.5", "expo-modules-core": "2.4.0", + "expo-linking": "^8.0.8", "expo-secure-store": "~14.2.4", + "expo-web-browser": "14.2.0", "fast-equals": "^5.2.2", "focus-trap-react": "^11.0.3", "group-ib-fp": "file:modules/group-ib-fp", diff --git a/src/App.tsx b/src/App.tsx index abd15d47c8fe..46503625d348 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,5 @@ import {PortalProvider} from '@gorhom/portal'; +import {maybeCompleteAuthSession} from 'expo-web-browser'; import React from 'react'; import {LogBox, View} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; @@ -64,6 +65,8 @@ const fill = {flex: 1}; const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children; +maybeCompleteAuthSession(); +// function App() { useDefaultDragAndDrop(); OnyxUpdateManager(); diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index 77baf3565713..42cdeaf962b8 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -1,6 +1,6 @@ -import React, {useCallback, useEffect, useRef, useState} from 'react'; -import WebView from 'react-native-webview'; -import type {WebViewNativeEvent} from 'react-native-webview/lib/WebViewTypes'; +import type {WebBrowserAuthSessionResult} from 'expo-web-browser'; +import {openAuthSessionAsync} from 'expo-web-browser'; +import React, {useCallback, useEffect, useState} from 'react'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import SAMLLoadingIndicator from '@components/SAMLLoadingIndicator'; @@ -8,7 +8,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import getPlatform from '@libs/getPlatform'; -import getUAForWebView from '@libs/getUAForWebView'; import Log from '@libs/Log'; import {handleSAMLLoginError, postSAMLLogin} from '@libs/LoginUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -17,14 +16,66 @@ import CONFIG from '@src/CONFIG'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +// TODO +// 1. See if Linking.addEventListener is needed (according to the Expo docs it might be if we don't use expo-router) to dismiss the browser +// 2. In handleNavigationStateChange parse incoming JSON instead of URLSearchParams +// 3. Explore better solutions for handling the incoming SAMLUrl + function SAMLSignInPage() { const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS, {canBeMissing: false}); const [showNavigation, shouldShowNavigation] = useState(true); const [SAMLUrl, setSAMLUrl] = useState(''); - const webViewRef = useRef(null); const {translate} = useLocalize(); + const handleNavigationStateChange = useCallback( + (url: string) => { + // If we've gotten a callback then remove the option to navigate back to the sign-in page + if (url.includes('loginCallback')) { + shouldShowNavigation(false); + } + + const searchParams = new URLSearchParams(new URL(url).search); + const shortLivedAuthToken = searchParams.get('shortLivedAuthToken'); + if (!account?.isLoading && credentials?.login && !!shortLivedAuthToken) { + Log.info('SAMLSignInPage - Successfully received shortLivedAuthToken. Signing in...'); + signInWithShortLivedAuthToken(shortLivedAuthToken, true); + } + + // If the login attempt is unsuccessful, set the error message for the account and redirect to sign in page + if (searchParams.has('error')) { + clearSignInData(); + setAccountError(searchParams.get('error') ?? ''); + + Navigation.isNavigationReady().then(() => { + // We must call goBack() to remove the /transition route from history + Navigation.goBack(); + Navigation.navigate(ROUTES.HOME); + }); + } + }, + [credentials?.login, shouldShowNavigation, account?.isLoading], + ); + + useEffect(() => { + if (!SAMLUrl) { + return; + } + // const subscription = Linking.addEventListener('url', (url) => { + // console.log('triggered', url); + // // dismissBrowser(); + // }); + openAuthSessionAsync(SAMLUrl, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { + if (response.type !== 'success') { + return; + } + handleNavigationStateChange(response.url); + }); + return () => { + // subscription.remove(); + }; + }, [SAMLUrl, handleNavigationStateChange]); + useEffect(() => { // If we don't have a valid login to pass here, direct the user back to a clean sign in state to try again if (!credentials?.login) { @@ -41,6 +92,7 @@ function SAMLSignInPage() { body.append('email', credentials.login); body.append('referer', CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER); body.append('platform', getPlatform()); + body.append('useBrowser', 'true'); postSAMLLogin(body) .then((response) => { if (!response || !response.url) { @@ -57,34 +109,6 @@ function SAMLSignInPage() { /** * Handles in-app navigation once we get a response back from Expensify */ - const handleNavigationStateChange = useCallback( - ({url}: WebViewNativeEvent) => { - // If we've gotten a callback then remove the option to navigate back to the sign-in page - if (url.includes('loginCallback')) { - shouldShowNavigation(false); - } - - const searchParams = new URLSearchParams(new URL(url).search); - const shortLivedAuthToken = searchParams.get('shortLivedAuthToken'); - if (!account?.isLoading && credentials?.login && !!shortLivedAuthToken) { - Log.info('SAMLSignInPage - Successfully received shortLivedAuthToken. Signing in...'); - signInWithShortLivedAuthToken(shortLivedAuthToken, true); - } - - // If the login attempt is unsuccessful, set the error message for the account and redirect to sign in page - if (searchParams.has('error')) { - clearSignInData(); - setAccountError(searchParams.get('error') ?? ''); - - Navigation.isNavigationReady().then(() => { - // We must call goBack() to remove the /transition route from history - Navigation.goBack(); - Navigation.navigate(ROUTES.HOME); - }); - } - }, - [credentials?.login, shouldShowNavigation, account?.isLoading], - ); return ( )} - {!SAMLUrl ? ( - - ) : ( - } - onNavigationStateChange={handleNavigationStateChange} - /> - )} + ); From 6a9b8babd34e115e6de19195c2b90353a59d5b9c Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 24 Oct 2025 11:43:51 +0200 Subject: [PATCH 02/12] Remove expo-linking and maybeCompleteAuthSession --- package-lock.json | 212 ---------------------------------------------- package.json | 1 - src/App.tsx | 3 - 3 files changed, 216 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1cf5e1346a3..928316f8a3e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,7 +70,6 @@ "expo-font": "^13.0.4", "expo-image": "3.0.8", "expo-image-manipulator": "^13.1.5", - "expo-linking": "^8.0.8", "expo-modules-core": "2.4.0", "expo-secure-store": "~14.2.4", "expo-web-browser": "14.2.0", @@ -21483,217 +21482,6 @@ "react": "*" } }, - "node_modules/expo-linking": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.8.tgz", - "integrity": "sha512-MyeMcbFDKhXh4sDD1EHwd0uxFQNAc6VCrwBkNvvvufUsTYFq3glTA9Y8a+x78CPpjNqwNAamu74yIaIz7IEJyg==", - "license": "MIT", - "dependencies": { - "expo-constants": "~18.0.8", - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/expo-linking/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/expo-linking/node_modules/@expo/config": { - "version": "12.0.10", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.10.tgz", - "integrity": "sha512-lJMof5Nqakq1DxGYlghYB/ogSBjmv4Fxn1ovyDmcjlRsQdFCXgu06gEUogkhPtc9wBt9WlTTfqENln5HHyLW6w==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "@expo/config-plugins": "~54.0.2", - "@expo/config-types": "^54.0.8", - "@expo/json-file": "^10.0.7", - "deepmerge": "^4.3.1", - "getenv": "^2.0.0", - "glob": "^10.4.2", - "require-from-string": "^2.0.2", - "resolve-from": "^5.0.0", - "resolve-workspace-root": "^2.0.0", - "semver": "^7.6.0", - "slugify": "^1.3.4", - "sucrase": "3.35.0" - } - }, - "node_modules/expo-linking/node_modules/@expo/config-plugins": { - "version": "54.0.2", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.2.tgz", - "integrity": "sha512-jD4qxFcURQUVsUFGMcbo63a/AnviK8WUGard+yrdQE3ZrB/aurn68SlApjirQQLEizhjI5Ar2ufqflOBlNpyPg==", - "license": "MIT", - "dependencies": { - "@expo/config-types": "^54.0.8", - "@expo/json-file": "~10.0.7", - "@expo/plist": "^0.4.7", - "@expo/sdk-runtime-versions": "^1.0.0", - "chalk": "^4.1.2", - "debug": "^4.3.5", - "getenv": "^2.0.0", - "glob": "^10.4.2", - "resolve-from": "^5.0.0", - "semver": "^7.5.4", - "slash": "^3.0.0", - "slugify": "^1.6.6", - "xcode": "^3.0.1", - "xml2js": "0.6.0" - } - }, - "node_modules/expo-linking/node_modules/@expo/config-types": { - "version": "54.0.8", - "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.8.tgz", - "integrity": "sha512-lyIn/x/Yz0SgHL7IGWtgTLg6TJWC9vL7489++0hzCHZ4iGjVcfZmPTUfiragZ3HycFFj899qN0jlhl49IHa94A==", - "license": "MIT" - }, - "node_modules/expo-linking/node_modules/@expo/env": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.7.tgz", - "integrity": "sha512-BNETbLEohk3HQ2LxwwezpG8pq+h7Fs7/vAMP3eAtFT1BCpprLYoBBFZH7gW4aqGfqOcVP4Lc91j014verrYNGg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "debug": "^4.3.4", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "getenv": "^2.0.0" - } - }, - "node_modules/expo-linking/node_modules/@expo/json-file": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-10.0.7.tgz", - "integrity": "sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "~7.10.4", - "json5": "^2.2.3" - } - }, - "node_modules/expo-linking/node_modules/@expo/plist": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.7.tgz", - "integrity": "sha512-dGxqHPvCZKeRKDU1sJZMmuyVtcASuSYh1LPFVaM1DuffqPL36n6FMEL0iUqq2Tx3xhWk8wCnWl34IKplUjJDdA==", - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.2.3", - "xmlbuilder": "^15.1.1" - } - }, - "node_modules/expo-linking/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/expo-linking/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/expo-linking/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/expo-linking/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/expo-linking/node_modules/expo-constants": { - "version": "18.0.9", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", - "integrity": "sha512-sqoXHAOGDcr+M9NlXzj1tGoZyd3zxYDy215W6E0Z0n8fgBaqce9FAYQE2bu5X4G629AYig5go7U6sQz7Pjcm8A==", - "license": "MIT", - "dependencies": { - "@expo/config": "~12.0.9", - "@expo/env": "~2.0.7" - }, - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, - "node_modules/expo-linking/node_modules/getenv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", - "integrity": "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/expo-linking/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/expo-linking/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/expo-linking/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/expo-modules-autolinking": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.11.tgz", diff --git a/package.json b/package.json index 4f73eda73017..e593fef25a3a 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,6 @@ "expo-image": "3.0.8", "expo-image-manipulator": "^13.1.5", "expo-modules-core": "2.4.0", - "expo-linking": "^8.0.8", "expo-secure-store": "~14.2.4", "expo-web-browser": "14.2.0", "fast-equals": "^5.2.2", diff --git a/src/App.tsx b/src/App.tsx index 46503625d348..abd15d47c8fe 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,4 @@ import {PortalProvider} from '@gorhom/portal'; -import {maybeCompleteAuthSession} from 'expo-web-browser'; import React from 'react'; import {LogBox, View} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; @@ -65,8 +64,6 @@ const fill = {flex: 1}; const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children; -maybeCompleteAuthSession(); -// function App() { useDefaultDragAndDrop(); OnyxUpdateManager(); From 6a0b38baf412ffe71fef551ca48f19fa8e1e203a Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 24 Oct 2025 11:59:07 +0200 Subject: [PATCH 03/12] Fix comments --- src/pages/signin/SAMLSignInPage/index.native.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index 42cdeaf962b8..4be42c69c554 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -19,7 +19,9 @@ import ROUTES from '@src/ROUTES'; // TODO // 1. See if Linking.addEventListener is needed (according to the Expo docs it might be if we don't use expo-router) to dismiss the browser // 2. In handleNavigationStateChange parse incoming JSON instead of URLSearchParams -// 3. Explore better solutions for handling the incoming SAMLUrl +// 3. Explore better solutions for handling the incoming SAMLUrl (does the effect run too many times?) +// 4. Test the signout flow +// 5. Add error handling for openAuthSessionAsync function SAMLSignInPage() { const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); @@ -28,6 +30,10 @@ function SAMLSignInPage() { const [SAMLUrl, setSAMLUrl] = useState(''); const {translate} = useLocalize(); + /** + * Handles in-app navigation once we get a response back from Expensify + */ + const handleNavigationStateChange = useCallback( (url: string) => { // If we've gotten a callback then remove the option to navigate back to the sign-in page @@ -106,10 +112,6 @@ function SAMLSignInPage() { }); }, [credentials?.login, SAMLUrl, translate]); - /** - * Handles in-app navigation once we get a response back from Expensify - */ - return ( Date: Wed, 29 Oct 2025 17:25:43 +0100 Subject: [PATCH 04/12] Do not open auth session more than once per component life --- .../signin/SAMLSignInPage/index.native.tsx | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index 4be42c69c554..237d643efce5 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -1,6 +1,6 @@ import type {WebBrowserAuthSessionResult} from 'expo-web-browser'; -import {openAuthSessionAsync} from 'expo-web-browser'; -import React, {useCallback, useEffect, useState} from 'react'; +import {dismissAuthSession, openAuthSessionAsync} from 'expo-web-browser'; +import React, {useCallback, useEffect, useRef, useState} from 'react'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import SAMLLoadingIndicator from '@components/SAMLLoadingIndicator'; @@ -29,11 +29,11 @@ function SAMLSignInPage() { const [showNavigation, shouldShowNavigation] = useState(true); const [SAMLUrl, setSAMLUrl] = useState(''); const {translate} = useLocalize(); + const hasOpenedAuthSession = useRef(false); /** * Handles in-app navigation once we get a response back from Expensify */ - const handleNavigationStateChange = useCallback( (url: string) => { // If we've gotten a callback then remove the option to navigate back to the sign-in page @@ -42,16 +42,36 @@ function SAMLSignInPage() { } const searchParams = new URLSearchParams(new URL(url).search); - const shortLivedAuthToken = searchParams.get('shortLivedAuthToken'); + const jsonParam = searchParams.get('json'); + + if (!jsonParam) { + Log.hmmm('SAMLSignInPage - No JSON parameter found in callback URL'); + return; + } + + let shortLivedAuthToken: string | null = null; + let error: string | null = null; + + try { + const decodedData = JSON.parse(jsonParam) as Record; + shortLivedAuthToken = decodedData.shortLivedAuthToken ?? null; + error = decodedData.error ?? null; + Log.info('SAMLSignInPage - Parsed data from JSON parameter'); + } catch { + // We need to come generate translation for message indicating parsing error + Log.hmmm(`SAMLSignInPage - Failed to parse JSON parameter`); + error = 'Failed to parse JSON'; + } + if (!account?.isLoading && credentials?.login && !!shortLivedAuthToken) { Log.info('SAMLSignInPage - Successfully received shortLivedAuthToken. Signing in...'); signInWithShortLivedAuthToken(shortLivedAuthToken, true); + return; } - // If the login attempt is unsuccessful, set the error message for the account and redirect to sign in page - if (searchParams.has('error')) { + if (error) { clearSignInData(); - setAccountError(searchParams.get('error') ?? ''); + setAccountError(error); Navigation.isNavigationReady().then(() => { // We must call goBack() to remove the /transition route from history @@ -64,13 +84,15 @@ function SAMLSignInPage() { ); useEffect(() => { - if (!SAMLUrl) { + // Don't open auth session more than once. If user cancels it we should navigate back to ROUTES.HOME + if (!SAMLUrl || hasOpenedAuthSession.current) { return; } // const subscription = Linking.addEventListener('url', (url) => { // console.log('triggered', url); // // dismissBrowser(); // }); + hasOpenedAuthSession.current = true openAuthSessionAsync(SAMLUrl, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { if (response.type !== 'success') { return; From 573ac744c58f3f5b133fe0e8f2f9fc05b74f4058 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 14 Nov 2025 13:29:50 +0100 Subject: [PATCH 05/12] Remove comments --- src/pages/signin/SAMLSignInPage/index.native.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index 237d643efce5..adfa47870adf 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -16,13 +16,6 @@ import CONFIG from '@src/CONFIG'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -// TODO -// 1. See if Linking.addEventListener is needed (according to the Expo docs it might be if we don't use expo-router) to dismiss the browser -// 2. In handleNavigationStateChange parse incoming JSON instead of URLSearchParams -// 3. Explore better solutions for handling the incoming SAMLUrl (does the effect run too many times?) -// 4. Test the signout flow -// 5. Add error handling for openAuthSessionAsync - function SAMLSignInPage() { const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false}); const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS, {canBeMissing: false}); @@ -88,10 +81,6 @@ function SAMLSignInPage() { if (!SAMLUrl || hasOpenedAuthSession.current) { return; } - // const subscription = Linking.addEventListener('url', (url) => { - // console.log('triggered', url); - // // dismissBrowser(); - // }); hasOpenedAuthSession.current = true openAuthSessionAsync(SAMLUrl, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { if (response.type !== 'success') { From ad93a6ae50e1dca98f25c0a2f6884fb0405592dd Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 19 Nov 2025 09:48:53 +0100 Subject: [PATCH 06/12] Remove unnecessary return from a useEffect --- Mobile-Expensify | 2 +- src/pages/signin/SAMLSignInPage/index.native.tsx | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 54feda018414..7771e81a17ff 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 54feda018414778cb81bedf971f55c45167c3c00 +Subproject commit 7771e81a17ff09f89996ada50fd923014c479ceb diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index adfa47870adf..34c0e6d3b113 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -88,9 +88,6 @@ function SAMLSignInPage() { } handleNavigationStateChange(response.url); }); - return () => { - // subscription.remove(); - }; }, [SAMLUrl, handleNavigationStateChange]); useEffect(() => { From 75e781a5fce53b941ff0db2e7365226306c1ff93 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 19 Nov 2025 14:29:14 +0100 Subject: [PATCH 07/12] Add callSAMLSignOut with expo-web-browser --- src/libs/actions/Session/index.ts | 20 +++++++++++++++++++ .../signin/SAMLSignInPage/index.native.tsx | 2 +- src/types/onyx/Session.ts | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 9b5e0618ee0e..dde2a9358a4f 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -1,4 +1,6 @@ import HybridAppModule from '@expensify/react-native-hybrid-app'; +import type {WebBrowserAuthSessionResult} from 'expo-web-browser'; +import {openAuthSessionAsync} from 'expo-web-browser'; import throttle from 'lodash/throttle'; import type {ChannelAuthorizationData} from 'pusher-js/types/src/core/auth/options'; import type {ChannelAuthorizationCallback} from 'pusher-js/with-encryption'; @@ -66,6 +68,7 @@ import type Locale from '@src/types/onyx/Locale'; import type Response from '@src/types/onyx/Response'; import type Session from '@src/types/onyx/Session'; import type {AutoAuthState} from '@src/types/onyx/Session'; +import pkg from '../../../../package.json'; import clearCache from './clearCache'; import updateSessionAuthTokens from './updateSessionAuthTokens'; @@ -163,6 +166,7 @@ function getShortLivedLoginParams(isSupportAuthTokenUsed = false, isSAML = false key: ONYXKEYS.SESSION, value: { signedInWithShortLivedAuthToken: true, + signedInWithSAML: isSAML, isAuthenticatingWithShortLivedToken: true, isSupportAuthTokenUsed, }, @@ -183,6 +187,7 @@ function getShortLivedLoginParams(isSupportAuthTokenUsed = false, isSAML = false key: ONYXKEYS.SESSION, value: { signedInWithShortLivedAuthToken: null, + signedInWithSAML: isSAML, isSupportAuthTokenUsed: null, isAuthenticatingWithShortLivedToken: false, }, @@ -237,10 +242,25 @@ function signOut(): Promise { skipReauthentication: true, }; + if (session.signedInWithSAML) { + return callSAMLSignOut(params); + } // eslint-disable-next-line rulesdir/no-api-side-effects-method return API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.LOG_OUT, params, {}); } +function callSAMLSignOut(params: LogOutParams): Promise { + const queryString = `appversion=${pkg.version}&referer=ecash&authToken=${session.authToken}`; + return openAuthSessionAsync(`https://www.expensify.com/authentication/saml/logout?${queryString}`, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { + // if (response.type !== 'success') { + // return; + // } + + // eslint-disable-next-line rulesdir/no-api-side-effects-method + API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.LOG_OUT, params, {}); + }); +} + /** * Checks if the account is an anonymous account. */ diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index 34c0e6d3b113..a575be2f314c 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -1,5 +1,5 @@ import type {WebBrowserAuthSessionResult} from 'expo-web-browser'; -import {dismissAuthSession, openAuthSessionAsync} from 'expo-web-browser'; +import {openAuthSessionAsync} from 'expo-web-browser'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; diff --git a/src/types/onyx/Session.ts b/src/types/onyx/Session.ts index 5b662764f221..9988590e7912 100644 --- a/src/types/onyx/Session.ts +++ b/src/types/onyx/Session.ts @@ -37,6 +37,9 @@ type Session = { /** User signed in with short lived token */ signedInWithShortLivedAuthToken?: boolean; + /** User signed in with SAML */ + signedInWithSAML?: boolean; + /** Indicates whether the user is re-authenticating with shortLivedToken */ isAuthenticatingWithShortLivedToken?: boolean; From 546f662aebf53ec7f6a4482a67ae1c28de47d8d4 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 20 Nov 2025 15:05:40 +0100 Subject: [PATCH 08/12] Cleanup callSAMLSignOut --- src/libs/actions/Session/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index dde2a9358a4f..d69e39cece9e 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -251,11 +251,7 @@ function signOut(): Promise { function callSAMLSignOut(params: LogOutParams): Promise { const queryString = `appversion=${pkg.version}&referer=ecash&authToken=${session.authToken}`; - return openAuthSessionAsync(`https://www.expensify.com/authentication/saml/logout?${queryString}`, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { - // if (response.type !== 'success') { - // return; - // } - + return openAuthSessionAsync(`${CONST.EXPENSIFY_URL}/authentication/saml/logout?${queryString}`).then(() => { // eslint-disable-next-line rulesdir/no-api-side-effects-method API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.LOG_OUT, params, {}); }); From a7f1e13d1398440552cbd90333120c4c95a51a79 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 20 Nov 2025 17:15:04 +0100 Subject: [PATCH 09/12] Fix linter and prettier --- src/libs/actions/Session/index.ts | 1 - src/pages/signin/SAMLSignInPage/index.native.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index d69e39cece9e..b3446e8cc314 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -1,5 +1,4 @@ import HybridAppModule from '@expensify/react-native-hybrid-app'; -import type {WebBrowserAuthSessionResult} from 'expo-web-browser'; import {openAuthSessionAsync} from 'expo-web-browser'; import throttle from 'lodash/throttle'; import type {ChannelAuthorizationData} from 'pusher-js/types/src/core/auth/options'; diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index a575be2f314c..d900e366098e 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -81,7 +81,7 @@ function SAMLSignInPage() { if (!SAMLUrl || hasOpenedAuthSession.current) { return; } - hasOpenedAuthSession.current = true + hasOpenedAuthSession.current = true; openAuthSessionAsync(SAMLUrl, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { if (response.type !== 'success') { return; From 2360864dae960e71b68f7a7922443be6777af993 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 20 Nov 2025 17:31:10 +0100 Subject: [PATCH 10/12] Navigate back when dismissing the browser --- src/pages/signin/SAMLSignInPage/index.native.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index d900e366098e..c799bf716730 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -84,6 +84,7 @@ function SAMLSignInPage() { hasOpenedAuthSession.current = true; openAuthSessionAsync(SAMLUrl, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { if (response.type !== 'success') { + Navigation.goBack() return; } handleNavigationStateChange(response.url); From d41cc41d2758a9bce736031022b4d649d6dd81ea Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 20 Nov 2025 17:37:10 +0100 Subject: [PATCH 11/12] Update submodule --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 7771e81a17ff..2cf05d2cf021 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 7771e81a17ff09f89996ada50fd923014c479ceb +Subproject commit 2cf05d2cf021eacfed5fa91856c5e73e97a159a7 From dd7042faec0f733a557bc5582670bbff8f632b12 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Fri, 21 Nov 2025 13:06:26 +0100 Subject: [PATCH 12/12] Extract SAML_REDIRECT_URL to CONST --- src/CONST/index.ts | 1 + src/pages/signin/SAMLSignInPage/index.native.tsx | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 0daa5ee140fb..f523f83c11a6 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -952,6 +952,7 @@ const CONST = { GOOGLE_DOC_IMAGE_LINK_MATCH: 'googleusercontent.com', IMAGE_BASE64_MATCH: 'base64', DEEPLINK_BASE_URL: 'new-expensify://', + SAML_REDIRECT_URL: 'expensify://open', PDF_VIEWER_URL: '/pdf/web/viewer.html', CLOUDFRONT_DOMAIN_REGEX: /^https:\/\/\w+\.cloudfront\.net/i, EXPENSIFY_ICON_URL: `${CLOUDFRONT_URL}/images/favicon-2019.png`, diff --git a/src/pages/signin/SAMLSignInPage/index.native.tsx b/src/pages/signin/SAMLSignInPage/index.native.tsx index c799bf716730..e7ec9aaa17f0 100644 --- a/src/pages/signin/SAMLSignInPage/index.native.tsx +++ b/src/pages/signin/SAMLSignInPage/index.native.tsx @@ -13,6 +13,7 @@ import {handleSAMLLoginError, postSAMLLogin} from '@libs/LoginUtils'; import Navigation from '@libs/Navigation/Navigation'; import {clearSignInData, setAccountError, signInWithShortLivedAuthToken} from '@userActions/Session'; import CONFIG from '@src/CONFIG'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -82,9 +83,9 @@ function SAMLSignInPage() { return; } hasOpenedAuthSession.current = true; - openAuthSessionAsync(SAMLUrl, 'expensify://open').then((response: WebBrowserAuthSessionResult) => { + openAuthSessionAsync(SAMLUrl, CONST.SAML_REDIRECT_URL).then((response: WebBrowserAuthSessionResult) => { if (response.type !== 'success') { - Navigation.goBack() + Navigation.goBack(); return; } handleNavigationStateChange(response.url);