From 4934920e10f6900efa9a010fbcf86e2a6cc18b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Thu, 18 Dec 2025 13:31:18 +0100 Subject: [PATCH] [MM-66711] Verify push notifications on magic link login (#9322) * [MM-66711] Verify push notifications on magic link login * Address copilot suggestion * Add missing await --------- Co-authored-by: Mattermost Build --- app/actions/remote/session.ts | 16 ++++++++++++++++ app/screens/edit_server/index.tsx | 7 ++++++- app/screens/server/index.tsx | 15 ++++++++++----- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/app/actions/remote/session.ts b/app/actions/remote/session.ts index 9da70139b..64ac81727 100644 --- a/app/actions/remote/session.ts +++ b/app/actions/remote/session.ts @@ -5,6 +5,7 @@ import {defineMessages, type IntlShape} from 'react-intl'; import {Alert, DeviceEventEmitter, type AlertButton} from 'react-native'; import {cancelSessionNotification, findSession} from '@actions/local/session'; +import {doPing} from '@actions/remote/general'; import {Database, Events} from '@constants'; import {SYSTEM_IDENTIFIERS} from '@constants/database'; import DatabaseManager from '@database/manager'; @@ -18,8 +19,10 @@ import {getCurrentUser} from '@queries/servers/user'; import {resetToHome} from '@screens/navigation'; import EphemeralStore from '@store/ephemeral_store'; import {getFullErrorMessage, isErrorWithStatusCode, isErrorWithUrl} from '@utils/errors'; +import {getIntlShape} from '@utils/general'; import {logWarning, logError, logDebug} from '@utils/log'; import {scheduleExpiredNotification} from '@utils/notification'; +import {canReceiveNotifications} from '@utils/push_proxy'; import {type SAMLChallenge} from '@utils/saml_challenge'; import {getCSRFFromCookie} from '@utils/security'; import {getServerUrlAfterRedirect} from '@utils/url'; @@ -468,6 +471,19 @@ export const magicLinkLogin = async (serverUrl: string, token: string): Promise< }); const csrfToken = await getCSRFFromCookie(serverUrlToUse); client.setCSRFToken(csrfToken); + + // Check push notification capability (similar to normal login flow) + const pingResult = await doPing( + serverUrlToUse, + true, // verifyPushProxy + undefined, // timeoutInterval + undefined, // preauthSecret + client, // client + ); + if (!pingResult.error && pingResult.canReceiveNotifications) { + const intl = getIntlShape(user.locale); + await canReceiveNotifications(serverUrlToUse, pingResult.canReceiveNotifications as string, intl); + } } catch (error) { return {error, failed: true}; } diff --git a/app/screens/edit_server/index.tsx b/app/screens/edit_server/index.tsx index 99a0202e1..03aade6c6 100644 --- a/app/screens/edit_server/index.tsx +++ b/app/screens/edit_server/index.tsx @@ -110,7 +110,12 @@ const EditServer = ({closeButtonId, componentId, server, theme}: ServerProps) => } // Then try ping request - use doPing without client to avoid client pollution - const result = await doPing(headRequest.url, true, undefined, secretForValidation); + const result = await doPing( + headRequest.url, // serverUrl + true, // verifyPushProxy + undefined, // timeoutInterval + secretForValidation, // preauthSecret + ); if (result.error) { if (result.isPreauthError) { setPreauthSecretError(formatMessage({ diff --git a/app/screens/server/index.tsx b/app/screens/server/index.tsx index 9a08bef41..12f215aeb 100644 --- a/app/screens/server/index.tsx +++ b/app/screens/server/index.tsx @@ -146,8 +146,8 @@ const Server = ({ handleConnect(managedConfig?.serverUrl || LocalConfig.DefaultServerUrl); } - // functions do not need memoization - // eslint-disable-next-line react-hooks/exhaustive-deps + // We only want to handle connect when a smaller set of variables change + // eslint-disable-next-line react-hooks/exhaustive-deps }, [managedConfig?.allowOtherServers, managedConfig?.serverUrl, managedConfig?.serverName, defaultServerUrl]); useEffect(() => { @@ -189,8 +189,8 @@ const Server = ({ return () => backHandler.remove(); - // only needed on mount - // eslint-disable-next-line react-hooks/exhaustive-deps + // We register the back handler and the push notifications only on mount + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useNavButtonPressed(closeButtonId || '', componentId, dismiss, []); @@ -329,7 +329,12 @@ const Server = ({ } return; } - const result = await doPing(headRequest.url, true, managedConfig?.timeout ? parseInt(managedConfig?.timeout, 10) : undefined, preauthSecret.trim() || undefined); + const result = await doPing( + headRequest.url, + true, // verifyPushProxy + managedConfig?.timeout ? parseInt(managedConfig?.timeout, 10) : undefined, // timeoutInterval + preauthSecret.trim() || undefined, // preauthSecret + ); if (canceled) { return;