security hardening

This commit is contained in:
2025-12-28 20:04:29 -05:00
parent aefd467660
commit 1ba20339a8
22 changed files with 5177 additions and 116 deletions

View File

@@ -1,4 +1,5 @@
import { z } from "zod";
import { validatePassword } from "~/lib/validation";
/**
* User API Validation Schemas
@@ -7,6 +8,31 @@ import { z } from "zod";
* profile updates, and password management
*/
// ============================================================================
// Custom Password Validator
// ============================================================================
/**
* Secure password validation with strength requirements
* Minimum 12 characters, uppercase, lowercase, number, and special character
*/
const securePasswordSchema = z
.string()
.min(12, "Password must be at least 12 characters")
.refine(
(password) => {
const result = validatePassword(password);
return result.isValid;
},
(password) => {
const result = validatePassword(password);
return {
message:
result.errors.join(", ") || "Password does not meet requirements"
};
}
);
// ============================================================================
// Authentication Schemas
// ============================================================================
@@ -17,8 +43,8 @@ import { z } from "zod";
export const registerUserSchema = z
.object({
email: z.string().email(),
password: z.string().min(8, "Password must be at least 8 characters"),
passwordConfirmation: z.string().min(8)
password: securePasswordSchema,
passwordConfirmation: z.string().min(12)
})
.refine((data) => data.password === data.passwordConfirmation, {
message: "Passwords do not match",
@@ -73,10 +99,8 @@ export const updateProfileImageSchema = z.object({
export const changePasswordSchema = z
.object({
oldPassword: z.string().min(1, "Current password is required"),
newPassword: z
.string()
.min(8, "New password must be at least 8 characters"),
newPasswordConfirmation: z.string().min(8)
newPassword: securePasswordSchema,
newPasswordConfirmation: z.string().min(12)
})
.refine((data) => data.newPassword === data.newPasswordConfirmation, {
message: "Passwords do not match",
@@ -92,8 +116,8 @@ export const changePasswordSchema = z
*/
export const setPasswordSchema = z
.object({
newPassword: z.string().min(8, "Password must be at least 8 characters"),
newPasswordConfirmation: z.string().min(8)
newPassword: securePasswordSchema,
newPasswordConfirmation: z.string().min(12)
})
.refine((data) => data.newPassword === data.newPasswordConfirmation, {
message: "Passwords do not match",
@@ -113,8 +137,8 @@ export const requestPasswordResetSchema = z.object({
export const resetPasswordSchema = z
.object({
token: z.string().min(1),
newPassword: z.string().min(8, "Password must be at least 8 characters"),
newPasswordConfirmation: z.string().min(8)
newPassword: securePasswordSchema,
newPasswordConfirmation: z.string().min(12)
})
.refine((data) => data.newPassword === data.newPasswordConfirmation, {
message: "Passwords do not match",