fix: Strict password validation in login form
Strict password validation is for registration, change password, reset password and similar forms. In login form, we don't want to interrupt user with unnecessary validation as it's completely irrelevant what they actually entered. The one thing it's about is the correctness of the email and the password. Disabling strict validation for password is a kind of security through obscurity, which could filter some untargeted attempts for trying passwords by bots. If the bot immediatelly receives notes about the requirements, it has more imformation for better attacks.
This commit is contained in:
10 files changed
+49
-35
No files matched your search
@@ -4,7 +4,7 @@ import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack, Typography, Button } from "@mui/material";
|
||||
import { PasswordEndAdornment } from "../../Inputs/TextInput/Adornments";
|
||||
import TextInput from "../../Inputs/TextInput";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import { newOrChangedCredentials } from "../../../Validation/validation";
|
||||
import Alert from "../../Alert";
|
||||
import { update } from "../../../Features/Auth/authSlice";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
@@ -61,7 +61,7 @@ const PasswordPanel = () => {
|
||||
[name]: true,
|
||||
};
|
||||
|
||||
const validation = credentials.validate(
|
||||
const validation = newOrChangedCredentials.validate(
|
||||
{ ...updatedData },
|
||||
{ abortEarly: false, context: { password: updatedData.newPassword } }
|
||||
);
|
||||
@@ -79,7 +79,7 @@ const PasswordPanel = () => {
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const { error } = credentials.validate(localData, {
|
||||
const { error } = newOrChangedCredentials.validate(localData, {
|
||||
abortEarly: false,
|
||||
context: { password: localData.newPassword },
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Box, Button, Divider, Stack, Typography } from "@mui/material";
|
||||
import Avatar from "../../Avatar";
|
||||
import TextInput from "../../Inputs/TextInput";
|
||||
import ImageUpload from "../../Inputs/ImageUpload";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import { newOrChangedCredentials } from "../../../Validation/validation";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { clearAuthState, deleteUser, update } from "../../../Features/Auth/authSlice";
|
||||
import { clearUptimeMonitorState } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
@@ -58,7 +58,7 @@ const ProfilePanel = () => {
|
||||
[name]: value,
|
||||
}));
|
||||
|
||||
validateField({ [name]: value }, credentials, name);
|
||||
validateField({ [name]: value }, newOrChangedCredentials, name);
|
||||
};
|
||||
|
||||
// Validates input against provided schema and updates error state
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Button, ButtonGroup, Stack, Typography } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import TextInput from "../../Inputs/TextInput";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import { newOrChangedCredentials } from "../../../Validation/validation";
|
||||
import { networkService } from "../../../main";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import Select from "../../Inputs/Select";
|
||||
@@ -115,7 +115,7 @@ const TeamPanel = () => {
|
||||
email: newEmail,
|
||||
}));
|
||||
|
||||
const validation = credentials.validate({ email: newEmail }, { abortEarly: false });
|
||||
const validation = newOrChangedCredentials.validate({ email: newEmail }, { abortEarly: false });
|
||||
|
||||
setErrors((prev) => {
|
||||
const updatedErrors = { ...prev };
|
||||
@@ -142,7 +142,7 @@ const TeamPanel = () => {
|
||||
if (!toInvite.role.includes("user") || !toInvite.role.includes("admin"))
|
||||
setToInvite((prev) => ({ ...prev, role: ["user"] }));
|
||||
|
||||
const { error } = credentials.validate(
|
||||
const { error } = newOrChangedCredentials.validate(
|
||||
{ email: toInvite.email },
|
||||
{
|
||||
abortEarly: false,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createToast } from "../../Utils/toastUtils";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { forgotPassword } from "../../Features/Auth/authSlice";
|
||||
import { useEffect, useState } from "react";
|
||||
import { credentials } from "../../Validation/validation";
|
||||
import { newOrChangedCredentials } from "../../Validation/validation";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import TextInput from "../../Components/Inputs/TextInput";
|
||||
import Logo from "../../assets/icons/checkmate-icon.svg?react";
|
||||
@@ -35,7 +35,7 @@ const ForgotPassword = () => {
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const { error } = credentials.validate(form, { abortEarly: false });
|
||||
const { error } = newOrChangedCredentials.validate(form, { abortEarly: false });
|
||||
|
||||
if (error) {
|
||||
// validation errors
|
||||
@@ -75,7 +75,7 @@ const ForgotPassword = () => {
|
||||
const { value } = event.target;
|
||||
setForm({ email: value });
|
||||
|
||||
const { error } = credentials.validate({ email: value }, { abortEarly: false });
|
||||
const { error } = newOrChangedCredentials.validate({ email: value }, { abortEarly: false });
|
||||
|
||||
if (error) setErrors({ email: error.details[0].message });
|
||||
else delete errors.email;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import { loginCredentials } from "../../../Validation/validation";
|
||||
import { login } from "../../../Features/Auth/authSlice";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
@@ -59,7 +59,7 @@ const Login = () => {
|
||||
[name]: lowerCasedValue,
|
||||
}));
|
||||
|
||||
const { error } = credentials.validate(
|
||||
const { error } = loginCredentials.validate(
|
||||
{ [name]: lowerCasedValue },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
@@ -76,7 +76,7 @@ const Login = () => {
|
||||
event.preventDefault();
|
||||
|
||||
if (step === 0) {
|
||||
const { error } = credentials.validate(
|
||||
const { error } = loginCredentials.validate(
|
||||
{ email: form.email },
|
||||
{ abortEarly: false }
|
||||
);
|
||||
@@ -91,7 +91,7 @@ const Login = () => {
|
||||
setStep(1);
|
||||
}
|
||||
} else if (step === 1) {
|
||||
const { error } = credentials.validate(form, { abortEarly: false });
|
||||
const { error } = loginCredentials.validate(form, { abortEarly: false });
|
||||
|
||||
if (error) {
|
||||
// validation errors
|
||||
|
||||
@@ -8,7 +8,7 @@ import { StepOne } from "./StepOne";
|
||||
import { StepTwo } from "./StepTwo";
|
||||
import { StepThree } from "./StepThree";
|
||||
import { networkService } from "../../../main";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import { newOrChangedCredentials } from "../../../Validation/validation";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { register } from "../../../Features/Auth/authSlice";
|
||||
import Background from "../../../assets/Images/background-grid.svg?react";
|
||||
@@ -171,7 +171,7 @@ const Register = ({ isSuperAdmin }) => {
|
||||
* @returns {Object | undefined} - Returns the validation error object if there are validation errors; otherwise, `undefined`.
|
||||
*/
|
||||
const validateForm = (data, options = {}) => {
|
||||
const { error } = credentials.validate(data, {
|
||||
const { error } = newOrChangedCredentials.validate(data, {
|
||||
abortEarly: false,
|
||||
...options,
|
||||
});
|
||||
@@ -271,7 +271,7 @@ const Register = ({ isSuperAdmin }) => {
|
||||
[name]: lowerCasedValue,
|
||||
}));
|
||||
|
||||
const { error } = credentials.validate(
|
||||
const { error } = newOrChangedCredentials.validate(
|
||||
{ [name]: lowerCasedValue },
|
||||
{ abortEarly: false, context: { password: form.password } }
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack, Typography, Button } from "@mui/material";
|
||||
import { setNewPassword } from "../../Features/Auth/authSlice";
|
||||
import { createToast } from "../../Utils/toastUtils";
|
||||
import { credentials } from "../../Validation/validation";
|
||||
import { newOrChangedCredentials } from "../../Validation/validation";
|
||||
import Check from "../../Components/Check/Check";
|
||||
import TextInput from "../../Components/Inputs/TextInput";
|
||||
import { PasswordEndAdornment } from "../../Components/Inputs/TextInput/Adornments";
|
||||
@@ -34,7 +34,7 @@ const SetNewPassword = () => {
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const { error } = credentials.validate(form, {
|
||||
const { error } = newOrChangedCredentials.validate(form, {
|
||||
abortEarly: false,
|
||||
context: { password: form.password },
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
import { newOrChangedCredentials } from "../../../Validation/validation";
|
||||
|
||||
const getFeedbackStatus = (form, errors, field, criteria) => {
|
||||
const fieldErrors = errors[field];
|
||||
@@ -36,7 +36,7 @@ function useValidatePassword() {
|
||||
validateValue.password = form.password;
|
||||
}
|
||||
|
||||
const { error } = credentials.validate(validateValue, validateOptions);
|
||||
const { error } = newOrChangedCredentials.validate(validateValue, validateOptions);
|
||||
const errors = error?.details.map((error) => ({
|
||||
path: error.path[0],
|
||||
type: error.type,
|
||||
|
||||
@@ -27,7 +27,7 @@ const lastnameSchema = joi
|
||||
"Surname must contain only letters, spaces, apostrophes, or hyphens",
|
||||
});
|
||||
|
||||
const passwordSchema = joi
|
||||
const newPasswordSchema = joi
|
||||
.string()
|
||||
.trim()
|
||||
.min(8)
|
||||
@@ -64,7 +64,7 @@ const passwordSchema = joi
|
||||
special: "Password must contain at least one special character",
|
||||
});
|
||||
|
||||
const credentials = joi.object({
|
||||
const newOrChangedCredentials = joi.object({
|
||||
firstName: nameSchema,
|
||||
lastName: lastnameSchema,
|
||||
email: joi
|
||||
@@ -76,8 +76,8 @@ const credentials = joi.object({
|
||||
"string.empty": "authRegisterEmailRequired",
|
||||
"string.email": "authRegisterEmailInvalid",
|
||||
}),
|
||||
password: passwordSchema,
|
||||
newPassword: passwordSchema,
|
||||
password: newPasswordSchema,
|
||||
newPassword: newPasswordSchema,
|
||||
confirm: joi
|
||||
.string()
|
||||
.trim()
|
||||
@@ -97,6 +97,23 @@ const credentials = joi.object({
|
||||
inviteToken: joi.string().allow(""),
|
||||
});
|
||||
|
||||
const loginCredentials = joi.object({
|
||||
email: joi
|
||||
.string()
|
||||
.trim()
|
||||
.email({ tlds: { allow: false } })
|
||||
.lowercase()
|
||||
.messages({
|
||||
"string.empty": "authRegisterEmailRequired",
|
||||
"string.email": "authRegisterEmailInvalid",
|
||||
}),
|
||||
password: joi
|
||||
.string()
|
||||
.messages({
|
||||
"string.empty": "Please enter your password",
|
||||
}),
|
||||
});
|
||||
|
||||
const monitorValidation = joi.object({
|
||||
url: joi.when("type", {
|
||||
is: "docker",
|
||||
@@ -391,7 +408,8 @@ const infrastructureMonitorValidation = joi.object({
|
||||
});
|
||||
|
||||
export {
|
||||
credentials,
|
||||
newOrChangedCredentials,
|
||||
loginCredentials,
|
||||
imageValidation,
|
||||
monitorValidation,
|
||||
settingsValidation,
|
||||
|
||||
@@ -26,14 +26,10 @@ const loginValidation = joi.object({
|
||||
.string()
|
||||
.email()
|
||||
.required()
|
||||
.custom((value, helpers) => {
|
||||
const lowercasedValue = value.toLowerCase();
|
||||
if (value !== lowercasedValue) {
|
||||
return helpers.message("Email must be in lowercase");
|
||||
}
|
||||
return lowercasedValue;
|
||||
}),
|
||||
password: joi.string().min(8).required().pattern(passwordPattern),
|
||||
.lowercase(),
|
||||
password: joi
|
||||
.string()
|
||||
.required(),
|
||||
});
|
||||
const nameValidation = joi
|
||||
.string()
|
||||
|
||||
Reference in new issue
Block a user