Remove remaining SecurityClient auth calls in favor of hooks, keep RouteGuard

This commit is contained in:
Tuan Dang
2023-08-11 11:27:33 +07:00
parent 2dba7847b6
commit a4edf6bd0c
40 changed files with 443 additions and 910 deletions
@@ -1,46 +0,0 @@
import { apiRequest } from "@app/config/request";
interface Props {
clientProof: string;
protectedKey: string;
protectedKeyIV: string;
protectedKeyTag: string;
encryptedPrivateKey: string;
encryptedPrivateKeyIV: string;
encryptedPrivateKeyTag: string;
salt: string;
verifier: string;
}
/**
* This is the second step of the change password process (pake)
* @param {*} clientPublicKey
* @returns
*/
const changePassword2 = async ({
clientProof,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
}: Props) => {
const { data } = await apiRequest.post("/api/v1/password/change-password", {
clientProof,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
});
return data;
}
export default changePassword2;
+3 -2
View File
@@ -4,12 +4,13 @@ import SecurityClient from "@app/components/utilities/SecurityClient";
* This function is used to check if the user is authenticated.
* To do that, we get their tokens from cookies, and verify if they are good.
*/
const checkAuth = async () =>
SecurityClient.fetchCall("/api/v1/auth/checkAuth", {
const checkAuth = async () => {
return SecurityClient.fetchCall("/api/v1/auth/checkAuth", {
method: "POST",
headers: {
"Content-Type": "application/json"
}
}).then((res) => res);
}
export default checkAuth;
@@ -1,24 +0,0 @@
interface Props {
email: string;
code: string;
}
/**
* This route check the verification code from the email that user just recieved
* @param {object} obj
* @param {string} obj.email
* @param {string} obj.code
* @returns
*/
const checkEmailVerificationCode = ({ email, code }: Props) => fetch("/api/v1/signup/email/verify", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email,
code
})
});
export default checkEmailVerificationCode;
@@ -1,79 +0,0 @@
import { apiRequest } from "@app/config/request";
interface Props {
email: string;
firstName: string;
lastName: string;
protectedKey: string;
protectedKeyIV: string;
protectedKeyTag: string;
providerAuthToken?: string;
publicKey: string;
encryptedPrivateKey: string;
encryptedPrivateKeyIV: string;
encryptedPrivateKeyTag: string;
organizationName: string;
salt: string;
verifier: string;
attributionSource?: string;
}
/**
* This function is called in the end of the signup process.
* It sends all the necessary nformation to the server.
* @param {object} obj
* @param {string} obj.email - email of the user completing signup
* @param {string} obj.firstName - first name of the user completing signup
* @param {string} obj.lastName - last name of the user completing sign up
* @param {string} obj.protectedKey - protected key in encryption version 2
* @param {string} obj.protectedKeyIV - IV of protected key in encryption version 2
* @param {string} obj.protectedKeyTag - tag of protected key in encryption version 2
* @param {string} obj.organizationName - organization name for this user (usually, [FIRST_NAME]'s organization)
* @param {string} obj.publicKey - public key of the user completing signup
* @param {string} obj.ciphertext
* @param {string} obj.iv
* @param {string} obj.tag
* @param {string} obj.salt
* @param {string} obj.verifier
* @returns
*/
const completeAccountInformationSignup = async ({
email,
firstName,
lastName,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier,
organizationName,
providerAuthToken,
attributionSource
}: Props) => {
const { data } = await apiRequest.post("/api/v3/signup/complete-account/signup", {
email,
firstName,
lastName,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier,
organizationName,
providerAuthToken,
...(attributionSource ? { attributionSource } : {})
});
return data;
}
export default completeAccountInformationSignup;
@@ -1,70 +0,0 @@
import { apiRequest } from "@app/config/request";
interface Props {
email: string;
firstName: string;
lastName: string;
protectedKey: string;
protectedKeyIV: string;
protectedKeyTag: string;
publicKey: string;
encryptedPrivateKey: string;
encryptedPrivateKeyIV: string;
encryptedPrivateKeyTag: string;
salt: string;
verifier: string;
}
// missing token?
// TODO: add to SecurityClient
/**
* This function is called in the end of the signup process.
* It sends all the necessary nformation to the server.
* @param {object} obj
* @param {string} obj.email - email of the user completing signupinvite flow
* @param {string} obj.firstName - first name of the user completing signupinvite flow
* @param {string} obj.lastName - last name of the user completing signupinvite flow
* @param {string} obj.publicKey - public key of the user completing signupinvite flow
* @param {string} obj.ciphertext
* @param {string} obj.iv
* @param {string} obj.tag
* @param {string} obj.salt
* @param {string} obj.verifier
* @param {string} obj.token - token that confirms a user's identity
* @returns
*/
const completeAccountInformationSignupInvite = async ({
email,
firstName,
lastName,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
}: Props) => {
const { data } = await apiRequest.post("/api/v2/signup/complete-account/invite", {
email,
firstName,
lastName,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
});
return data;
}
export default completeAccountInformationSignupInvite;
@@ -1,34 +0,0 @@
interface Props {
email: string;
code: string;
}
/**
* This is the second part of the account recovery step (a user needs to verify their email).
* A user need to click on a button in a magic link page
* @param {object} obj
* @param {object} obj.email - email of a user that is trying to recover access to their account
* @param {object} obj.code - token that a use received via the magic link
* @returns
*/
const EmailVerifyOnPasswordReset = async ({ email, code }: Props) => {
const response = await fetch("/api/v1/password/email/password-reset-verify", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email,
code
})
});
if (response?.status === 200) {
return response;
}
throw new Error(
"Something went wrong during email verification on password reset."
);
};
export default EmailVerifyOnPasswordReset;
@@ -1,51 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
interface Props {
encryptedPrivateKey: string;
iv: string;
tag: string;
salt: string;
verifier: string;
clientProof: string;
}
/**
* This is the route that issues a backup private key that will afterwards be added into a pdf
* @param {object} obj
* @param {string} obj.encryptedPrivateKey
* @param {string} obj.iv
* @param {string} obj.tag
* @param {string} obj.salt
* @param {string} obj.verifier
* @param {string} obj.clientProof
* @returns
*/
const issueBackupPrivateKey = ({
encryptedPrivateKey,
iv,
tag,
salt,
verifier,
clientProof
}: Props) =>
SecurityClient.fetchCall("/api/v1/password/backup-private-key", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
clientProof,
encryptedPrivateKey,
iv,
tag,
salt,
verifier
})
}).then((res) => {
if (res?.status !== 200) {
console.log("Failed to issue the backup key");
}
return res;
});
export default issueBackupPrivateKey;
-33
View File
@@ -1,33 +0,0 @@
interface Login1 {
serverPublicKey: string;
salt: string;
}
/**
* This is the first step of the login process (pake)
* @param {*} email
* @param {*} clientPublicKey
* @returns
*/
const login1 = async (loginDetails: {
email: string;
clientPublicKey: string;
providerAuthToken?: string;
}) => {
const response = await fetch("/api/v3/auth/login1", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(loginDetails),
});
// need precise error handling about the status code
if (response?.status === 200) {
const data = (await response.json()) as unknown as Login1;
return data;
}
throw new Error("Wrong password");
};
export default login1;
-42
View File
@@ -1,42 +0,0 @@
interface Login2Response {
mfaEnabled: boolean;
token: string;
encryptionVersion?: number;
protectedKey?: string;
protectedKeyIV?: string;
protectedKeyTag?: string;
publicKey?: string;
encryptedPrivateKey?: string;
iv?: string;
tag?: string;
}
/**
* This is the second step of the login process
* @param {*} email
* @param {*} clientPublicKey
* @returns
*/
const login2 = async (loginDetails: {
email: string;
clientProof: string;
providerAuthToken?: string;
}) => {
const response = await fetch("/api/v3/auth/login2", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(loginDetails),
credentials: "include"
});
// need precise error handling about the status code
if (response.status === 200) {
const data = (await response.json()) as unknown as Login2Response;
return data;
}
throw new Error("Password verification failed");
};
export default login2;
-41
View File
@@ -1,41 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
/**
* This route logs the user out. Note: the user should authorized to do this.
* We first try to log out - if the authorization fails (response.status = 401), we refetch the new token, and then retry
*/
const logout = async () => {
try {
const res = await SecurityClient.fetchCall("/api/v1/auth/logout", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
credentials: "include"
});
if (res?.status === 200) {
SecurityClient.setToken("");
// Delete the cookie by not setting a value; Alternatively clear the local storage
localStorage.removeItem("protectedKey");
localStorage.removeItem("protectedKeyIV");
localStorage.removeItem("protectedKeyTag");
localStorage.removeItem("publicKey");
localStorage.removeItem("encryptedPrivateKey");
localStorage.removeItem("iv");
localStorage.removeItem("tag");
localStorage.removeItem("PRIVATE_KEY");
localStorage.removeItem("orgData.id");
localStorage.removeItem("projectData.id");
return res;
}
} catch (error) {
console.log("Error logging out", error);
}
return undefined;
};
export default logout;
-29
View File
@@ -1,29 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
interface Props {
clientPublicKey: string;
}
/**
* This is the first step of the change password process (pake)
* @param {string} clientPublicKey
* @returns
*/
const SRP1 = ({ clientPublicKey }: Props) =>
SecurityClient.fetchCall("/api/v1/password/srp1", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
clientPublicKey
})
}).then(async (res) => {
if (res && res.status === 200) {
return res.json();
}
console.log("Failed to do the first step of SRP");
return undefined;
});
export default SRP1;
@@ -1,33 +0,0 @@
interface Props {
email: string;
}
/**
* This is the first of the account recovery step (a user needs to verify their email).
* It will send an email containing a magic link to start the account recovery flow.
* @param {object} obj
* @param {object} obj.email - email of a user that is trying to recover access to their account
* @returns
*/
const SendEmailOnPasswordReset = async ({ email }: Props) => {
const response = await fetch("/api/v1/password/email/password-reset", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email
})
});
// need precise error handling about the status code
if (response?.status === 200) {
const data = await response.json();
return data;
}
throw new Error(
"Something went wrong while sending the email verification for password reset."
);
};
export default SendEmailOnPasswordReset;
@@ -1,17 +0,0 @@
/**
* This route send the verification email to the user's email (contains a 6-digit verification code)
* @param {*} email
*/
const sendVerificationEmail = (email: string) => {
fetch("/api/v1/signup/email/signup", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email
})
});
};
export default sendVerificationEmail;
-16
View File
@@ -1,16 +0,0 @@
const token = async () =>
fetch("/api/v1/auth/token", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
credentials: "include"
}).then(async (res) => {
if (res.status === 200) {
return (await res.json()).token;
}
console.log("Getting a new token failed");
return undefined;
});
export default token;
@@ -1,27 +0,0 @@
interface Props {
email: string;
code: string;
organizationId: string;
}
/**
* This route verifies the signup invite link
* @param {object} obj
* @param {string} obj.email - email that a user is trying to verify
* @param {string} obj.organizationId - id of organization that a user is trying to verify for
* @param {string} obj.code - code that a user received to the abovementioned email
* @returns
*/
const verifySignupInvite = ({ email, organizationId, code }: Props) => fetch("/api/v1/invite-org/verify", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email,
organizationId,
code
})
});
export default verifySignupInvite;
@@ -1,24 +0,0 @@
/**
* This is the route that get an encrypted private key (will be decrypted with a backup key)
* @param {object} obj
* @param {object} obj.verificationToken - this is the token that confirms that a user is the right one
* @returns
*/
const getBackupEncryptedPrivateKey = ({
verificationToken
}: {
verificationToken: string;
}) => fetch("/api/v1/password/backup-private-key", {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${ verificationToken}`
}
}).then(async (res) => {
if (res?.status !== 200) {
console.log("Failed to get the backup key");
}
return (await res?.json())?.backupPrivateKey;
});
export default getBackupEncryptedPrivateKey;
@@ -1,8 +0,0 @@
const publicKeyInfisical = () => fetch("/api/v1/key/publicKey/infisical", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
export default publicKeyInfisical;
@@ -1,57 +0,0 @@
interface Props {
protectedKey: string;
protectedKeyIV: string;
protectedKeyTag: string;
encryptedPrivateKey: string;
encryptedPrivateKeyIV: string;
encryptedPrivateKeyTag: string;
salt: string;
verifier: string;
verificationToken: string;
}
/**
* This is the route that resets the account password if all the previus steps were passed
* @param {object} obj
* @param {object} obj.verificationToken - this is the token that confirms that a user is the right one
* @param {object} obj.encryptedPrivateKey - the new encrypted private key (encrypted using the new password)
* @param {object} obj.iv
* @param {object} obj.tag
* @param {object} obj.salt
* @param {object} obj.verifier
* @returns
*/
const resetPasswordOnAccountRecovery = ({
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier,
verificationToken,
}: Props) => fetch("/api/v1/password/password-reset", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${verificationToken}`
},
body: JSON.stringify({
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
})
}).then(async (res) => {
if (res?.status !== 200) {
console.log("Failed to get the backup key");
}
return res;
});
export default resetPasswordOnAccountRecovery;
@@ -1,25 +0,0 @@
import { apiRequest } from "@app/config/request";
/**
* Verify MFA token [mfaToken] for user with email [email]
* @param {object} obj
* @param {string} obj.email - email of user
* @param {string} obj.mfaToken - MFA cod/token to verify
* @returns
*/
const verifyMfaToken = async ({
email,
mfaToken
}: {
email: string;
mfaToken: string;
}) => {
const { data } = await apiRequest.post("/api/v2/auth/mfa/verify", {
email,
mfaToken
});
return data;
}
export default verifyMfaToken;
+21 -18
View File
@@ -12,11 +12,10 @@ import Button from "@app/components/basic/buttons/Button";
import InputField from "@app/components/basic/InputField";
import passwordCheck from "@app/components/utilities/checks/PasswordCheck";
import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm";
import { useResetPassword,useVerifyPasswordResetCode } from "@app/hooks/api";
import { getBackupEncryptedPrivateKey } from "@app/hooks/api/auth/queries";
import { deriveArgonKey } from "../components/utilities/cryptography/crypto";
import EmailVerifyOnPasswordReset from "./api/auth/EmailVerifyOnPasswordReset";
import getBackupEncryptedPrivateKey from "./api/auth/getBackupEncryptedPrivateKey";
import resetPasswordOnAccountRecovery from "./api/auth/resetPasswordOnAccountRecovery";
// eslint-disable-next-line new-cap
const client = new jsrp.client();
@@ -34,6 +33,10 @@ export default function PasswordReset() {
const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false);
const router = useRouter();
const { mutateAsync: verifyPasswordResetCodeMutateAsync } = useVerifyPasswordResetCode();
const { mutateAsync: resetPasswordMutateAsync } = useResetPassword();
const parsedUrl = queryString.parse(router.asPath.split("?")[1]);
const token = parsedUrl.token as string;
const email = (parsedUrl.to as string)?.replace(" ", "+").trim();
@@ -43,7 +46,7 @@ export default function PasswordReset() {
e.preventDefault();
try {
const result = await getBackupEncryptedPrivateKey({ verificationToken });
setPrivateKey(
Aes256Gcm.decrypt({
ciphertext: result.encryptedPrivateKey,
@@ -53,7 +56,8 @@ export default function PasswordReset() {
})
);
setStep(3);
} catch {
} catch(err) {
console.error(err);
setBackupKeyError(true);
}
};
@@ -112,7 +116,7 @@ export default function PasswordReset() {
secret: Buffer.from(derivedKey.hash)
});
const response = await resetPasswordOnAccountRecovery({
await resetPasswordMutateAsync({
protectedKey,
protectedKeyIV,
protectedKeyTag,
@@ -123,11 +127,9 @@ export default function PasswordReset() {
verifier: result.verifier,
verificationToken
});
router.push("/login");
// if everything works, go the main dashboard page.
if (response?.status === 200) {
router.push("/login");
}
setLoading(false)
});
}
@@ -146,15 +148,16 @@ export default function PasswordReset() {
<Button
text="Confirm Email"
onButtonPressed={async () => {
const response = await EmailVerifyOnPasswordReset({
email,
code: token
});
if (response.status === 200) {
setVerificationToken((await response.json()).token);
try {
const response = await verifyPasswordResetCodeMutateAsync({
email,
code: token
});
setVerificationToken(response.token);
setStep(2);
} else {
console.log("ERROR", response);
} catch (err) {
console.log("ERROR", err);
router.push("/email-not-verified");
}
}}
+14 -12
View File
@@ -12,9 +12,9 @@ import InitialSignupStep from "@app/components/signup/InitialSignupStep";
import TeamInviteStep from "@app/components/signup/TeamInviteStep";
import UserInfoStep from "@app/components/signup/UserInfoStep";
import SecurityClient from "@app/components/utilities/SecurityClient";
import { useVerifyEmailVerificationCode } from "@app/hooks/api";
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
import checkEmailVerificationCode from "@app/pages/api/auth/CheckEmailVerificationCode";
/**
* @returns the signup page
@@ -33,6 +33,7 @@ export default function SignUp() {
const [isSignupWithEmail, setIsSignupWithEmail] = useState(false);
const [isCodeInputCheckLoading, setIsCodeInputCheckLoading] = useState(false);
const { t } = useTranslation();
const { mutateAsync } = useVerifyEmailVerificationCode();
useEffect(() => {
const tryAuth = async () => {
@@ -60,12 +61,12 @@ export default function SignUp() {
} else if (step === 2) {
setIsCodeInputCheckLoading(true);
// Checking if the code matches the email.
const response = await checkEmailVerificationCode({ email, code });
if (response.status === 200) {
const { token } = await response.json();
try {
const { token } = await mutateAsync({ email, code });
SecurityClient.setSignupToken(token);
setStep(3);
} else {
} catch(err) {
console.error(err);
setCodeError(true);
}
setIsCodeInputCheckLoading(false);
@@ -74,15 +75,16 @@ export default function SignUp() {
// when email service is not configured, skip step 2 and 5
useEffect(() => {
if (!serverDetails?.emailConfigured && step === 2) {
incrementStep();
}
(async () => {
if (!serverDetails?.emailConfigured && step === 2) {
incrementStep();
}
if (!serverDetails?.emailConfigured && step === 5) {
getOrganizations().then((userOrgs) => {
if (!serverDetails?.emailConfigured && step === 5) {
const userOrgs = await fetchOrganizations();
router.push(`/org/${userOrgs[0]._id}/overview`);
});
}
}
})();
}, [step]);
const renderView = (registerStep: number) => {
+26 -24
View File
@@ -7,7 +7,7 @@ import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { faCheck, faWarning, faXmark } from "@fortawesome/free-solid-svg-icons";
import { faWarning, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import jsrp from "jsrp";
import queryString from "query-string";
@@ -16,7 +16,6 @@ import { encodeBase64 } from "tweetnacl-util";
import Button from "@app/components/basic/buttons/Button";
import InputField from "@app/components/basic/InputField";
import attemptLogin from "@app/components/utilities/attemptLogin";
import checkPassword from "@app/components/utilities/checks/checkPassword";
import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm";
import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto";
@@ -26,11 +25,12 @@ import SecurityClient from "@app/components/utilities/SecurityClient";
import {
useGetCommonPasswords
} from "@app/hooks/api";
import {
completeAccountSignupInvite,
verifySignupInvite
} from "@app/hooks/api/auth/queries";
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
import completeAccountInformationSignupInvite from "./api/auth/CompleteAccountInformationSignupInvite";
import verifySignupInvite from "./api/auth/VerifySignupInvite";
// eslint-disable-next-line new-cap
const client = new jsrp.client();
@@ -141,7 +141,7 @@ export default function SignupInvite() {
const {
token: jwtToken
} = await completeAccountInformationSignupInvite({
} = await completeAccountSignupInvite({
email,
firstName,
lastName,
@@ -197,25 +197,27 @@ export default function SignupInvite() {
<Button
text="Confirm Email"
onButtonPressed={async () => {
const response = await verifySignupInvite({
email,
code: token,
organizationId
});
if (response.status === 200) {
const res = await response.json();
// user will have temp token if doesn't have an account
// then continue with account setup workflow
if (res?.token) {
SecurityClient.setSignupToken(res.token);
setStep(2);
} else {
// user will be redirected to dashboard
// if not logged in gets kicked out to login
router.push(`/org/${organizationId}/overview`);
try {
const response = await verifySignupInvite({
email,
code: token,
organizationId
});
if (response) {
// user will have temp token if doesn't have an account
// then continue with account setup workflow
if (response?.token) {
SecurityClient.setSignupToken(response.token);
setStep(2);
} else {
// user will be redirected to dashboard
// if not logged in gets kicked out to login
router.push(`/org/${organizationId}/overview`);
}
}
} else {
console.log("ERROR", response);
} catch (err) {
console.error(err);
router.push("/requestnewinvite");
}
}}
+4 -3
View File
@@ -7,10 +7,9 @@ import Button from "@app/components/basic/buttons/Button";
import InputField from "@app/components/basic/InputField";
import { EmailServiceSetupModal } from "@app/components/v2";
import { usePopUp } from "@app/hooks";
import { useSendPasswordResetEmail } from "@app/hooks/api";
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
import SendEmailOnPasswordReset from "./api/auth/SendEmailOnPasswordReset";
export default function VerifyEmail() {
const [loading, setLoading] = useState(false);
const [email, setEmail] = useState("");
@@ -18,12 +17,14 @@ export default function VerifyEmail() {
const { data: serverDetails } = useFetchServerStatus();
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp(["setUpEmail"] as const);
const { mutateAsync } = useSendPasswordResetEmail();
/**
* This function sends the verification email and forwards a user to the next step.
*/
const sendVerificationEmail = async () => {
if (email) {
await SendEmailOnPasswordReset({ email });
await mutateAsync({ email });
setStep(2);
}
};