From b1309282d34fc768be8da169601bfff33c918de2 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Fri, 25 Oct 2024 15:11:19 -0400 Subject: [PATCH 1/2] prevent onboarding initiation if already logged in Bug: If already logged in and an nrelopenpath:// link is launched (either via "JOIN" button or external QR scan), onboarding will re-initiate. Storage from the first login is not cleared before attempting a second login and that could cause major issues. Fix: Prevent onboarding if onboarding has already been initiated. When handling an external URL launch we first check the onboarding state to see if the user has already initiated onboarding (i.e. gotten past "WELCOME"). If so, we'll popup with a message. I also renamed `handleOpenURL` in the AppContext. cordova-plugin-customurlscheme requires a function with this name on the window object, which is why I named it that before. However we use that function to handle raw tokens too; not just URLs, so it is a misnomer. Renamed it to `handleTokenOrUrl`; then defined window.handleOpenURL as a function that calls `handleTokenOrUrl` with a joinMethod of 'external'. This allows customurlscheme to work while keeping our code descriptive + readable. --- www/i18n/en.json | 3 ++- www/js/App.tsx | 28 +++++++++++++++++++++------- www/js/onboarding/WelcomePage.tsx | 8 ++++---- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/www/i18n/en.json b/www/i18n/en.json index ec432d89e..6be5a0dac 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -405,7 +405,8 @@ "dont-force-kill": "Do not force kill the app", "background-restrictions": "On Samsung and Huwaei phones, make sure that background restrictions are turned off", "close": "Close", - "proceeding-with-token": "Proceeding with OPcode: {{token}}" + "proceeding-with-token": "Proceeding with OPcode: {{token}}", + "already-logged-in": "You are already logged in with OPcode {{token}}. If you wish to use a different OPcode, please log out first." }, "config": { "unable-read-saved-config": "Unable to read saved config", diff --git a/www/js/App.tsx b/www/js/App.tsx index 328e7ab29..2f7850d15 100644 --- a/www/js/App.tsx +++ b/www/js/App.tsx @@ -18,6 +18,8 @@ import AlertBar from './components/AlertBar'; import Main from './Main'; import { joinWithTokenOrUrl } from './config/dynamicConfig'; import { addStatReading } from './plugin/clientStats'; +import { displayErrorMsg, logDebug } from './plugin/logger'; +import i18next from 'i18next'; export const AppContext = createContext({}); const CUSTOM_LABEL_KEYS_IN_DATABASE = ['mode', 'purpose']; @@ -34,21 +36,33 @@ const App = () => { const appConfig = useAppConfig(); const permissionStatus = usePermissionStatus(); - const refreshOnboardingState = () => getPendingOnboardingState().then(setOnboardingState); + const refreshOnboardingState = () => + getPendingOnboardingState().then((state) => { + setOnboardingState(state); + return state; + }); + useEffect(() => { refreshOnboardingState(); }, []); - // handleOpenURL function must be provided globally for cordova-plugin-customurlscheme - // https://www.npmjs.com/package/cordova-plugin-customurlscheme - window['handleOpenURL'] = async (url: string, joinMethod: OnboardingJoinMethod = 'external') => { - const configUpdated = await joinWithTokenOrUrl(url); + async function handleTokenOrUrl(tokenOrUrl: string, joinMethod: OnboardingJoinMethod) { + const onboardingState = await refreshOnboardingState(); + logDebug(`handleTokenOrUrl: onboardingState = ${JSON.stringify(onboardingState)}`); + if (onboardingState.route > OnboardingRoute.WELCOME) { + displayErrorMsg(i18next.t('join.already-logged-in', { token: onboardingState.opcode })); + return; + } + const configUpdated = await joinWithTokenOrUrl(tokenOrUrl); addStatReading('onboard', { configUpdated, joinMethod }); if (configUpdated) { refreshOnboardingState(); } return configUpdated; - }; + } + // handleOpenURL function must be provided globally for cordova-plugin-customurlscheme + // https://www.npmjs.com/package/cordova-plugin-customurlscheme + window['handleOpenURL'] = (url: string) => handleTokenOrUrl(url, 'external'); useEffect(() => { if (!appConfig) return; @@ -63,7 +77,7 @@ const App = () => { const appContextValue = { appConfig, - handleOpenURL: window['handleOpenURL'], + handleTokenOrUrl, onboardingState, setOnboardingState, refreshOnboardingState, diff --git a/www/js/onboarding/WelcomePage.tsx b/www/js/onboarding/WelcomePage.tsx index 2698df5d4..ff8214578 100644 --- a/www/js/onboarding/WelcomePage.tsx +++ b/www/js/onboarding/WelcomePage.tsx @@ -34,7 +34,7 @@ const WelcomePage = () => { const { t } = useTranslation(); const { colors } = useTheme(); const { width: windowWidth } = useWindowDimensions(); - const { handleOpenURL } = useContext(AppContext); + const { handleTokenOrUrl } = useContext(AppContext); const [pasteModalVis, setPasteModalVis] = useState(false); const [infoPopupVis, setInfoPopupVis] = useState(false); const [existingToken, setExistingToken] = useState(''); @@ -52,7 +52,7 @@ const WelcomePage = () => { AlertManager.addMessage({ text: 'No QR code found in scan. Please try again.' }); return; } - handleOpenURL(result.text, 'scan'); + handleTokenOrUrl(result.text, 'scan'); }, (error) => { barcodeScannerIsOpen = false; @@ -68,7 +68,7 @@ const WelcomePage = () => { if (!clipboardContent?.startsWith('nrelop_') && !clipboardContent?.includes('://')) { throw new Error('Clipboard content is not a valid token or URL'); } - handleOpenURL(clipboardContent, 'paste'); + handleTokenOrUrl(clipboardContent, 'paste'); } catch (e) { logWarn(`Tried using clipboard content ${clipboardContent}: ${e}`); setPasteModalVis(true); @@ -141,7 +141,7 @@ const WelcomePage = () => {