import classNames from "classnames"; import { GetServerSidePropsContext } from "next"; 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 { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Alert } from "@calcom/ui/Alert"; import Button from "@calcom/ui/Button"; import { Icon } from "@calcom/ui/Icon"; import { EmailField, Form, PasswordField } from "@calcom/ui/form/fields"; import prisma from "@calcom/web/lib/prisma"; import { ErrorCode, getSession } from "@lib/auth"; import { WEBAPP_URL, WEBSITE_URL } from "@lib/config/constants"; import { hostedCal, isSAMLLoginEnabled, samlProductID, samlTenantID } from "@lib/saml"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import AddToHomescreen from "@components/AddToHomescreen"; import SAMLLogin from "@components/auth/SAMLLogin"; import TwoFactor from "@components/auth/TwoFactor"; import AuthContainer from "@components/ui/AuthContainer"; import { IS_GOOGLE_LOGIN_ENABLED } from "@server/lib/constants"; import { ssrInit } from "@server/lib/ssr"; interface LoginValues { email: string; password: string; totpCode: string; csrfToken: string; } 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 [twoFactorRequired, setTwoFactorRequired] = useState(false); const [errorMessage, setErrorMessage] = useState(null); const errorMessages: { [key: string]: string } = { // [ErrorCode.SecondFactorRequired]: t("2fa_enabled_instructions"), [ErrorCode.IncorrectPassword]: `${t("incorrect_password")} ${t("please_try_again")}`, [ErrorCode.UserNotFound]: t("no_account_exists"), [ErrorCode.IncorrectTwoFactorCode]: `${t("incorrect_2fa_code")} ${t("please_try_again")}`, [ErrorCode.InternalServerError]: `${t("something_went_wrong")} ${t("please_try_again_and_contact_us")}`, [ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"), }; const telemetry = useTelemetry(); let callbackUrl = typeof router.query?.callbackUrl === "string" ? router.query.callbackUrl : ""; if (/"\//.test(callbackUrl)) callbackUrl = callbackUrl.substring(1); // If not absolute URL, make it absolute if (!/^https?:\/\//.test(callbackUrl)) { callbackUrl = `${WEBAPP_URL}/${callbackUrl}`; } const safeCallbackUrl = getSafeRedirectUrl(callbackUrl); callbackUrl = safeCallbackUrl || ""; const LoginFooter = ( {t("dont_have_an_account")}{" "} {t("create_an_account")} ); const TwoFactorFooter = ( ); 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 && } {errorMessage && }
{!twoFactorRequired && ( <> {isGoogleLoginEnabled && (
)} {isSAMLLoginEnabled && ( )} )}
); } export async function getServerSideProps(context: GetServerSidePropsContext) { const { req } = context; const session = await getSession({ req }); const ssr = await ssrInit(context); if (session) { return { redirect: { destination: "/", permanent: false, }, }; } const userCount = await prisma.user.count(); if (userCount === 0) { // Proceed to new onboarding to create first admin user return { redirect: { destination: "/auth/setup", permanent: false, }, }; } return { props: { csrfToken: await getCsrfToken(context), trpcState: ssr.dehydrate(), isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED, isSAMLLoginEnabled, hostedCal, samlTenantID, samlProductID, }, }; }