diff --git a/apps/web/components/auth/SAMLLogin.tsx b/apps/web/components/auth/SAMLLogin.tsx deleted file mode 100644 index a675b90a5e..0000000000 --- a/apps/web/components/auth/SAMLLogin.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { signIn } from "next-auth/react"; -import { Dispatch, SetStateAction } from "react"; -import { useFormContext } from "react-hook-form"; - -import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import { trpc } from "@calcom/trpc/react"; -import Button from "@calcom/ui/Button"; - -import { useLocale } from "@lib/hooks/useLocale"; - -interface Props { - email: string; - samlTenantID: string; - samlProductID: string; - hostedCal: boolean; - setErrorMessage: Dispatch>; -} - -export default function SAMLLogin(props: Props) { - const { t } = useLocale(); - const methods = useFormContext(); - const telemetry = useTelemetry(); - - const mutation = trpc.useMutation("viewer.public.samlTenantProduct", { - onSuccess: async (data) => { - await signIn("saml", {}, { tenant: data.tenant, product: data.product }); - }, - onError: (err) => { - props.setErrorMessage(err.message); - }, - }); - - return ( -
- -
- ); -} diff --git a/apps/web/pages/auth/login.tsx b/apps/web/pages/auth/login.tsx index e147b71623..6480c328fa 100644 --- a/apps/web/pages/auth/login.tsx +++ b/apps/web/pages/auth/login.tsx @@ -4,17 +4,17 @@ import { getCsrfToken, signIn } from "next-auth/react"; import Link from "next/link"; import { useRouter } from "next/router"; import { useState } from "react"; -import { useForm } from "react-hook-form"; +import { useForm, FormProvider } from "react-hook-form"; import { FaGoogle } from "react-icons/fa"; -import { hostedCal, isSAMLLoginEnabled, samlProductID, samlTenantID } from "@calcom/features/ee/sso/lib/saml"; +import { isSAMLLoginEnabled, samlProductID, samlTenantID } from "@calcom/features/ee/sso/lib/saml"; import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import prisma from "@calcom/prisma"; import { Icon } from "@calcom/ui"; import { Alert } from "@calcom/ui/Alert"; -import { Button, EmailField, Form, PasswordField } from "@calcom/ui/v2"; +import { Button, EmailField, PasswordField } from "@calcom/ui/v2"; import SAMLLogin from "@calcom/ui/v2/modules/auth/SAMLLogin"; import { ErrorCode, getSession } from "@lib/auth"; @@ -39,15 +39,14 @@ export default function Login({ csrfToken, isGoogleLoginEnabled, isSAMLLoginEnabled, - hostedCal, samlTenantID, samlProductID, }: inferSSRProps) { const { t } = useLocale(); const router = useRouter(); - const form = useForm(); - const { formState } = form; - const { isSubmitting } = formState; + const methods = useForm(); + + const { register, formState } = methods; const [twoFactorRequired, setTwoFactorRequired] = useState(false); const [errorMessage, setErrorMessage] = useState(null); @@ -86,7 +85,7 @@ export default function Login({ ); + const onSubmit = async (values: LoginValues) => { + setErrorMessage(null); + telemetry.event(telemetryEventTypes.login, collectPageParameters()); + const res = await signIn<"credentials">("credentials", { + ...values, + callbackUrl, + redirect: false, + }); + if (!res) setErrorMessage(errorMessages[ErrorCode.InternalServerError]); + // we're logged in! let's do a hard refresh to the desired url + else if (!res.error) router.push(callbackUrl); + // reveal two factor input if required + else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true); + // fallback if error not found + else setErrorMessage(errorMessages[res.error] || t("something_went_wrong")); + }; + return ( <> -
{ - setErrorMessage(null); - telemetry.event(telemetryEventTypes.login, collectPageParameters()); - const res = await signIn<"credentials">("credentials", { - ...values, - callbackUrl, - redirect: false, - }); - if (!res) setErrorMessage(errorMessages[ErrorCode.InternalServerError]); - // we're logged in! let's do a hard refresh to the desired url - else if (!res.error) router.push(callbackUrl); - // reveal two factor input if required - else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true); - // fallback if error not found - else setErrorMessage(errorMessages[res.error] || t("something_went_wrong")); - }} - data-testid="login-form"> -
- -
-
-
- -
- - + +
+ +
+
+
+ +
+ + +
-
- {twoFactorRequired && } + {twoFactorRequired && } - {errorMessage && } - -
- - {!twoFactorRequired && ( - <> - {(isGoogleLoginEnabled || isSAMLLoginEnabled) &&
} -
- {isGoogleLoginEnabled && ( - - )} - {isSAMLLoginEnabled && ( - - )} + {errorMessage && } +
- - )} + + {!twoFactorRequired && ( + <> + {(isGoogleLoginEnabled || isSAMLLoginEnabled) &&
} +
+ {isGoogleLoginEnabled && ( + + )} + {isSAMLLoginEnabled && ( + + )} +
+ + )} + @@ -233,7 +230,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { trpcState: ssr.dehydrate(), isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED, isSAMLLoginEnabled, - hostedCal, samlTenantID, samlProductID, }, diff --git a/packages/features/ee/sso/lib/saml.ts b/packages/features/ee/sso/lib/saml.ts index 86dbd645b0..6baed7658b 100644 --- a/packages/features/ee/sso/lib/saml.ts +++ b/packages/features/ee/sso/lib/saml.ts @@ -41,7 +41,7 @@ export const samlTenantProduct = async (prisma: PrismaClient, email: string) => if (!user) { throw new TRPCError({ code: "UNAUTHORIZED", - message: "Unauthorized Request", + message: "no_account_exists", }); } diff --git a/packages/ui/v2/modules/auth/SAMLLogin.tsx b/packages/ui/v2/modules/auth/SAMLLogin.tsx index d47182410c..813d1da44f 100644 --- a/packages/ui/v2/modules/auth/SAMLLogin.tsx +++ b/packages/ui/v2/modules/auth/SAMLLogin.tsx @@ -1,7 +1,9 @@ import { signIn } from "next-auth/react"; import { Dispatch, SetStateAction } from "react"; import { useFormContext } from "react-hook-form"; +import z from "zod"; +import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { trpc } from "@calcom/trpc/react"; @@ -9,14 +11,16 @@ import { Icon } from "@calcom/ui"; import Button from "@calcom/ui/v2/core/Button"; interface Props { - email: string; samlTenantID: string; samlProductID: string; - hostedCal: boolean; setErrorMessage: Dispatch>; } -export default function SAMLLogin(props: Props) { +const schema = z.object({ + email: z.string().email({ message: "Please enter a valid email" }), +}); + +export default function SAMLLogin({ samlTenantID, samlProductID, setErrorMessage }: Props) { const { t } = useLocale(); const methods = useFormContext(); const telemetry = useTelemetry(); @@ -26,7 +30,7 @@ export default function SAMLLogin(props: Props) { await signIn("saml", {}, { tenant: data.tenant, product: data.product }); }, onError: (err) => { - props.setErrorMessage(err.message); + setErrorMessage(t(err.message)); }, }); @@ -42,18 +46,27 @@ export default function SAMLLogin(props: Props) { // track Google logins. Without personal data/payload telemetry.event(telemetryEventTypes.googleLogin, collectPageParameters()); - if (!props.hostedCal) { - await signIn("saml", {}, { tenant: props.samlTenantID, product: props.samlProductID }); - } else { - if (props.email.length === 0) { - props.setErrorMessage(t("saml_email_required")); - return; - } - // hosted solution, fetch tenant and product from the backend - mutation.mutate({ - email: methods.getValues("email"), - }); + if (!HOSTED_CAL_FEATURES) { + await signIn("saml", {}, { tenant: samlTenantID, product: samlProductID }); + return; } + + // Hosted solution, fetch tenant and product from the backend + const email = methods.getValues("email"); + const parsed = schema.safeParse({ email }); + + if (!parsed.success) { + const { + fieldErrors: { email }, + } = parsed.error.flatten(); + + setErrorMessage(email ? email[0] : null); + return; + } + + mutation.mutate({ + email, + }); }}> {t("signin_with_saml")}