From 4068d5b480c9f6eaf37dcc9644caac0f57c2312b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Wed, 20 Mar 2024 15:31:40 +0100 Subject: [PATCH] chore(web): prevent from adding U2F if 2FA is not enabled (#2861) --- .../Preferences/Panes/Security/Security.tsx | 19 ++++++++++++++----- .../Panes/Security/TwoFactorAuth/MfaProps.ts | 2 ++ .../Security/TwoFactorAuth/TwoFactorAuth.ts | 4 ++++ .../TwoFactorAuth/TwoFactorAuthWrapper.tsx | 8 +++----- .../Panes/Security/U2F/U2FProps.ts | 1 + .../Security/U2F/U2FView/U2FDescription.tsx | 9 ++++++++- .../Panes/Security/U2F/U2FView/U2FView.tsx | 7 ++++--- .../Panes/Security/U2F/U2FWrapper.tsx | 2 +- .../Preferences/PreferencesProps.tsx | 3 +-- 9 files changed, 38 insertions(+), 17 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 492087439..66e8d1430 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/Security.tsx @@ -1,9 +1,8 @@ import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs' +import { FunctionComponent, useState } from 'react' import { WebApplication } from '@/Application/WebApplication' -import { FunctionComponent } from 'react' import TwoFactorAuthWrapper from './TwoFactorAuth/TwoFactorAuthWrapper' -import { MfaProps } from './TwoFactorAuth/MfaProps' import Encryption from './Encryption' import PasscodeLock from './PasscodeLock' import Privacy from './Privacy' @@ -13,13 +12,23 @@ import PreferencesPane from '@/Components/Preferences/PreferencesComponents/Pref 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' -interface SecurityProps extends MfaProps { +interface SecurityProps { application: WebApplication } const Security: FunctionComponent = (props) => { const isNativeMobileWeb = props.application.isNativeMobileWeb() + const [is2FAEnabled, setIs2FAEnabled] = useState(false) + + const [auth] = useState( + () => + new TwoFactorAuth(props.application.sessions, props.application.mfa, (status) => + setIs2FAEnabled(checkIf2FAIsEnabled(status)), + ), + ) + auth.fetchStatus() const isU2FFeatureAvailable = props.application.features.getFeatureStatus( @@ -31,8 +40,8 @@ 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 81eb2ba9c..2fb45cd76 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 @@ -1,5 +1,7 @@ import { WebApplication } from '@/Application/WebApplication' +import { TwoFactorAuth } from './TwoFactorAuth' export interface MfaProps { application: WebApplication + auth: TwoFactorAuth } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuth.ts b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuth.ts index 6c34f7264..c087256b9 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuth.ts +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/TwoFactorAuth/TwoFactorAuth.ts @@ -19,6 +19,7 @@ export class TwoFactorAuth { constructor( private readonly sessions: SessionsClientInterface, private readonly mfa: MfaServiceInterface, + private callback?: (status: TwoFactorStatus) => void, ) { this._errorMessage = null @@ -90,6 +91,9 @@ export class TwoFactorAuth { action((active) => { this._status = active ? 'two-factor-enabled' : 'two-factor-disabled' this.setError(null) + if (this.callback) { + this.callback(this._status) + } }), ) .catch( 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 43457e063..5e575beab 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 @@ -1,12 +1,10 @@ -import { FunctionComponent, useState } from 'react' +import { FunctionComponent } from 'react' + import { MfaProps } from './MfaProps' -import { TwoFactorAuth } from './TwoFactorAuth' import TwoFactorAuthView from './TwoFactorAuthView/TwoFactorAuthView' const TwoFactorAuthWrapper: FunctionComponent = (props) => { - const [auth] = useState(() => new TwoFactorAuth(props.application.sessions, props.application.mfa)) - auth.fetchStatus() - 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 60092ecba..20f0ab8e6 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 @@ -2,4 +2,5 @@ import { WebApplication } from '@/Application/WebApplication' export interface U2FProps { application: WebApplication + is2FAEnabled: boolean } diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FDescription.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FDescription.tsx index e0df3c352..04ce3de06 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FDescription.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Security/U2F/U2FView/U2FDescription.tsx @@ -4,7 +4,11 @@ import { observer } from 'mobx-react-lite' import { Text } from '@/Components/Preferences/PreferencesComponents/Content' import { useApplication } from '@/Components/ApplicationProvider' -const U2FDescription: FunctionComponent = () => { +type Props = { + is2FAEnabled: boolean +} + +const U2FDescription: FunctionComponent = ({ is2FAEnabled }) => { const application = useApplication() if (application.sessions.getUser() === undefined) { @@ -17,6 +21,9 @@ const U2FDescription: FunctionComponent = () => { {!application.isFullU2FClient && ( Please visit the web app in order to add a hardware security key. )} + {!is2FAEnabled && ( + You must enable two-factor authentication before adding a hardware security key. + )} ) } 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 d2cb1205b..cbaa42750 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 @@ -15,9 +15,10 @@ import RecoveryCodeBanner from '@/Components/RecoveryCodeBanner/RecoveryCodeBann type Props = { application: WebApplication + is2FAEnabled: boolean } -const U2FView: FunctionComponent = ({ application }) => { +const U2FView: FunctionComponent = ({ application, is2FAEnabled }) => { const [showDeviceAddingModal, setShowDeviceAddingModal] = useState(false) const [devices, setDevices] = useState>([]) const [error, setError] = useState('') @@ -47,7 +48,7 @@ const U2FView: FunctionComponent = ({ application }) => {
- +
@@ -60,7 +61,7 @@ const U2FView: FunctionComponent = ({ application }) => { />