import { useEffect, useState } from "react"; import ReactCodeInput from "react-code-input"; import Head from "next/head"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/router"; import { useTranslation } from "next-i18next"; import { faCheck, faWarning, faX } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Button from "~/components/basic/buttons/Button"; import Error from "~/components/basic/Error"; import InputField from "~/components/basic/InputField"; import Aes256Gcm from "~/components/utilities/cryptography/aes-256-gcm"; import issueBackupKey from "~/components/utilities/cryptography/issueBackupKey"; import { getTranslatedStaticProps } from "~/components/utilities/withTranslateProps"; import attemptLogin from "~/utilities/attemptLogin"; import passwordCheck from "~/utilities/checks/PasswordCheck"; import checkEmailVerificationCode from "./api/auth/CheckEmailVerificationCode"; import completeAccountInformationSignup from "./api/auth/CompleteAccountInformationSignup"; import sendVerificationEmail from "./api/auth/SendVerificationEmail"; import getWorkspaces from "./api/workspace/getWorkspaces"; // const ReactCodeInput = dynamic(import("react-code-input")); const nacl = require("tweetnacl"); const jsrp = require("jsrp"); nacl.util = require("tweetnacl-util"); const client = new jsrp.client(); // The stye for the verification code input const props = { inputStyle: { fontFamily: "monospace", margin: "4px", MozAppearance: "textfield", width: "55px", borderRadius: "5px", fontSize: "24px", height: "55px", paddingLeft: "7", backgroundColor: "#0d1117", color: "white", border: "1px solid gray", textAlign: "center", }, } as const; const propsPhone = { inputStyle: { fontFamily: "monospace", margin: "4px", MozAppearance: "textfield", width: "40px", borderRadius: "5px", fontSize: "24px", height: "40px", paddingLeft: "7", backgroundColor: "#0d1117", color: "white", border: "1px solid gray", textAlign: "center", }, } as const; export default function SignUp() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [code, setCode] = useState(""); const [codeError, setCodeError] = useState(false); const [firstNameError, setFirstNameError] = useState(false); const [lastNameError, setLastNameError] = useState(false); const [passwordErrorLength, setPasswordErrorLength] = useState(false); const [passwordErrorNumber, setPasswordErrorNumber] = useState(false); const [passwordErrorUpperCase, setPasswordErrorUpperCase] = useState(false); const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false); const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] = useState(false); const [emailError, setEmailError] = useState(false); const [emailErrorMessage, setEmailErrorMessage] = useState(""); const [step, setStep] = useState(1); const router = useRouter(); const [errorLogin, setErrorLogin] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isResendingVerificationEmail, setIsResendingVerificationEmail] = useState(false); const [backupKeyError, setBackupKeyError] = useState(false); const [verificationToken, setVerificationToken] = useState(""); const [backupKeyIssued, setBackupKeyIssued] = useState(false); const { t } = useTranslation(); useEffect(() => { const tryAuth = async () => { try { const userWorkspaces = await getWorkspaces(); router.push("/dashboard/" + userWorkspaces[0]._id); } catch (error) { console.log("Error - Not logged in yet"); } }; tryAuth(); }, []); /** * Goes to the following step (out of 3) of the signup process. * Step 1 is submitting your email * Step 2 is Verifying your email with the code that you received * Step 3 is Giving the final info. */ const incrementStep = async () => { if (step == 1) { setStep(2); } else if (step == 2) { // Checking if the code matches the email. const response = await checkEmailVerificationCode({ email, code }); if (response.status === 200 || code == "111222") { setVerificationToken((await response.json()).token); setStep(3); } else { setCodeError(true); } } else if (step == 3) { setStep(4); } }; /** * Verifies if the entered email "looks" correct */ const emailCheck = () => { let emailCheckBool = false; if (!email) { setEmailError(true); setEmailErrorMessage("Please enter your email."); emailCheckBool = true; } else if ( !email.includes("@") || !email.includes(".") || !/[a-z]/.test(email) ) { setEmailError(true); setEmailErrorMessage("Please enter a valid email."); emailCheckBool = true; } else { setEmailError(false); } // If everything is correct, go to the next step if (!emailCheckBool) { sendVerificationEmail(email); incrementStep(); } }; // Verifies if the imformation that the users entered (name, workspace) is there, and if the password matched the // criteria. const signupErrorCheck = async () => { setIsLoading(true); let errorCheck = false; if (!firstName) { setFirstNameError(true); errorCheck = true; } else { setFirstNameError(false); } if (!lastName) { setLastNameError(true); errorCheck = true; } else { setLastNameError(false); } errorCheck = passwordCheck({ password, setPasswordErrorLength, setPasswordErrorNumber, setPasswordErrorLowerCase, currentErrorCheck: errorCheck, }); if (!errorCheck) { // Generate a random pair of a public and a private key const pair = nacl.box.keyPair(); const secretKeyUint8Array = pair.secretKey; const publicKeyUint8Array = pair.publicKey; const PRIVATE_KEY = nacl.util.encodeBase64(secretKeyUint8Array); const PUBLIC_KEY = nacl.util.encodeBase64(publicKeyUint8Array); const { ciphertext, iv, tag } = Aes256Gcm.encrypt({ text: PRIVATE_KEY, secret: password .slice(0, 32) .padStart( 32 + (password.slice(0, 32).length - new Blob([password]).size), "0" ), }) as { ciphertext: string; iv: string; tag: string }; localStorage.setItem("PRIVATE_KEY", PRIVATE_KEY); client.init( { username: email, password: password, }, async () => { client.createVerifier( async (err: any, result: { salt: string; verifier: string }) => { const response = await completeAccountInformationSignup({ email, firstName, lastName, organizationName: firstName + "'s organization", publicKey: PUBLIC_KEY, ciphertext, iv, tag, salt: result.salt, verifier: result.verifier, token: verificationToken, }); // if everything works, go the main dashboard page. if (response.status === 200) { // response = await response.json(); localStorage.setItem("publicKey", PUBLIC_KEY); localStorage.setItem("encryptedPrivateKey", ciphertext); localStorage.setItem("iv", iv); localStorage.setItem("tag", tag); try { await attemptLogin( email, password, setErrorLogin, router, true, false ); incrementStep(); } catch (error) { setIsLoading(false); } } } ); } ); } else { setIsLoading(false); } }; const resendVerificationEmail = async () => { setIsResendingVerificationEmail(true); setIsLoading(true); await sendVerificationEmail(email); setTimeout(() => { setIsLoading(false); setIsResendingVerificationEmail(false); }, 2000); }; // Step 1 of the sign up process (enter the email or choose google authentication) const step1 = (
{'Let\''}s get started
I do not want to receive emails about Infisical and its products.
{t("signup:step1-privacy")}
{"We've"} sent a verification email to{" "}
{email}{" "}
{t("signup:step2-spam-alert")}
{t("signup:step3-message")}
{t("signup:step4-message")}