From 9a5ed4bdd291a586cbccffb9dcb87b9d3ac7c440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Thu, 21 Mar 2024 17:26:15 +0100 Subject: [PATCH 1/5] chore: block disabling 2FA if U2F authenticators present --- .../Preferences/Panes/Security/Security.tsx | 17 +++++++++++++++-- .../Panes/Security/TwoFactorAuth/MfaProps.ts | 1 + .../TwoFactorAuthView/TwoFactorAuthView.tsx | 5 +++-- .../TwoFactorAuthView/TwoFactorSwitch.tsx | 7 +++++-- .../TwoFactorAuth/TwoFactorAuthWrapper.tsx | 8 +++++++- .../Preferences/Panes/Security/U2F/U2FProps.ts | 1 + .../Panes/Security/U2F/U2FView/U2FView.tsx | 9 ++++++--- .../Panes/Security/U2F/U2FWrapper.tsx | 8 +++++++- 8 files changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx index 66e8d1430f6..e81ae53f99a 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx @@ -21,6 +21,7 @@ interface SecurityProps { const Security: FunctionComponent = (props) => { const isNativeMobileWeb = props.application.isNativeMobileWeb() const [is2FAEnabled, setIs2FAEnabled] = useState(false) + const [isDisabling2FAEnabled, setIsDisabling2FAEnabled] = useState(true) const [auth] = useState( () => @@ -30,6 +31,12 @@ const Security: FunctionComponent = (props) => { ) auth.fetchStatus() + const onU2FDevicesLoaded = (devices: Array<{ id: string; name: string }>) => { + if (devices.length > 0) { + setIsDisabling2FAEnabled(false) + } + } + const isU2FFeatureAvailable = props.application.features.getFeatureStatus( NativeFeatureIdentifier.create(NativeFeatureIdentifier.TYPES.UniversalSecondFactor).getValue(), @@ -40,8 +47,14 @@ const Security: FunctionComponent = (props) => { {props.application.items.invalidNonVaultedItems.length > 0 && } - - {isU2FFeatureAvailable && } + + {isU2FFeatureAvailable && ( + + )} {isNativeMobileWeb && } {isNativeMobileWeb && } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/MfaProps.ts b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/MfaProps.ts index 2fb45cd76cb..a1080f6e2e4 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/MfaProps.ts +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/MfaProps.ts @@ -4,4 +4,5 @@ import { TwoFactorAuth } from './TwoFactorAuth' export interface MfaProps { application: WebApplication auth: TwoFactorAuth + isDisabling2FAEnabled: boolean } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorAuthView.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorAuthView.tsx index 28001198c78..5e73f4e617a 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorAuthView.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorAuthView.tsx @@ -16,9 +16,10 @@ import ModalOverlay from '@/Components/Modal/ModalOverlay' type Props = { auth: TwoFactorAuth application: WebApplication + isDisabling2FAEnabled: boolean } -const TwoFactorAuthView: FunctionComponent = ({ auth, application }) => { +const TwoFactorAuthView: FunctionComponent = ({ auth, application, isDisabling2FAEnabled }) => { const shouldShowActivationModal = auth.status !== 'fetching' && is2FAActivation(auth.status) const activationModalTitle = shouldShowActivationModal @@ -96,7 +97,7 @@ const TwoFactorAuthView: FunctionComponent = ({ auth, application }) => { - + diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx index eecfb8b99cf..900c606a917 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx @@ -6,9 +6,10 @@ import Spinner from '@/Components/Spinner/Spinner' type Props = { auth: TwoFactorAuth + isDisabling2FAEnabled: boolean } -const TwoFactorSwitch: FunctionComponent = ({ auth }) => { +const TwoFactorSwitch: FunctionComponent = ({ auth, isDisabling2FAEnabled }) => { if (!auth.isLoggedIn()) { return null } @@ -17,7 +18,9 @@ const TwoFactorSwitch: FunctionComponent = ({ auth }) => { return } - return + const shouldSwitchBeDisabled = auth.status === 'two-factor-enabled' && !isDisabling2FAEnabled + + return } export default observer(TwoFactorSwitch) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx index 5e575beab62..6bd1b8beabf 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx @@ -4,7 +4,13 @@ import { MfaProps } from './MfaProps' import TwoFactorAuthView from './TwoFactorAuthView/TwoFactorAuthView' const TwoFactorAuthWrapper: FunctionComponent = (props) => { - return + return ( + + ) } export default TwoFactorAuthWrapper diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FProps.ts b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FProps.ts index 20f0ab8e63e..644a6db7d64 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FProps.ts +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FProps.ts @@ -3,4 +3,5 @@ import { WebApplication } from '@/Application/WebApplication' export interface U2FProps { application: WebApplication is2FAEnabled: boolean + loadAuthenticatorsCallback: (devices: Array<{ id: string; name: string }>) => void } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FView.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FView.tsx index cbaa42750af..e997e669849 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FView.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FView.tsx @@ -16,9 +16,10 @@ import RecoveryCodeBanner from '@/Components/RecoveryCodeBanner/RecoveryCodeBann type Props = { application: WebApplication is2FAEnabled: boolean + loadAuthenticatorsCallback: (devices: Array<{ id: string; name: string }>) => void } -const U2FView: FunctionComponent = ({ application, is2FAEnabled }) => { +const U2FView: FunctionComponent = ({ application, is2FAEnabled, loadAuthenticatorsCallback }) => { const [showDeviceAddingModal, setShowDeviceAddingModal] = useState(false) const [devices, setDevices] = useState>([]) const [error, setError] = useState('') @@ -35,8 +36,10 @@ const U2FView: FunctionComponent = ({ application, is2FAEnabled }) => { return } - setDevices(authenticatorListOrError.getValue()) - }, [setError, setDevices, application]) + const authenticatorList = authenticatorListOrError.getValue() + setDevices(authenticatorList) + loadAuthenticatorsCallback(authenticatorList) + }, [setError, setDevices, application, loadAuthenticatorsCallback]) useEffect(() => { loadAuthenticatorDevices().catch(console.error) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx index d8d098e76ea..31367f8c017 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx @@ -4,7 +4,13 @@ import { U2FProps } from './U2FProps' import U2FView from './U2FView/U2FView' const U2FWrapper: FunctionComponent = (props) => { - return + return ( + + ) } export default U2FWrapper From 13e95b7d77775b2fb53694c4ee6479ee5497ec4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Fri, 22 Mar 2024 08:09:44 +0100 Subject: [PATCH 2/5] chore: remove the u2f wrapper component --- .../Preferences/Panes/Security/Security.tsx | 4 ++-- .../Preferences/Panes/Security/U2F/U2FProps.ts | 7 ------- .../Panes/Security/U2F/U2FWrapper.tsx | 16 ---------------- 3 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FProps.ts delete mode 100644 packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx index e81ae53f99a..9a778d0d3b1 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx @@ -11,8 +11,8 @@ import ErroredItems from './ErroredItems' import PreferencesPane from '@/Components/Preferences/PreferencesComponents/PreferencesPane' import BiometricsLock from '@/Components/Preferences/Panes/Security/BiometricsLock' import MultitaskingPrivacy from '@/Components/Preferences/Panes/Security/MultitaskingPrivacy' -import U2FWrapper from './U2F/U2FWrapper' import { TwoFactorAuth, is2FAEnabled as checkIf2FAIsEnabled } from './TwoFactorAuth/TwoFactorAuth' +import U2FView from './U2F/U2FView/U2FView' interface SecurityProps { application: WebApplication @@ -49,7 +49,7 @@ const Security: FunctionComponent = (props) => { {isU2FFeatureAvailable && ( - ) => void -} diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx deleted file mode 100644 index 31367f8c017..00000000000 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FWrapper.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { FunctionComponent } from 'react' - -import { U2FProps } from './U2FProps' -import U2FView from './U2FView/U2FView' - -const U2FWrapper: FunctionComponent = (props) => { - return ( - - ) -} - -export default U2FWrapper From dd63d6c41c27c2f69e56244ff6a2b8b35f7c76ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Fri, 22 Mar 2024 08:23:24 +0100 Subject: [PATCH 3/5] chore: adjust fetching 2fa status --- .../Preferences/Panes/Security/Security.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx index 9a778d0d3b1..70f34c4f7d7 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx @@ -1,5 +1,5 @@ import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs' -import { FunctionComponent, useState } from 'react' +import { FunctionComponent, useEffect, useState } from 'react' import { WebApplication } from '@/Application/WebApplication' import TwoFactorAuthWrapper from './TwoFactorAuth/TwoFactorAuthWrapper' @@ -29,12 +29,13 @@ const Security: FunctionComponent = (props) => { setIs2FAEnabled(checkIf2FAIsEnabled(status)), ), ) - auth.fetchStatus() + + useEffect(() => { + auth.fetchStatus() + }, [auth]) const onU2FDevicesLoaded = (devices: Array<{ id: string; name: string }>) => { - if (devices.length > 0) { - setIsDisabling2FAEnabled(false) - } + setIsDisabling2FAEnabled(devices.length === 0) } const isU2FFeatureAvailable = From 6ff74ee8885414218e5e8616297f303ca386898a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Fri, 22 Mar 2024 09:14:41 +0100 Subject: [PATCH 4/5] chore: fix property name --- .../Components/Preferences/Panes/Security/Security.tsx | 6 +++--- .../Preferences/Panes/Security/TwoFactorAuth/MfaProps.ts | 2 +- .../TwoFactorAuth/TwoFactorAuthView/TwoFactorAuthView.tsx | 6 +++--- .../TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx | 6 +++--- .../Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx index 70f34c4f7d7..12184f9affe 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx @@ -21,7 +21,7 @@ interface SecurityProps { const Security: FunctionComponent = (props) => { const isNativeMobileWeb = props.application.isNativeMobileWeb() const [is2FAEnabled, setIs2FAEnabled] = useState(false) - const [isDisabling2FAEnabled, setIsDisabling2FAEnabled] = useState(true) + const [canDisable2FA, setCanDisable2FA] = useState(true) const [auth] = useState( () => @@ -35,7 +35,7 @@ const Security: FunctionComponent = (props) => { }, [auth]) const onU2FDevicesLoaded = (devices: Array<{ id: string; name: string }>) => { - setIsDisabling2FAEnabled(devices.length === 0) + setCanDisable2FA(devices.length === 0) } const isU2FFeatureAvailable = @@ -48,7 +48,7 @@ const Security: FunctionComponent = (props) => { {props.application.items.invalidNonVaultedItems.length > 0 && } - + {isU2FFeatureAvailable && ( = ({ auth, application, isDisabling2FAEnabled }) => { +const TwoFactorAuthView: FunctionComponent = ({ auth, application, canDisable2FA }) => { const shouldShowActivationModal = auth.status !== 'fetching' && is2FAActivation(auth.status) const activationModalTitle = shouldShowActivationModal @@ -97,7 +97,7 @@ const TwoFactorAuthView: FunctionComponent = ({ auth, application, isDisa - + diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx index 900c606a917..dab2a2bda94 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthView/TwoFactorSwitch.tsx @@ -6,10 +6,10 @@ import Spinner from '@/Components/Spinner/Spinner' type Props = { auth: TwoFactorAuth - isDisabling2FAEnabled: boolean + canDisable2FA: boolean } -const TwoFactorSwitch: FunctionComponent = ({ auth, isDisabling2FAEnabled }) => { +const TwoFactorSwitch: FunctionComponent = ({ auth, canDisable2FA }) => { if (!auth.isLoggedIn()) { return null } @@ -18,7 +18,7 @@ const TwoFactorSwitch: FunctionComponent = ({ auth, isDisabling2FAEnabled return } - const shouldSwitchBeDisabled = auth.status === 'two-factor-enabled' && !isDisabling2FAEnabled + const shouldSwitchBeDisabled = auth.status === 'two-factor-enabled' && !canDisable2FA return } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx index 6bd1b8beabf..84495bd0ca1 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx @@ -8,7 +8,7 @@ const TwoFactorAuthWrapper: FunctionComponent = (props) => { ) } From 2b5412bf185f868844daf57948988b88d255bfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Fri, 22 Mar 2024 09:19:34 +0100 Subject: [PATCH 5/5] chore: remove wrapper --- .../Preferences/Panes/Security/Security.tsx | 4 ++-- .../Panes/Security/TwoFactorAuth/MfaProps.ts | 8 -------- .../TwoFactorAuth/TwoFactorAuthWrapper.tsx | 16 ---------------- 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/MfaProps.ts delete mode 100644 packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuthWrapper.tsx diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx index 12184f9affe..6ed50686405 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx @@ -2,7 +2,6 @@ import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs' import { FunctionComponent, useEffect, useState } from 'react' import { WebApplication } from '@/Application/WebApplication' -import TwoFactorAuthWrapper from './TwoFactorAuth/TwoFactorAuthWrapper' import Encryption from './Encryption' import PasscodeLock from './PasscodeLock' import Privacy from './Privacy' @@ -13,6 +12,7 @@ import BiometricsLock from '@/Components/Preferences/Panes/Security/BiometricsLo import MultitaskingPrivacy from '@/Components/Preferences/Panes/Security/MultitaskingPrivacy' import { TwoFactorAuth, is2FAEnabled as checkIf2FAIsEnabled } from './TwoFactorAuth/TwoFactorAuth' import U2FView from './U2F/U2FView/U2FView' +import TwoFactorAuthView from './TwoFactorAuth/TwoFactorAuthView/TwoFactorAuthView' interface SecurityProps { application: WebApplication @@ -48,7 +48,7 @@ const Security: FunctionComponent = (props) => { {props.application.items.invalidNonVaultedItems.length > 0 && } - + {isU2FFeatureAvailable && ( = (props) => { - return ( - - ) -} - -export default TwoFactorAuthWrapper