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
This commit is contained in:
Leo Giovanetti 2023-07-21 05:32:03 -03:00 committed by GitHub
parent b49952b8d8
commit d1778fdce8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 37 additions and 21 deletions

View File

@ -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 = ({
<DialogContent title={t("disable_2fa")} description={t("disable_2fa_recommendation")} type="creation">
<Form form={form} handleSubmit={handleDisable}>
<div className="mb-8">
<PasswordField
labelProps={{
className: "block text-sm font-medium text-default",
}}
{...form.register("password")}
className="border-default mt-1 block w-full rounded-md border px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black"
/>
{!disablePassword && (
<PasswordField
labelProps={{
className: "block text-sm font-medium text-default",
}}
{...form.register("password")}
className="border-default mt-1 block w-full rounded-md border px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black"
/>
)}
<TwoFactor center={false} />
{errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>}
</div>
<DialogFooter showDivider className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<DialogFooter showDivider className="relative mt-5">
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>

View File

@ -185,7 +185,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
)}
</div>
</WithStep>
<DialogFooter className="mt-8 sm:flex sm:flex-row-reverse" showDivider>
<DialogFooter className="mt-8" showDivider>
<Button color="secondary" onClick={onCancel}>
{t("cancel")}
</Button>

View File

@ -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) {

View File

@ -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();

View File

@ -34,14 +34,15 @@ const TwoFactorAuthView = () => {
if (isLoading) return <SkeletonLoader />;
const isCalProvider = user?.identityProvider === "CAL";
const canSetupTwoFactor = !isCalProvider && !user?.twoFactorEnabled;
return (
<>
<Meta title={t("2fa")} description={t("set_up_two_factor_authentication")} />
{!isCalProvider && <Alert severity="neutral" message={t("2fa_disabled")} />}
{canSetupTwoFactor && <Alert severity="neutral" message={t("2fa_disabled")} />}
<div className="mt-6 flex items-start space-x-4">
<Switch
data-testid="two-factor-switch"
disabled={!isCalProvider}
disabled={canSetupTwoFactor}
checked={user?.twoFactorEnabled}
onCheckedChange={() =>
user?.twoFactorEnabled ? setDisableModalOpen(true) : setEnableModalOpen(true)
@ -72,6 +73,7 @@ const TwoFactorAuthView = () => {
<DisableTwoFactorModal
open={disableModalOpen}
disablePassword={!isCalProvider}
onOpenChange={() => setDisableModalOpen(!disableModalOpen)}
onDisable={() => {
setDisableModalOpen(false);

View File

@ -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 {

View File

@ -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"
);