feat(auth): harden authentication and add configurable two-factor support

This commit is contained in:
2026-07-31 03:12:46 +02:00
parent 235d5f646c
commit e47243f7dc
51 changed files with 2904 additions and 1359 deletions

View File

@@ -40,8 +40,8 @@ describe('registerSchema', () => {
first_name: 'Test',
last_name: 'User',
email: 'test@example.com',
password: 'password123',
password_confirmation: 'password123',
password: 'correct horse battery staple',
password_confirmation: 'correct horse battery staple',
}
it('accepts valid registration data', () => {
@@ -69,8 +69,8 @@ describe('registerSchema', () => {
expect(result.success).toBe(false)
})
it('rejects password shorter than 8 characters', () => {
const result = validate(registerSchema, { ...validData, password: 'short', password_confirmation: 'short' })
it('rejects password shorter than 15 characters', () => {
const result = validate(registerSchema, { ...validData, password: 'too-short', password_confirmation: 'too-short' })
expect(result.success).toBe(false)
})
@@ -110,8 +110,8 @@ describe('forgotPasswordSchema', () => {
describe('resetPasswordSchema', () => {
const validData = {
email: 'test@example.com',
password: 'newpassword123',
password_confirmation: 'newpassword123',
password: 'a secure new password',
password_confirmation: 'a secure new password',
}
it('accepts valid reset data', () => {
@@ -124,8 +124,8 @@ describe('resetPasswordSchema', () => {
expect(result.success).toBe(false)
})
it('rejects password shorter than 8 characters', () => {
const result = validate(resetPasswordSchema, { ...validData, password: 'short', password_confirmation: 'short' })
it('rejects password shorter than 15 characters', () => {
const result = validate(resetPasswordSchema, { ...validData, password: 'too-short', password_confirmation: 'too-short' })
expect(result.success).toBe(false)
})

View File

@@ -17,7 +17,7 @@ export const registerSchema = v.pipe(
first_name: v.pipe(v.string('First name is required'), v.nonEmpty('First name is required')),
last_name: v.pipe(v.string('Last name is required'), v.nonEmpty('Last name is required')),
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(8, 'Password must be at least 8 characters')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(15, 'Password must be at least 15 characters'), v.maxLength(128, 'Password must be 128 characters or fewer')),
password_confirmation: v.pipe(v.string('Please confirm your password'), v.nonEmpty('Please confirm your password')),
}),
v.forward(
@@ -37,7 +37,7 @@ export const forgotPasswordSchema = v.object({
export const resetPasswordSchema = v.pipe(
v.object({
email: v.pipe(v.string('Email is required'), v.nonEmpty('Email is required'), v.email('Please enter a valid email')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(8, 'Password must be at least 8 characters')),
password: v.pipe(v.string('Password is required'), v.nonEmpty('Password is required'), v.minLength(15, 'Password must be at least 15 characters'), v.maxLength(128, 'Password must be 128 characters or fewer')),
password_confirmation: v.pipe(v.string('Please confirm your password'), v.nonEmpty('Please confirm your password')),
}),
v.forward(