diff --git a/apps/web/src/dialogs/settings/auth-settings.ts b/apps/web/src/dialogs/settings/auth-settings.ts index 8166be867..349de9fbe 100644 --- a/apps/web/src/dialogs/settings/auth-settings.ts +++ b/apps/web/src/dialogs/settings/auth-settings.ts @@ -87,21 +87,42 @@ export const AuthenticationSettings: SettingsGroup[] = [ header: strings.twoFactorAuth(), key: "2fa", section: "auth", + onStateChange: (listener) => + useUserStore.subscribe((s) => s.user?.mfa, listener), settings: [ { key: "2fa-enabled", - title: strings.twoFactorAuthEnabled(), - description: strings.accountIsSecure(), + title: strings.twoFactorAuth(), + description: () => + useUserStore.getState().user?.mfa.isEnabled + ? strings.accountIsSecure() + : strings.twoFactorAuthDesc(), keywords: [], - components: [] + components: [ + { + type: "toggle", + isToggled: () => !!useUserStore.getState().user?.mfa.isEnabled, + toggle: async () => { + if (useUserStore.getState().user?.mfa.isEnabled) { + await db.mfa.disable(); + showToast("success", strings.twoFactorAuthDisabled()); + await useUserStore.getState().refreshUser(); + } else { + await MultifactorDialog.show({}); + await useUserStore.getState().refreshUser(); + } + } + } + ] }, { key: "primary-2fa-method", title: strings.change2faMethod(), keywords: ["primary 2fa method"], description: strings.twoFactorAuthDesc(), - onStateChange: (listener) => - useUserStore.subscribe((s) => s.user?.mfa.primaryMethod, listener), + isHidden: () => + !useUserStore.getState().user?.mfa || + !useUserStore.getState().user?.mfa.isEnabled, components: [ { type: "button", @@ -121,8 +142,9 @@ export const AuthenticationSettings: SettingsGroup[] = [ title: strings.addFallback2faMethod(), description: strings.addFallback2faMethodDesc(), keywords: ["backup 2fa methods"], - onStateChange: (listener) => - useUserStore.subscribe((s) => s.user?.mfa.secondaryMethod, listener), + isHidden: () => + !useUserStore.getState().user?.mfa || + !useUserStore.getState().user?.mfa.isEnabled, components: () => [ { type: "button", @@ -147,6 +169,9 @@ export const AuthenticationSettings: SettingsGroup[] = [ title: strings.viewRecoveryCodes(), description: strings.viewRecoveryCodesDesc(), keywords: ["recovery codes"], + isHidden: () => + !useUserStore.getState().user?.mfa || + !useUserStore.getState().user?.mfa.isEnabled, components: [ { type: "button", diff --git a/apps/web/src/views/auth.tsx b/apps/web/src/views/auth.tsx index a24d7730a..92c024962 100644 --- a/apps/web/src/views/auth.tsx +++ b/apps/web/src/views/auth.tsx @@ -251,16 +251,24 @@ function LoginEmail(props: BaseAuthComponentProps<"login:email">) { subtitle: strings.authWait() }} onSubmit={async (form) => { - const { primaryMethod, phoneNumber, secondaryMethod } = - (await userstore.login(form)) as MFAErrorData; + const loginResult = await userstore.login(form); + if (typeof loginResult === "boolean" || !loginResult) + throw new Error(strings.loginFailed()); - navigate("mfa:code", { - email: form.email, - selectedMethod: primaryMethod, - primaryMethod, - phoneNumber, - secondaryMethod - }); + if (loginResult.redirect === "mfa") { + navigate("mfa:code", { + email: form.email, + selectedMethod: loginResult.primaryMethod, + primaryMethod: loginResult.primaryMethod, + phoneNumber: loginResult.phoneNumber, + secondaryMethod: loginResult.secondaryMethod + }); + } else { + navigate("login:password", { + email: form.email, + password: "" + }); + } }} > {(form?: EmailFormData) => ( @@ -472,16 +480,24 @@ function SessionExpiry(props: BaseAuthComponentProps<"sessionExpiry">) { onSubmit={async () => { if (!user) return; - const { primaryMethod, phoneNumber, secondaryMethod } = - (await userstore.login(user)) as MFAErrorData; + const loginResult = await userstore.login(user); + if (typeof loginResult === "boolean" || !loginResult) + throw new Error(strings.loginFailed()); - navigate("mfa:code", { - email: user.email, - selectedMethod: primaryMethod, - primaryMethod, - phoneNumber, - secondaryMethod - }); + if (loginResult.redirect === "mfa") { + navigate("mfa:code", { + email: user.email, + selectedMethod: loginResult.primaryMethod, + primaryMethod: loginResult.primaryMethod, + phoneNumber: loginResult.phoneNumber, + secondaryMethod: loginResult.secondaryMethod + }); + } else { + navigate("login:password", { + email: user.email, + password: "" + }); + } }} >