/* eslint-disable no-nested-ternary */ /* eslint-disable @typescript-eslint/no-unused-vars */ import crypto from "crypto"; import { useEffect, useState } from "react"; import Head from "next/head"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/router"; import { faWarning, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import jsrp from "jsrp"; import queryString from "query-string"; import nacl from "tweetnacl"; import { encodeBase64 } from "tweetnacl-util"; import Button from "@app/components/basic/buttons/Button"; import InputField from "@app/components/basic/InputField"; import checkPassword from "@app/components/utilities/checks/password/checkPassword"; import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm"; import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto"; import issueBackupKey from "@app/components/utilities/cryptography/issueBackupKey"; import { saveTokenToLocalStorage } from "@app/components/utilities/saveTokenToLocalStorage"; import SecurityClient from "@app/components/utilities/SecurityClient"; import { useServerConfig } from "@app/context"; import { completeAccountSignupInvite, useSelectOrganization, verifySignupInvite } from "@app/hooks/api/auth/queries"; import { fetchOrganizations } from "@app/hooks/api/organization/queries"; import { navigateUserToOrg } from "@app/views/Login/Login.utils"; // eslint-disable-next-line new-cap const client = new jsrp.client(); type Errors = { tooShort?: string; tooLong?: string; noLetterChar?: string; noNumOrSpecialChar?: string; repeatedChar?: string; escapeChar?: string; lowEntropy?: string; breached?: string; }; export default function SignupInvite() { const [password, setPassword] = useState(""); const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [firstNameError, setFirstNameError] = useState(false); const [lastNameError, setLastNameError] = useState(false); const [isLoading, setIsLoading] = useState(false); const [step, setStep] = useState(1); const [backupKeyError, setBackupKeyError] = useState(false); const [backupKeyIssued, setBackupKeyIssued] = useState(false); const [errors, setErrors] = useState({}); const router = useRouter(); const parsedUrl = queryString.parse(router.asPath.split("?")[1]); const token = parsedUrl.token as string; const organizationId = parsedUrl.organization_id as string; const email = (parsedUrl.to as string)?.replace(" ", "+").trim(); const { config } = useServerConfig(); const { mutateAsync: selectOrganization } = useSelectOrganization(); useEffect(() => { if (!config.allowSignUp) { router.push("/login"); } }, [config.allowSignUp]); // Verifies if the information that the users entered (name, workspace) is there, and if the password matched the criteria. const signupErrorCheck = async () => { setIsLoading(true); let errorCheck = await checkPassword({ password, setErrors }); if (!firstName) { setFirstNameError(true); errorCheck = true; } else { setFirstNameError(false); } if (!lastName) { setLastNameError(true); errorCheck = true; } else { setLastNameError(false); } 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 privateKey = encodeBase64(secretKeyUint8Array); const publicKey = encodeBase64(publicKeyUint8Array); localStorage.setItem("PRIVATE_KEY", privateKey); client.init( { username: email, password }, async () => { client.createVerifier(async (err, result) => { try { const derivedKey = await deriveArgonKey({ password, salt: result.salt, mem: 65536, time: 3, parallelism: 1, hashLen: 32 }); if (!derivedKey) throw new Error("Failed to derive key from password"); const key = crypto.randomBytes(32); // create encrypted private key by encrypting the private // key with the symmetric key [key] const { ciphertext: encryptedPrivateKey, iv: encryptedPrivateKeyIV, tag: encryptedPrivateKeyTag } = Aes256Gcm.encrypt({ text: privateKey, secret: key }); // create the protected key by encrypting the symmetric key // [key] with the derived key const { ciphertext: protectedKey, iv: protectedKeyIV, tag: protectedKeyTag } = Aes256Gcm.encrypt({ text: key.toString("hex"), secret: Buffer.from(derivedKey.hash) }); const { token: jwtToken } = await completeAccountSignupInvite({ email, password, firstName, lastName, protectedKey, protectedKeyIV, protectedKeyTag, publicKey, encryptedPrivateKey, encryptedPrivateKeyIV, encryptedPrivateKeyTag, salt: result.salt, verifier: result.verifier }); // unset temporary signup JWT token and set JWT token SecurityClient.setSignupToken(""); SecurityClient.setToken(jwtToken); saveTokenToLocalStorage({ publicKey, encryptedPrivateKey, iv: encryptedPrivateKeyIV, tag: encryptedPrivateKeyTag, privateKey }); const userOrgs = await fetchOrganizations(); const orgId = userOrgs[0].id; if (!orgId) throw new Error("You are not part of any organization"); await selectOrganization({ organizationId: orgId }); localStorage.setItem("orgData.id", orgId); setStep(3); } catch (error) { setIsLoading(false); console.error(error); } }); } ); } else { setIsLoading(false); } }; // Step 4 of the sign up process (download the emergency kit pdf) const stepConfirmEmail = (

Confirm your email

verify email
); // Because this is the invite signup - we directly go to the last step of signup (email is already verified) const main = (

Almost there!

{ setPassword(pass); checkPassword({ password: pass, setErrors }); }} type="password" value={password} isRequired error={Object.keys(errors).length > 0} autoComplete="new-password" id="new-password" /> {Object.keys(errors).length > 0 && (
Password should contain at least:
{Object.keys(errors).map((key) => { if (errors[key as keyof Errors]) { return (

{errors[key as keyof Errors]}

); } return null; })}
)}
); // Step 4 of the sign up process (download the emergency kit pdf) const step4 = (

Save your Emergency Kit

If you get locked out of your account, your Emergency Kit is the only way to sign in.
We recommend you download it and keep it somewhere safe.
It contains your Secret Key which we cannot access or recover for you if you lose it.
); return (
Sign Up
Infisical Logo
{step === 1 ? stepConfirmEmail : step === 2 ? main : step4}
); }