Closes the two gaps 010-identity-auth explicitly deferred (password reset, login rate-limiting), plus a shared password-strength validator both the reset-consume endpoint and admin account creation now depend on. - Password reset: single-use, paired-Redis-key tokens (never in Postgres), identical response regardless of account existence, stubbed delivery via a structured log line (no email infrastructure exists yet). - Password strength: one validatePasswordStrength() call site, wired into both POST /admin/users and the reset-consume flow. - Login rate-limiting: checkRateLimit keyed by submitted email, checked before any credential verification. Also fixes tests/helpers/auth.ts's shared loginAs() helper, which reused two fixed accounts across the whole integration suite via upsert — now rate-limited per email, that collided across ~30 files sharing one budget. Each call now gets a unique email; no call sites needed to change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
29 lines
623 B
TypeScript
29 lines
623 B
TypeScript
import { z } from 'zod';
|
|
|
|
export const loginSchema = z
|
|
.object({
|
|
email: z.string().email(),
|
|
password: z.string().min(1),
|
|
})
|
|
.strict();
|
|
|
|
export type LoginBody = z.infer<typeof loginSchema>;
|
|
|
|
/** 013-auth-hardening */
|
|
export const requestPasswordResetSchema = z
|
|
.object({
|
|
email: z.string().email(),
|
|
})
|
|
.strict();
|
|
|
|
export type RequestPasswordResetBody = z.infer<typeof requestPasswordResetSchema>;
|
|
|
|
export const resetPasswordSchema = z
|
|
.object({
|
|
token: z.string().min(1),
|
|
newPassword: z.string().min(1),
|
|
})
|
|
.strict();
|
|
|
|
export type ResetPasswordBody = z.infer<typeof resetPasswordSchema>;
|