chore(web): prevent from adding U2F if 2FA is not enabled (#2861)
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs'
|
import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs'
|
||||||
|
import { FunctionComponent, useState } from 'react'
|
||||||
|
|
||||||
import { WebApplication } from '@/Application/WebApplication'
|
import { WebApplication } from '@/Application/WebApplication'
|
||||||
import { FunctionComponent } from 'react'
|
|
||||||
import TwoFactorAuthWrapper from './TwoFactorAuth/TwoFactorAuthWrapper'
|
import TwoFactorAuthWrapper from './TwoFactorAuth/TwoFactorAuthWrapper'
|
||||||
import { MfaProps } from './TwoFactorAuth/MfaProps'
|
|
||||||
import Encryption from './Encryption'
|
import Encryption from './Encryption'
|
||||||
import PasscodeLock from './PasscodeLock'
|
import PasscodeLock from './PasscodeLock'
|
||||||
import Privacy from './Privacy'
|
import Privacy from './Privacy'
|
||||||
@@ -13,13 +12,23 @@ import PreferencesPane from '@/Components/Preferences/PreferencesComponents/Pref
|
|||||||
import BiometricsLock from '@/Components/Preferences/Panes/Security/BiometricsLock'
|
import BiometricsLock from '@/Components/Preferences/Panes/Security/BiometricsLock'
|
||||||
import MultitaskingPrivacy from '@/Components/Preferences/Panes/Security/MultitaskingPrivacy'
|
import MultitaskingPrivacy from '@/Components/Preferences/Panes/Security/MultitaskingPrivacy'
|
||||||
import U2FWrapper from './U2F/U2FWrapper'
|
import U2FWrapper from './U2F/U2FWrapper'
|
||||||
|
import { TwoFactorAuth, is2FAEnabled as checkIf2FAIsEnabled } from './TwoFactorAuth/TwoFactorAuth'
|
||||||
|
|
||||||
interface SecurityProps extends MfaProps {
|
interface SecurityProps {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
}
|
}
|
||||||
|
|
||||||
const Security: FunctionComponent<SecurityProps> = (props) => {
|
const Security: FunctionComponent<SecurityProps> = (props) => {
|
||||||
const isNativeMobileWeb = props.application.isNativeMobileWeb()
|
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 =
|
const isU2FFeatureAvailable =
|
||||||
props.application.features.getFeatureStatus(
|
props.application.features.getFeatureStatus(
|
||||||
@@ -31,8 +40,8 @@ const Security: FunctionComponent<SecurityProps> = (props) => {
|
|||||||
<Encryption />
|
<Encryption />
|
||||||
{props.application.items.invalidNonVaultedItems.length > 0 && <ErroredItems />}
|
{props.application.items.invalidNonVaultedItems.length > 0 && <ErroredItems />}
|
||||||
<Protections application={props.application} />
|
<Protections application={props.application} />
|
||||||
<TwoFactorAuthWrapper application={props.application} />
|
<TwoFactorAuthWrapper auth={auth} application={props.application} />
|
||||||
{isU2FFeatureAvailable && <U2FWrapper application={props.application} />}
|
{isU2FFeatureAvailable && <U2FWrapper application={props.application} is2FAEnabled={is2FAEnabled} />}
|
||||||
{isNativeMobileWeb && <MultitaskingPrivacy application={props.application} />}
|
{isNativeMobileWeb && <MultitaskingPrivacy application={props.application} />}
|
||||||
<PasscodeLock application={props.application} />
|
<PasscodeLock application={props.application} />
|
||||||
{isNativeMobileWeb && <BiometricsLock application={props.application} />}
|
{isNativeMobileWeb && <BiometricsLock application={props.application} />}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { WebApplication } from '@/Application/WebApplication'
|
import { WebApplication } from '@/Application/WebApplication'
|
||||||
|
import { TwoFactorAuth } from './TwoFactorAuth'
|
||||||
|
|
||||||
export interface MfaProps {
|
export interface MfaProps {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
|
auth: TwoFactorAuth
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export class TwoFactorAuth {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly sessions: SessionsClientInterface,
|
private readonly sessions: SessionsClientInterface,
|
||||||
private readonly mfa: MfaServiceInterface,
|
private readonly mfa: MfaServiceInterface,
|
||||||
|
private callback?: (status: TwoFactorStatus) => void,
|
||||||
) {
|
) {
|
||||||
this._errorMessage = null
|
this._errorMessage = null
|
||||||
|
|
||||||
@@ -90,6 +91,9 @@ export class TwoFactorAuth {
|
|||||||
action((active) => {
|
action((active) => {
|
||||||
this._status = active ? 'two-factor-enabled' : 'two-factor-disabled'
|
this._status = active ? 'two-factor-enabled' : 'two-factor-disabled'
|
||||||
this.setError(null)
|
this.setError(null)
|
||||||
|
if (this.callback) {
|
||||||
|
this.callback(this._status)
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.catch(
|
.catch(
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { FunctionComponent, useState } from 'react'
|
import { FunctionComponent } from 'react'
|
||||||
|
|
||||||
import { MfaProps } from './MfaProps'
|
import { MfaProps } from './MfaProps'
|
||||||
import { TwoFactorAuth } from './TwoFactorAuth'
|
|
||||||
import TwoFactorAuthView from './TwoFactorAuthView/TwoFactorAuthView'
|
import TwoFactorAuthView from './TwoFactorAuthView/TwoFactorAuthView'
|
||||||
|
|
||||||
const TwoFactorAuthWrapper: FunctionComponent<MfaProps> = (props) => {
|
const TwoFactorAuthWrapper: FunctionComponent<MfaProps> = (props) => {
|
||||||
const [auth] = useState(() => new TwoFactorAuth(props.application.sessions, props.application.mfa))
|
return <TwoFactorAuthView auth={props.auth} application={props.application} />
|
||||||
auth.fetchStatus()
|
|
||||||
return <TwoFactorAuthView auth={auth} application={props.application} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TwoFactorAuthWrapper
|
export default TwoFactorAuthWrapper
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ import { WebApplication } from '@/Application/WebApplication'
|
|||||||
|
|
||||||
export interface U2FProps {
|
export interface U2FProps {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
|
is2FAEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import { observer } from 'mobx-react-lite'
|
|||||||
import { Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
import { Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||||
import { useApplication } from '@/Components/ApplicationProvider'
|
import { useApplication } from '@/Components/ApplicationProvider'
|
||||||
|
|
||||||
const U2FDescription: FunctionComponent = () => {
|
type Props = {
|
||||||
|
is2FAEnabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const U2FDescription: FunctionComponent<Props> = ({ is2FAEnabled }) => {
|
||||||
const application = useApplication()
|
const application = useApplication()
|
||||||
|
|
||||||
if (application.sessions.getUser() === undefined) {
|
if (application.sessions.getUser() === undefined) {
|
||||||
@@ -17,6 +21,9 @@ const U2FDescription: FunctionComponent = () => {
|
|||||||
{!application.isFullU2FClient && (
|
{!application.isFullU2FClient && (
|
||||||
<Text className="italic">Please visit the web app in order to add a hardware security key.</Text>
|
<Text className="italic">Please visit the web app in order to add a hardware security key.</Text>
|
||||||
)}
|
)}
|
||||||
|
{!is2FAEnabled && (
|
||||||
|
<Text className="italic">You must enable two-factor authentication before adding a hardware security key.</Text>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ import RecoveryCodeBanner from '@/Components/RecoveryCodeBanner/RecoveryCodeBann
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
|
is2FAEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const U2FView: FunctionComponent<Props> = ({ application }) => {
|
const U2FView: FunctionComponent<Props> = ({ application, is2FAEnabled }) => {
|
||||||
const [showDeviceAddingModal, setShowDeviceAddingModal] = useState(false)
|
const [showDeviceAddingModal, setShowDeviceAddingModal] = useState(false)
|
||||||
const [devices, setDevices] = useState<Array<{ id: string; name: string }>>([])
|
const [devices, setDevices] = useState<Array<{ id: string; name: string }>>([])
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
@@ -47,7 +48,7 @@ const U2FView: FunctionComponent<Props> = ({ application }) => {
|
|||||||
<PreferencesSegment>
|
<PreferencesSegment>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<U2FTitle />
|
<U2FTitle />
|
||||||
<U2FDescription />
|
<U2FDescription is2FAEnabled={is2FAEnabled} />
|
||||||
</div>
|
</div>
|
||||||
</PreferencesSegment>
|
</PreferencesSegment>
|
||||||
<PreferencesSegment classes="mt-2">
|
<PreferencesSegment classes="mt-2">
|
||||||
@@ -60,7 +61,7 @@ const U2FView: FunctionComponent<Props> = ({ application }) => {
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className="mt-1"
|
className="mt-1"
|
||||||
disabled={!application.isFullU2FClient}
|
disabled={!application.isFullU2FClient || !is2FAEnabled}
|
||||||
label="Add Device"
|
label="Add Device"
|
||||||
primary
|
primary
|
||||||
onClick={handleAddDeviceClick}
|
onClick={handleAddDeviceClick}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { U2FProps } from './U2FProps'
|
|||||||
import U2FView from './U2FView/U2FView'
|
import U2FView from './U2FView/U2FView'
|
||||||
|
|
||||||
const U2FWrapper: FunctionComponent<U2FProps> = (props) => {
|
const U2FWrapper: FunctionComponent<U2FProps> = (props) => {
|
||||||
return <U2FView application={props.application} />
|
return <U2FView application={props.application} is2FAEnabled={props.is2FAEnabled} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default U2FWrapper
|
export default U2FWrapper
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { WebApplication } from '@/Application/WebApplication'
|
import { WebApplication } from '@/Application/WebApplication'
|
||||||
import { MfaProps } from './Panes/Security/TwoFactorAuth/MfaProps'
|
|
||||||
|
|
||||||
export interface PreferencesProps extends MfaProps {
|
export interface PreferencesProps {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
closePreferences: () => void
|
closePreferences: () => void
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user