feat(auth): enforce password policy in reset and change flows
This commit is contained in:
@@ -12,10 +12,11 @@ import { Button } from "../components/ui/button"
|
||||
import { Camera, Edit2, Trash2, User as UserIcon, UploadCloud, X, Check } from "lucide-react"
|
||||
import JalaliDatePicker from "../components/ui/JalaliDatePicker"
|
||||
import { toast } from "sonner"
|
||||
import { Modal } from "../components/Modal"
|
||||
import { Input } from "../components/ui/input"
|
||||
import { TextAreaInput } from "../components/ui/TextAreaInput"
|
||||
import { AuthPasswordField } from "./auth/AuthPasswordField"
|
||||
import { Modal } from "../components/Modal"
|
||||
import { Input } from "../components/ui/input"
|
||||
import { TextAreaInput } from "../components/ui/TextAreaInput"
|
||||
import { AuthPasswordField } from "./auth/AuthPasswordField"
|
||||
import { getPasswordValidationMessage } from "./auth/utils"
|
||||
|
||||
export interface UserProfile {
|
||||
id?: string;
|
||||
@@ -187,12 +188,23 @@ export default function Profile() {
|
||||
return
|
||||
}
|
||||
|
||||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||||
toast.error(t.login.passwordMismatch)
|
||||
return
|
||||
}
|
||||
|
||||
setIsSaving(true)
|
||||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||||
toast.error(t.login.passwordMismatch)
|
||||
return
|
||||
}
|
||||
|
||||
const passwordValidationMessage = getPasswordValidationMessage(passwordForm.newPassword, t.login)
|
||||
if (passwordValidationMessage) {
|
||||
toast.error(passwordValidationMessage)
|
||||
return
|
||||
}
|
||||
|
||||
if (passwordForm.currentPassword === passwordForm.newPassword) {
|
||||
toast.error(t.login.passwordReuse)
|
||||
return
|
||||
}
|
||||
|
||||
setIsSaving(true)
|
||||
try {
|
||||
await changePassword(
|
||||
passwordForm.currentPassword,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useAuthFlow } from "../../context/AuthFlowContext"
|
||||
import { useTranslation } from "../../hooks/useTranslation"
|
||||
import { AuthPanel } from "./AuthPanel"
|
||||
import { AuthPasswordField } from "./AuthPasswordField"
|
||||
import { getApiErrorMessage } from "./utils"
|
||||
import { getApiErrorMessage, getPasswordValidationMessage } from "./utils"
|
||||
|
||||
export function ForgotPasswordPasswordPage() {
|
||||
const navigate = useNavigate()
|
||||
@@ -40,6 +40,12 @@ export function ForgotPasswordPasswordPage() {
|
||||
return
|
||||
}
|
||||
|
||||
const passwordValidationMessage = getPasswordValidationMessage(password, t.login)
|
||||
if (passwordValidationMessage) {
|
||||
toast.error(passwordValidationMessage)
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
await resetPasswordWithOtp(state.forgotPassword.mobile, state.forgotPassword.code, password, confirmation)
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useAuthFlow } from "../../context/AuthFlowContext"
|
||||
import { useTranslation } from "../../hooks/useTranslation"
|
||||
import { AuthPanel } from "./AuthPanel"
|
||||
import { AuthPasswordField } from "./AuthPasswordField"
|
||||
import { completeAuthentication, getApiErrorMessage } from "./utils"
|
||||
import { completeAuthentication, getApiErrorMessage, getPasswordValidationMessage } from "./utils"
|
||||
|
||||
export function SignupPasswordPage() {
|
||||
const navigate = useNavigate()
|
||||
@@ -40,6 +40,12 @@ export function SignupPasswordPage() {
|
||||
return
|
||||
}
|
||||
|
||||
const passwordValidationMessage = getPasswordValidationMessage(password, t.login)
|
||||
if (passwordValidationMessage) {
|
||||
toast.error(passwordValidationMessage)
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
const data = await registerWithOtp(state.signup.mobile, state.signup.code, password, confirmation)
|
||||
|
||||
@@ -29,6 +29,24 @@ export const getApiErrorMessage = (error: unknown, fallbackMessage: string) => {
|
||||
return fallbackMessage
|
||||
}
|
||||
|
||||
export const getPasswordValidationMessage = (
|
||||
password: string,
|
||||
copy: {
|
||||
passwordRequirements: string
|
||||
},
|
||||
) => {
|
||||
const hasLowercase = /[a-z]/.test(password)
|
||||
const hasUppercase = /[A-Z]/.test(password)
|
||||
const hasDigit = /\d/.test(password)
|
||||
const hasSymbol = /[^A-Za-z0-9]/.test(password)
|
||||
|
||||
if (password.length < 8 || !hasLowercase || !hasUppercase || !hasDigit || !hasSymbol) {
|
||||
return copy.passwordRequirements
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const handleThrottleError = ({
|
||||
error,
|
||||
cooldownKey,
|
||||
|
||||
Reference in New Issue
Block a user