From b7ea798d1a130a74f6c190136cdee51a2818c424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Eduardo=20=C3=81lvarez=20Lerebours?= <45927529+JE1999@users.noreply.github.com> Date: Sat, 20 May 2023 09:29:35 -0400 Subject: [PATCH] feat(iam): add pwned password validation (#29) * [ADD] pwned * [ADD] types sha1 * [REF] hibp integration * lint: use prettier and better wording --------- Co-authored-by: Gustavo Valverde --- package.json | 1 + .../elements/boxContentCenter/index.tsx | 2 +- src/pages/api/pwned/[password].ts | 24 +++++ src/pages/register/index.tsx | 2 +- src/pages/register/stepper/step3.tsx | 101 ++++++++++-------- yarn.lock.REMOVED.git-id | 2 +- 6 files changed, 87 insertions(+), 45 deletions(-) create mode 100644 src/pages/api/pwned/[password].ts diff --git a/package.json b/package.json index 6dcba917..2192ca95 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "cryptr": "^6.2.0", "eslint": "^8.40.0", "eslint-config-next": "13.2.3", + "hibp": "^13.0.0", "next": "13.2.3", "prop-types": ">=15.7.0", "react": "18.2.0", diff --git a/src/components/elements/boxContentCenter/index.tsx b/src/components/elements/boxContentCenter/index.tsx index 0c757934..28ed31c8 100644 --- a/src/components/elements/boxContentCenter/index.tsx +++ b/src/components/elements/boxContentCenter/index.tsx @@ -11,7 +11,7 @@ export default function BoxContentCenter({ children }: any) { alignItems: "center", }} > - + {children} diff --git a/src/pages/api/pwned/[password].ts b/src/pages/api/pwned/[password].ts new file mode 100644 index 00000000..caa5ab18 --- /dev/null +++ b/src/pages/api/pwned/[password].ts @@ -0,0 +1,24 @@ +import { NextApiRequest, NextApiResponse } from 'next/types'; +import { pwnedPassword } from 'hibp'; +import { Crypto } from '@/helpers'; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +): Promise { + const { token } = req.cookies; + + if (token !== process.env.NEXT_PUBLIC_COOKIE_KEY) { + return res.status(401).send(); + } + + const { password } = req.query; + + if (typeof password !== 'undefined') { + const passwordKey = Array.isArray(password) ? password[0] : password; + const data = await pwnedPassword(Crypto.decrypt(passwordKey)); + res.status(200).json(data); + } else { + res.status(400); + } +} diff --git a/src/pages/register/index.tsx b/src/pages/register/index.tsx index a29365f7..fbf79b46 100644 --- a/src/pages/register/index.tsx +++ b/src/pages/register/index.tsx @@ -9,7 +9,7 @@ export default function Index() { diff --git a/src/pages/register/stepper/step3.tsx b/src/pages/register/stepper/step3.tsx index 3299042d..0b3854ad 100644 --- a/src/pages/register/stepper/step3.tsx +++ b/src/pages/register/stepper/step3.tsx @@ -5,7 +5,11 @@ import { useState } from 'react'; import * as yup from 'yup'; import axios from 'axios'; -import { AlertError, AlertWarning } from '@/components/elements/alert'; +import { + AlertError, + AlertErrorMessage, + AlertWarning, +} from '@/components/elements/alert'; import { GridContainer, GridItem } from '@/components/elements/grid'; import LoadingBackdrop from '@/components/elements/loadingBackdrop'; import PasswordLevel from '@/components/elements/passwordLevel'; @@ -42,14 +46,16 @@ const schema = yup.object({ }); export default function Step3({ handleNext, infoCedula }: any) { + const [loadingValidatingPassword, setLoadingValidatingPassword] = + useState(false); const [loading, setLoading] = useState(false); const [passwordLevel, setPasswordLevel] = useState({}); + const [isPwned, setIsPwned] = useState(false); const { register, handleSubmit, formState: { errors }, - getValues, setValue, } = useForm({ mode: 'onChange', @@ -60,36 +66,58 @@ export default function Step3({ handleNext, infoCedula }: any) { const level = passwordStrength(password); setPasswordLevel(level); setValue('password', password); + setIsPwned(false); }; const onSubmit = (data: IFormInputs) => { + if (isPwned) return; if (passwordLevel.id !== 3) return; - setLoading(true); + setLoadingValidatingPassword(true); const password = Crypto.encrypt(data.password); - axios - .post('/api/iam', { - email: data.email, - username: infoCedula.id, - password, - }) - .then(() => { - handleNext(); - }) - .catch((err) => { - if (err?.response?.status === 409) { - AlertWarning('El correo electrónico ya está registrado.'); - } else { - AlertError(); - } - }) - .finally(() => setLoading(false)); + if (!isPwned) { + axios + .get(`/api/pwned/${password}`) + .then((res) => { + console.log(res); + const isPwnedIncludes = res.data === 0 ? false : true; + setIsPwned(isPwnedIncludes); + if (!isPwnedIncludes) { + setLoadingValidatingPassword(false); + setLoading(true); + axios + .post('/api/iam', { + email: data.email, + username: infoCedula.id, + password, + }) + .then(() => { + handleNext(); + }) + .catch((err) => { + if (err?.response?.status === 409) { + AlertWarning('El correo electrónico ya está registrado.'); + } else { + AlertError(); + } + }) + .finally(() => setLoading(false)); + } + }) + .catch(() => { + return AlertWarning('No pudimos validar si la contraseña es segura.'); + }) + .finally(() => setLoadingValidatingPassword(false)); + } }; return ( <> - {loading && } + {loadingValidatingPassword && ( + + )} + {loading && }
Para finalizar tu registro completa los siguientes campos: @@ -166,10 +194,6 @@ export default function Step3({ handleNext, infoCedula }: any) { { - e.preventDefault(); - return false; - }} autoComplete="off" {...register('password')} onChange={(e: React.ChangeEvent) => @@ -189,10 +213,6 @@ export default function Step3({ handleNext, infoCedula }: any) { { - e.preventDefault(); - return false; - }} autoComplete="off" {...register('passwordConfirm')} disabled={passwordLevel.id === 3 ? false : true} @@ -200,20 +220,17 @@ export default function Step3({ handleNext, infoCedula }: any) { + {isPwned && ( + + + + )} + - - value !== null && value !== undefined && value !== '' - ) === false - ? true - : false - } - > - ACEPTAR Y CONFIRMAR - + CREAR CUENTA ÚNICA diff --git a/yarn.lock.REMOVED.git-id b/yarn.lock.REMOVED.git-id index 60b974bf..9be85a84 100644 --- a/yarn.lock.REMOVED.git-id +++ b/yarn.lock.REMOVED.git-id @@ -1 +1 @@ -7a5ee94e69cf3c51f830ec8554b70732b07e624b \ No newline at end of file +c430adebb4598c04f3378557d9d1f330dc26253d \ No newline at end of file