diff --git a/frontend/public/locales/en/translations.json b/frontend/public/locales/en/translations.json index 84554d8fc..8bac30094 100644 --- a/frontend/public/locales/en/translations.json +++ b/frontend/public/locales/en/translations.json @@ -234,9 +234,12 @@ "validate-base": "Password should contain:", "validate-too-short": "at least 14 characters", "validate-too-long": "at most 100 characters", + "validate-uppercase": "at least 1 uppercase character", + "validate-lowercase": "at least 1 lowercase character", "validate-number": "at least 1 number", - "validate-case": "at least 1 lowercase character", - "validate-breached": "The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password." + "validate-special-char": "at least 1 special character", + "validate-repeated-char": "at most 2 repeated consecutive characters", + "validate-is-breached": "The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password." }, "token": { "service-tokens": "Service Tokens", diff --git a/frontend/public/locales/es/translations.json b/frontend/public/locales/es/translations.json index ffb0a5dd4..7f3bbf84f 100644 --- a/frontend/public/locales/es/translations.json +++ b/frontend/public/locales/es/translations.json @@ -229,11 +229,14 @@ "current-wrong": "La contraseña actual puede puede que sea incorrecta", "new": "Nueva contraseña", "validate-base": "La contraseña debe contener:", - "validate-too-short": "como mínimo 14 caracteres", + "validate-too-short": "al menos 14 caracteres", "validate-too-long": "como máximo 100 caracteres", - "validate-number": "como mínimo 1 número", - "validate-case": "como mínimo 1 letra en minúsculas", - "validate-breached": "La contraseña que proporcionó se encuentra en una lista de contraseñas comúnmente utilizadas en otros sitios web. Inténtelo de nuevo con una contraseña más segura." + "validate-uppercase": "al menos 1 carácter en mayúscula", + "validate-lowercase": "al menos 1 carácter en minúsculas", + "validate-number": "al menos 1 número", + "validate-special-char": "al menos 1 carácter especial", + "validate-repeated-char": "au plus 2 caracteres consecutivos repetidos", + "validate-breached": "La contraseña que proporcionó se encuentra en una lista de contraseñas comúnmente utilizadas en otros sitios web. Vuelva a intentarlo con una contraseña más segura." }, "token": { "service-tokens": "Tokens de servicio", diff --git a/frontend/public/locales/fr/translations.json b/frontend/public/locales/fr/translations.json index 7ce136191..25e2de3c0 100644 --- a/frontend/public/locales/fr/translations.json +++ b/frontend/public/locales/fr/translations.json @@ -218,9 +218,12 @@ "validate-base": "Le mot de passe doit contenir:", "validate-too-short": "au moins 14 caractères", "validate-too-long": "au maximum 100 caractères", + "validate-uppercase": "au moins 1 caractère miniscule", + "validate-lowercase": "au moins 1 caractère majuscule", "validate-number": "au moins 1 chiffre", - "validate-case": "au moins 1 caractère miniscule", - "validate-breached": "Le mot de passe que vous avez fourni figure dans une liste de mots de passe couramment utilisés sur d'autres sites Web. Veuillez réessayer avec un mot de passe plus fort." + "validate-special-char": "au moins 1 caractère spécial", + "validate-repeated-char": "au plus 2 caractères consécutifs répétés", + "validate-is-breached": "Le mot de passe que vous avez fourni figure dans une liste de mots de passe couramment utilisés sur d'autres sites Web. Veuillez réessayer avec un mot de passe plus fort." }, "token": { "service-tokens": "Jetons de service", diff --git a/frontend/public/locales/ko/translations.json b/frontend/public/locales/ko/translations.json index bd86c9fdc..c6bec94f4 100644 --- a/frontend/public/locales/ko/translations.json +++ b/frontend/public/locales/ko/translations.json @@ -183,10 +183,13 @@ "new": "새 비밀번호", "current-wrong": "현재 비밀번호가 잘못되었어요", "validate-base": "비밀번호는 다음 조건을 만족해야 합니다:", - "validate-too-short": "14 글자 이상", - "validate-too-long": "100 자 이하", - "validate-number": "1개 이상의 숫자", - "validate-case": "1개 이상의 소문자", + "validate-too-short": "최소 14자", + "validate-too-long": "최대 100자", + "validate-uppercase": "최소 1개의 대문자", + "validate-lowercase": "최소 1개의 소문자", + "validate-number": "숫자 1개 이상", + "validate-special-char": "특수 문자 1개 이상", + "validate-repeated-char": "최대 2개의 반복되는 연속 문자", "validate-breached": "귀하가 제공한 비밀번호는 다른 웹사이트에서 일반적으로 사용되는 비밀번호 목록에 포함되어 있습니다. 더 강력한 비밀번호로 다시 시도해 주세요." }, "token": { diff --git a/frontend/public/locales/pt-BR/translations.json b/frontend/public/locales/pt-BR/translations.json index 9412facdb..d6958f5a8 100644 --- a/frontend/public/locales/pt-BR/translations.json +++ b/frontend/public/locales/pt-BR/translations.json @@ -213,8 +213,11 @@ "validate-base": "A senha deve conter:", "validate-too-short": "pelo menos 14 caracteres", "validate-too-long": "no máximo 100 caracteres", + "validate-uppercase": "pelo menos 1 caractere maiúsculo", + "validate-lowercase": "pelo menos 1 caractere minúsculo", "validate-number": "pelo menos 1 número", - "validate-case": "pelo menos 1 caractere minúsculo", + "validate-special-char": "pelo menos 1 caractere especial", + "validate-repeated-char": "au plus 2 caractères consécutifs répétés", "validate-breached": "A senha que você forneceu está em uma lista de senhas comumente usadas em outros sites. Tente novamente com uma senha mais forte." }, "token": { diff --git a/frontend/public/locales/tr/translations.json b/frontend/public/locales/tr/translations.json index 53d1bfddc..cf441d0ae 100644 --- a/frontend/public/locales/tr/translations.json +++ b/frontend/public/locales/tr/translations.json @@ -231,8 +231,11 @@ "validate-base": "Şifre kısıtlamaları:", "validate-too-short": "en az 14 karakter", "validate-too-long": "en fazla 100 karakter", - "validate-number": "en az 1 rakam", - "validate-case": "en az 1 küçük harf", + "validate-uppercase": "en az 1 büyük harf karakter", + "validate-lowercase": "en az 1 küçük harf karakter", + "validate-number": "en az 1 sayı", + "validate-special-char": "en az 1 özel karakter", + "validate-repeated-char": "veya artı 2 karakter ardışık tekrar", "validate-breached": "Sağladığınız şifre, diğer web sitelerinde yaygın olarak kullanılan şifreler listesinde yer almaktadır. Lütfen daha güçlü bir şifre ile tekrar deneyiniz." }, "token": { diff --git a/frontend/src/components/signup/UserInfoStep.tsx b/frontend/src/components/signup/UserInfoStep.tsx index 581298c42..45ae09d0d 100644 --- a/frontend/src/components/signup/UserInfoStep.tsx +++ b/frontend/src/components/signup/UserInfoStep.tsx @@ -39,14 +39,15 @@ interface UserInfoStepProps { } type Errors = { - tooShort?: string, - tooLong?: string, - upperCase?: string, - lowerCase?: string, - number?: string, - specialChar?: string, - repeatedChar?: string, - breachedPassword?: string + tooShort?: string; + tooLong?: string; + upperCase?: string; + lowerCase?: string; + number?: string; + specialChar?: string; + repeatedChar?: string; + isBeachedPassword?: string; + isCommonPassword?: string; }; /** @@ -73,7 +74,7 @@ export default function UserInfoStep({ setOrganizationName, attributionSource, setAttributionSource, - providerAuthToken, + providerAuthToken }: UserInfoStepProps): JSX.Element { const { data: commonPasswords } = useGetCommonPasswords(); const [nameError, setNameError] = useState(false); @@ -101,7 +102,7 @@ export default function UserInfoStep({ } else { setOrganizationNameError(false); } - + errorCheck = await checkPassword({ password, commonPasswords, @@ -176,7 +177,7 @@ export default function UserInfoStep({ salt: result.salt, verifier: result.verifier, organizationName, - attributionSource, + attributionSource }); // unset signup JWT token and set JWT token @@ -193,7 +194,7 @@ export default function UserInfoStep({ }); const userOrgs = await fetchOrganizations(); - + const orgId = userOrgs[0]?._id; const project = await ProjectService.initProject({ organizationId: orgId, @@ -217,13 +218,15 @@ export default function UserInfoStep({ }; return ( -
-

+

+

{t("signup.step3-message")}

-
-
-

Your Name

+
+
+

+ Your Name +

setName(e.target.value)} @@ -232,10 +235,16 @@ export default function UserInfoStep({ autoComplete="given-name" className="h-12" /> - {nameError &&

Please, specify your name

} + {nameError && ( +

+ Please, specify your name +

+ )}
-
-

Organization Name

+
+

+ Organization Name +

setOrganizationName(e.target.value)} @@ -243,10 +252,16 @@ export default function UserInfoStep({ isRequired className="h-12" /> - {organizationNameError &&

Please, specify your organization name

} + {organizationNameError && ( +

+ Please, specify your organization name +

+ )}
-
-

Where did you hear about us? (optional)

+
+

+ Where did you hear about us? (optional) +

setAttributionSource(e.target.value)} @@ -254,7 +269,7 @@ export default function UserInfoStep({ className="h-12" />
-
+
{ @@ -274,23 +289,20 @@ export default function UserInfoStep({ /> {Object.keys(errors).length > 0 && (
-
{t("section.password.validate-base")}
+
+ {t("section.password.validate-base")} +
{Object.keys(errors).map((key) => { if (errors[key as keyof Errors]) { return ( -
+
-
-

- {errors[key as keyof Errors]} -

+

{errors[key as keyof Errors]}

); } @@ -300,18 +312,21 @@ export default function UserInfoStep({
)}
-
-
+
+
+ > + {" "} + {String(t("signup.signup"))}{" "} +
diff --git a/frontend/src/components/utilities/checks/PasswordCheck.ts b/frontend/src/components/utilities/checks/PasswordCheck.ts index 133eb5a35..be0a275f4 100644 --- a/frontend/src/components/utilities/checks/PasswordCheck.ts +++ b/frontend/src/components/utilities/checks/PasswordCheck.ts @@ -6,9 +6,12 @@ interface PasswordCheckProps { errorCheck: boolean; setPasswordErrorTooShort: (value: boolean) => void; setPasswordErrorTooLong: (value: boolean) => void; - setPasswordErrorNumber: (value: boolean) => void; + setPasswordErrorUpperCase: (value: boolean) => void; setPasswordErrorLowerCase: (value: boolean) => void; - setPasswordErrorIsBreached: (value: boolean) => void; + setPasswordErrorNumber: (value: boolean) => void; + setPasswordErrorSpecialChar: (value: boolean) => void; + setPasswordErrorRepeatedChar: (value: boolean) => void; + setPasswordErrorIsBreachedPassword: (value: boolean) => void; } /** @@ -18,11 +21,15 @@ const passwordCheck = async ({ password, setPasswordErrorTooShort, setPasswordErrorTooLong, - setPasswordErrorNumber, + setPasswordErrorUpperCase, setPasswordErrorLowerCase, - setPasswordErrorIsBreached, + setPasswordErrorNumber, + setPasswordErrorSpecialChar, + setPasswordErrorRepeatedChar, + setPasswordErrorIsBreachedPassword, errorCheck }: PasswordCheckProps) => { + // tooShort if (!password || password.length < 14) { setPasswordErrorTooShort(true); errorCheck = true; @@ -30,6 +37,7 @@ const passwordCheck = async ({ setPasswordErrorTooShort(false); } + // tooLong if (password.length > 100) { setPasswordErrorTooLong(true); errorCheck = true; @@ -37,51 +45,54 @@ const passwordCheck = async ({ setPasswordErrorTooLong(false); } - if (!/\d/.test(password)) { + // upperCase + if (!/[A-Z]/.test(password)) { + setPasswordErrorUpperCase(true); + errorCheck = true; + } else { + setPasswordErrorUpperCase(false); + } + + // lowerCase + if (!/[a-z]/.test(password)) { + setPasswordErrorLowerCase(true); + errorCheck = true; + } else { + setPasswordErrorLowerCase(false); + } + + // number + if (!/[0-9]/.test(password)) { setPasswordErrorNumber(true); errorCheck = true; } else { setPasswordErrorNumber(false); } - if (!/[a-z]/.test(password)) { - setPasswordErrorLowerCase(true); + // specialChar + if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { + setPasswordErrorSpecialChar(true); errorCheck = true; - // } else if (/(.)(?:(?!\1).){1,2}/.test(password)) { - // console.log(111) - // setPasswordError(true); - // setPasswordErrorMessage("Password should not contain repeating characters."); - // errorCheck = true; - // } else if (RegExp(`[${email}]`).test(password)) { - // console.log(222) - // setPasswordError(true); - // setPasswordErrorMessage("Password should not contain your email."); - // errorCheck = true; } else { - setPasswordErrorLowerCase(false); + setPasswordErrorSpecialChar(false); } + // repeatedChar + if (/([A-Za-z0-9])\1\1\1/.test(password)) { + setPasswordErrorRepeatedChar(true); + errorCheck = true; + } else { + setPasswordErrorRepeatedChar(false); + } + + // breachedPassword if (await checkIsPasswordBreached(password)) { - setPasswordErrorIsBreached(true); + setPasswordErrorIsBreachedPassword(true); errorCheck = true; } else { - setPasswordErrorIsBreached(false); + setPasswordErrorIsBreachedPassword(false); } - // if (!/[A-Z]/.test(password)) { - // setPasswordErrorUpperCase(true); - // errorCheck = true; - // } else { - // setPasswordErrorUpperCase(false); - // } - - // if (!/(?=.*[!@#$%^&*])/.test(password)) { - // setPasswordErrorSpecialChar(true); - // // "Please add at least 1 special character (*, !, #, %)." - // errorCheck = true; - // } else { - // setPasswordErrorSpecialChar(false); - // } return errorCheck; }; diff --git a/frontend/src/components/utilities/checks/checkPassword.ts b/frontend/src/components/utilities/checks/checkPassword.ts index 210d3f040..8ad4c6032 100644 --- a/frontend/src/components/utilities/checks/checkPassword.ts +++ b/frontend/src/components/utilities/checks/checkPassword.ts @@ -8,8 +8,8 @@ type Errors = { number?: string; specialChar?: string; repeatedChar?: string; - commonPassword?: string; - breachedPassword?: string; + isBreachedPassword?: string; + isCommonPassword?: string; }; interface CheckPasswordParams { @@ -20,12 +20,15 @@ interface CheckPasswordParams { /** * Validate that the password [password]: - * - Contains at least 14 characters long - * - Contains at most 100 characters long + * - Contains at least 14 characters + * - Contains at most 100 characters * - Contains at least 1 uppercase character (A-Z) * - Contains at least 1 lowercase character (a-z) * - Contains at least 1 number (0-9) + * - Contains at least 1 special character * - Does not contain 3 repeat, consecutive characters + * - Is not in a database of breached passwords + * - Is not in a list of common passwords * * The function returns whether or not the password [password] * passes the minimum requirements above. It sets errors on @@ -42,41 +45,50 @@ const checkPassword = async ({ }: CheckPasswordParams): Promise => { const errors: Errors = {}; + // tooShort if (password.length < 14) { errors.tooShort = "at least 14 characters"; } + // toolong if (password.length > 100) { errors.tooLong = "at most 100 characters"; } + // upperCase if (!/[A-Z]/.test(password)) { errors.upperCase = "at least 1 uppercase character (A-Z)"; } + // lowerCase if (!/[a-z]/.test(password)) { errors.lowerCase = "at least 1 lowercase character (a-z)"; } + // number if (!/[0-9]/.test(password)) { errors.number = "at least 1 number (0-9)"; } + // specialChar if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { errors.specialChar = "at least 1 special character (!@#$%^&*(),.?)"; } + // repeatedChar if (/([A-Za-z0-9])\1\1\1/.test(password)) { errors.repeatedChar = "No 3 repeat, consecutive characters"; } + // breachedPassword if (await checkIsPasswordBreached(password)) { - errors.breachedPassword = + errors.isBreachedPassword = "The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password."; } + // commonPassword if (commonPasswords.includes(password)) { - errors.commonPassword = "No common passwords"; + errors.isCommonPassword = "No common passwords"; } setErrors(errors); diff --git a/frontend/src/pages/password-reset.tsx b/frontend/src/pages/password-reset.tsx index 6aa3ea275..4a58f09fb 100644 --- a/frontend/src/pages/password-reset.tsx +++ b/frontend/src/pages/password-reset.tsx @@ -30,9 +30,12 @@ export default function PasswordReset() { const [backupKeyError, setBackupKeyError] = useState(false); const [passwordErrorTooShort, setPasswordErrorTooShort] = useState(false); const [passwordErrorTooLong, setPasswordErrorTooLong] = useState(false); - const [passwordErrorNumber, setPasswordErrorNumber] = useState(false); + const [passwordErrorUpperCase, setPasswordErrorUpperCase] = useState(false); const [passwordErrorLowerCase, setPasswordErrorLowerCase] = useState(false); - const [passwordErrorIsBreached, setPasswordErrorIsBreached] = useState(false); + const [passwordErrorNumber, setPasswordErrorNumber] = useState(false); + const [passwordErrorSpecialChar, setPasswordErrorSpecialChar] = useState(false); + const [passwordErrorRepeatedChar, setPasswordErrorRepeatedChar] = useState(false); + const [passwordErrorIsBreachedPassword, setPasswordErrorIsBreachedPassword] = useState(false); const router = useRouter(); @@ -43,7 +46,7 @@ export default function PasswordReset() { const token = parsedUrl.token as string; const email = (parsedUrl.to as string)?.replace(" ", "+").trim(); - // Unencrypt the private key with a backup key + // Decrypt the private key with a backup key const getEncryptedKeyHandler = async (e: FormEvent) => { e.preventDefault(); try { @@ -71,9 +74,12 @@ export default function PasswordReset() { password: newPassword, setPasswordErrorTooShort, setPasswordErrorTooLong, - setPasswordErrorNumber, + setPasswordErrorUpperCase, setPasswordErrorLowerCase, - setPasswordErrorIsBreached, + setPasswordErrorNumber, + setPasswordErrorSpecialChar, + setPasswordErrorRepeatedChar, + setPasswordErrorIsBreachedPassword, errorCheck: false }); @@ -229,9 +235,12 @@ export default function PasswordReset() { password, setPasswordErrorTooShort, setPasswordErrorTooLong, - setPasswordErrorNumber, + setPasswordErrorUpperCase, setPasswordErrorLowerCase, - setPasswordErrorIsBreached, + setPasswordErrorNumber, + setPasswordErrorSpecialChar, + setPasswordErrorRepeatedChar, + setPasswordErrorIsBreachedPassword, errorCheck: false }); }} @@ -241,9 +250,12 @@ export default function PasswordReset() { error={ passwordErrorTooShort && passwordErrorTooLong && - passwordErrorNumber && + passwordErrorUpperCase && passwordErrorLowerCase && - passwordErrorIsBreached + passwordErrorNumber && + passwordErrorSpecialChar && + passwordErrorRepeatedChar && + passwordErrorIsBreachedPassword } autoComplete="new-password" id="new-password" @@ -251,9 +263,12 @@ export default function PasswordReset() {
{passwordErrorTooShort || passwordErrorTooLong || - passwordErrorNumber || + passwordErrorUpperCase || passwordErrorLowerCase || - passwordErrorIsBreached ? ( + passwordErrorNumber || + passwordErrorSpecialChar || + passwordErrorRepeatedChar || + passwordErrorIsBreachedPassword ? (
Password should contain:
@@ -277,13 +292,15 @@ export default function PasswordReset() {
- {passwordErrorNumber ? ( + {passwordErrorUpperCase ? ( ) : ( )} -
- at least 1 number +
+ at least 1 uppercase character
@@ -297,14 +314,54 @@ export default function PasswordReset() { > at least 1 lowercase character
+
+
+ {passwordErrorNumber ? ( + + ) : ( + + )} +
+ at least 1 number +
- {passwordErrorIsBreached ? ( + {passwordErrorSpecialChar ? ( ) : ( )}
+ at least 1 special character +
+
+
+ {passwordErrorRepeatedChar ? ( + + ) : ( + + )} +
+ at most 2 repeated characters +
+
+
+ {passwordErrorIsBreachedPassword ? ( + + ) : ( + + )} +
The password you provided is in a list of passwords commonly used on other websites. Please try again with a stronger password. diff --git a/frontend/src/views/Settings/PersonalSettingsPage/ChangePasswordSection/ChangePasswordSection.tsx b/frontend/src/views/Settings/PersonalSettingsPage/ChangePasswordSection/ChangePasswordSection.tsx index 86cb58a01..725fb0e47 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/ChangePasswordSection/ChangePasswordSection.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/ChangePasswordSection/ChangePasswordSection.tsx @@ -21,8 +21,8 @@ type Errors = { number?: string; specialChar?: string; repeatedChar?: string; - commonPassword?: string; - breachedPassword?: string; + isBreachedPassword?: string; + isCommonPassword?: string; }; const schema = yup diff --git a/frontend/src/views/Signup/components/UserInfoSSOStep/UserInfoSSOStep.tsx b/frontend/src/views/Signup/components/UserInfoSSOStep/UserInfoSSOStep.tsx index c44fab790..f142a2bef 100644 --- a/frontend/src/views/Signup/components/UserInfoSSOStep/UserInfoSSOStep.tsx +++ b/frontend/src/views/Signup/components/UserInfoSSOStep/UserInfoSSOStep.tsx @@ -1,4 +1,3 @@ - import crypto from "crypto"; import React, { useEffect, useState } from "react"; @@ -25,24 +24,25 @@ import ProjectService from "@app/services/ProjectService"; const client = new jsrp.client(); type Props = { - setStep: (step: number) => void; - email: string; - password: string; - setPassword: (value: string) => void; - name: string; - providerOrganizationName: string; - providerAuthToken?: string; -} + setStep: (step: number) => void; + email: string; + password: string; + setPassword: (value: string) => void; + name: string; + providerOrganizationName: string; + providerAuthToken?: string; +}; type Errors = { - tooShort?: string, - tooLong?: string, - upperCase?: string, - lowerCase?: string, - number?: string, - specialChar?: string, - repeatedChar?: string, - breachedPassword?: string + tooShort?: string; + tooLong?: string; + upperCase?: string; + lowerCase?: string; + number?: string; + specialChar?: string; + repeatedChar?: string; + isBeachedPassword?: string; + isCommonPassword?: string; }; /** @@ -65,7 +65,7 @@ export const UserInfoSSOStep = ({ password, setPassword, setStep, - providerAuthToken, + providerAuthToken }: Props) => { const { data: commonPasswords } = useGetCommonPasswords(); const [nameError, setNameError] = useState(false); @@ -99,7 +99,7 @@ export const UserInfoSSOStep = ({ } else { setOrganizationNameError(false); } - + errorCheck = await checkPassword({ password, commonPasswords, @@ -212,15 +212,17 @@ export const UserInfoSSOStep = ({ setIsLoading(false); } }; - + return ( -
-

+

+

{t("signup.step3-message")}

-
-
-

Your Name

+
+
+

+ Your Name +

- {nameError &&

Please, specify your name

} + {nameError && ( +

+ Please, specify your name +

+ )}
{providerOrganizationName === undefined && ( -
-

Organization Name

+
+

+ Organization Name +

- {organizationNameError &&

Please, specify your organization name

} + {organizationNameError && ( +

+ Please, specify your organization name +

+ )}
)} {providerOrganizationName === undefined && ( -
-

Where did you hear about us? (optional)

+
+

+ Where did you hear about us? (optional) +

setAttributionSource(e.target.value)} @@ -256,12 +270,12 @@ export const UserInfoSSOStep = ({ />
)} -
+
{ + onChangeHandler={async (pass: string) => { setPassword(pass); - checkPassword({ + await checkPassword({ password: pass, commonPasswords, setErrors @@ -274,26 +288,27 @@ export const UserInfoSSOStep = ({ autoComplete="new-password" id="new-password" /> -
Infisical Password is used as part of the encryption mechanism so that even the authentication provider is not able to access your secrets.
+
+ + Infisical Password is used as part of the encryption mechanism so that even the + authentication provider is not able to access your secrets. +
{Object.keys(errors).length > 0 && (
-
{t("section.password.validate-base")}
+
+ {t("section.password.validate-base")} +
{Object.keys(errors).map((key) => { if (errors[key as keyof Errors]) { return ( -
+
-
-

- {errors[key as keyof Errors]} -

+

{errors[key as keyof Errors]}

); } @@ -303,21 +318,24 @@ export const UserInfoSSOStep = ({
)}
-
-
+
+
+ > + {" "} + {String(t("signup.signup"))}{" "} +
); -} +};