From d1778fdce8c20b2d1b6e462c8f2548c911f6f993 Mon Sep 17 00:00:00 2001 From: Leo Giovanetti Date: Fri, 21 Jul 2023 05:32:03 -0300 Subject: [PATCH] fix: 2FA from CAL to GOOGLE (#10288) * Fixing 2FA from CAL to GOOGLE * Missing change * Missing logic for converted providers * Fixing edge case and applying feedback * Leftovers --- .../settings/DisableTwoFactorModal.tsx | 21 +++++++++++-------- .../settings/EnableTwoFactorModal.tsx | 2 +- .../pages/api/auth/two-factor/totp/disable.ts | 11 ++++++---- apps/web/pages/auth/login.tsx | 2 +- .../settings/security/two-factor-auth.tsx | 6 ++++-- .../features/auth/lib/next-auth-options.ts | 10 ++++++--- .../settings/layouts/SettingsLayout.tsx | 6 +++++- 7 files changed, 37 insertions(+), 21 deletions(-) diff --git a/apps/web/components/settings/DisableTwoFactorModal.tsx b/apps/web/components/settings/DisableTwoFactorModal.tsx index 6b6f47236f..cbf02dbe80 100644 --- a/apps/web/components/settings/DisableTwoFactorModal.tsx +++ b/apps/web/components/settings/DisableTwoFactorModal.tsx @@ -12,7 +12,7 @@ import TwoFactorAuthAPI from "./TwoFactorAuthAPI"; interface DisableTwoFactorAuthModalProps { open: boolean; onOpenChange: () => void; - + disablePassword?: boolean; /** Called when the user closes the modal without disabling two-factor auth */ onCancel: () => void; /** Called when the user disables two-factor auth */ @@ -27,6 +27,7 @@ interface DisableTwoFactorValues { const DisableTwoFactorAuthModal = ({ onDisable, onCancel, + disablePassword, open, onOpenChange, }: DisableTwoFactorAuthModalProps) => { @@ -75,19 +76,21 @@ const DisableTwoFactorAuthModal = ({
- + {!disablePassword && ( + + )} {errorMessage &&

{errorMessage}

}
- + diff --git a/apps/web/components/settings/EnableTwoFactorModal.tsx b/apps/web/components/settings/EnableTwoFactorModal.tsx index 77babea82f..00e4cc1889 100644 --- a/apps/web/components/settings/EnableTwoFactorModal.tsx +++ b/apps/web/components/settings/EnableTwoFactorModal.tsx @@ -185,7 +185,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable )} - + diff --git a/apps/web/pages/api/auth/two-factor/totp/disable.ts b/apps/web/pages/api/auth/two-factor/totp/disable.ts index 11c3c70585..339d7f35bf 100644 --- a/apps/web/pages/api/auth/two-factor/totp/disable.ts +++ b/apps/web/pages/api/auth/two-factor/totp/disable.ts @@ -6,6 +6,7 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import { verifyPassword } from "@calcom/features/auth/lib/verifyPassword"; import { symmetricDecrypt } from "@calcom/lib/crypto"; import prisma from "@calcom/prisma"; +import { IdentityProvider } from "@calcom/prisma/client"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== "POST") { @@ -28,7 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(401).json({ message: "Not authenticated" }); } - if (!user.password) { + if (!user.password && user.identityProvider === IdentityProvider.CAL) { return res.status(400).json({ error: ErrorCode.UserMissingPassword }); } @@ -36,9 +37,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.json({ message: "Two factor disabled" }); } - const isCorrectPassword = await verifyPassword(req.body.password, user.password); - if (!isCorrectPassword) { - return res.status(400).json({ error: ErrorCode.IncorrectPassword }); + if (user.password && user.identityProvider === IdentityProvider.CAL) { + const isCorrectPassword = await verifyPassword(req.body.password, user.password); + if (!isCorrectPassword) { + return res.status(400).json({ error: ErrorCode.IncorrectPassword }); + } } // if user has 2fa if (user.twoFactorEnabled) { diff --git a/apps/web/pages/auth/login.tsx b/apps/web/pages/auth/login.tsx index fecb9d6856..98b98209eb 100644 --- a/apps/web/pages/auth/login.tsx +++ b/apps/web/pages/auth/login.tsx @@ -57,7 +57,7 @@ export default function Login({ .string() .min(1, `${t("error_required_field")}`) .email(`${t("enter_valid_email")}`), - password: z.string().min(1, `${t("error_required_field")}`), + password: !!totpEmail ? z.literal("") : z.string().min(1, `${t("error_required_field")}`), }) // Passthrough other fields like totpCode .passthrough(); diff --git a/apps/web/pages/settings/security/two-factor-auth.tsx b/apps/web/pages/settings/security/two-factor-auth.tsx index 08265930b6..fd5c1a7e20 100644 --- a/apps/web/pages/settings/security/two-factor-auth.tsx +++ b/apps/web/pages/settings/security/two-factor-auth.tsx @@ -34,14 +34,15 @@ const TwoFactorAuthView = () => { if (isLoading) return ; const isCalProvider = user?.identityProvider === "CAL"; + const canSetupTwoFactor = !isCalProvider && !user?.twoFactorEnabled; return ( <> - {!isCalProvider && } + {canSetupTwoFactor && }
user?.twoFactorEnabled ? setDisableModalOpen(true) : setEnableModalOpen(true) @@ -72,6 +73,7 @@ const TwoFactorAuthView = () => { setDisableModalOpen(!disableModalOpen)} onDisable={() => { setDisableModalOpen(false); diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index 4a496fd7a9..6a1033e5bc 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -115,7 +115,7 @@ const providers: Provider[] = [ throw new Error(ErrorCode.IncorrectEmailPassword); } - if (user.password || !credentials.totpCode) { + if (user.password && !credentials.totpCode) { if (!user.password) { throw new Error(ErrorCode.IncorrectEmailPassword); } @@ -560,7 +560,7 @@ export const AUTH_OPTIONS: AuthOptions = { console.error("Error while linking account of already existing user"); } } - if (existingUser.twoFactorEnabled) { + if (existingUser.twoFactorEnabled && existingUser.identityProvider === idP) { return loginWithTotp(existingUser); } else { return true; @@ -600,7 +600,11 @@ export const AUTH_OPTIONS: AuthOptions = { if (existingUserWithEmail) { // if self-hosted then we can allow auto-merge of identity providers if email is verified - if (!hostedCal && existingUserWithEmail.emailVerified) { + if ( + !hostedCal && + existingUserWithEmail.emailVerified && + existingUserWithEmail.identityProvider !== IdentityProvider.CAL + ) { if (existingUserWithEmail.twoFactorEnabled) { return loginWithTotp(existingUserWithEmail); } else { diff --git a/packages/features/settings/layouts/SettingsLayout.tsx b/packages/features/settings/layouts/SettingsLayout.tsx index 628ef47304..68e1aa86d5 100644 --- a/packages/features/settings/layouts/SettingsLayout.tsx +++ b/packages/features/settings/layouts/SettingsLayout.tsx @@ -147,7 +147,11 @@ const useTabs = () => { tab.name = user?.name || "my_account"; tab.icon = undefined; tab.avatar = avatar?.avatar || WEBAPP_URL + "/" + session?.data?.user?.username + "/avatar.png"; - } else if (tab.href === "/settings/security" && user?.identityProvider === IdentityProvider.GOOGLE) { + } else if ( + tab.href === "/settings/security" && + user?.identityProvider === IdentityProvider.GOOGLE && + !user?.twoFactorEnabled + ) { tab.children = tab?.children?.filter( (childTab) => childTab.href !== "/settings/security/two-factor-auth" );