feat(auth): make password length configurable via env (min 5–30, max 30–128)
CI / check (push) Successful in 29s
CI / tests-imaging-browsers (push) Successful in 1m46s
CI / tests-integration (push) Successful in 1m47s
CI / tests-unit (push) Successful in 1m49s
CI / tests-quality (push) Successful in 44s
CI / tests-ui (push) Successful in 4m38s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 3m38s
CI / check (push) Successful in 29s
CI / tests-imaging-browsers (push) Successful in 1m46s
CI / tests-integration (push) Successful in 1m47s
CI / tests-unit (push) Successful in 1m49s
CI / tests-quality (push) Successful in 44s
CI / tests-ui (push) Successful in 4m38s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 3m38s
Add PASSWORD_MIN_LENGTH / PASSWORD_MAX_LENGTH (validated in src/env.ts) and a
shared passwordPolicy helper. The policy drives the zod schemas in register,
password reset and the admin create-user dialog, plus the live requirement
checks and strength meter in the register form.
Locale messages now interpolate the configured minimum via {min} instead of
hard-coding 12.
This commit is contained in:
1 parent
aef0939ee9
commit
9cf1eb615c
36 files changed
+227
-143
No files matched your search
@@ -30,19 +30,23 @@ interface RegisterFormProps {
|
||||
captcha: { provider: string; siteKey?: string };
|
||||
error?: string;
|
||||
nonce?: string;
|
||||
/** Effective policy from `PASSWORD_MIN_LENGTH` / `PASSWORD_MAX_LENGTH`. */
|
||||
passwordMinLength: number;
|
||||
passwordMaxLength: number;
|
||||
}
|
||||
|
||||
function passwordStrength(
|
||||
pw: string,
|
||||
t: (key: string) => string,
|
||||
minLength: number,
|
||||
): {
|
||||
score: number;
|
||||
label: string;
|
||||
color: string;
|
||||
} {
|
||||
let score = 0;
|
||||
if (pw.length >= 8) score += 1;
|
||||
if (pw.length >= 12) score += 1;
|
||||
if (pw.length >= minLength) score += 1;
|
||||
if (pw.length >= minLength + 4) score += 1;
|
||||
if (/[a-z]/.test(pw) && /[A-Z]/.test(pw)) score += 1;
|
||||
if (/\d/.test(pw)) score += 1;
|
||||
if (/[^a-zA-Z0-9]/.test(pw)) score += 1;
|
||||
@@ -103,6 +107,8 @@ export function RegisterForm({
|
||||
captcha,
|
||||
error,
|
||||
nonce,
|
||||
passwordMinLength,
|
||||
passwordMaxLength,
|
||||
}: RegisterFormProps) {
|
||||
const t = useTranslations("pages.register");
|
||||
const showCaptcha = captcha.provider !== "none" && !!captcha.siteKey;
|
||||
@@ -138,7 +144,7 @@ export function RegisterForm({
|
||||
);
|
||||
const autoLoginStartedRef = useRef(false);
|
||||
const [referralCode, setReferralCode] = useState("");
|
||||
const strength = passwordStrength(password, t);
|
||||
const strength = passwordStrength(password, t, passwordMinLength);
|
||||
|
||||
/**
|
||||
* The password policy the server enforces, evaluated live so the visitor can
|
||||
@@ -146,7 +152,10 @@ export function RegisterForm({
|
||||
* single error back.
|
||||
*/
|
||||
const requirements = [
|
||||
{ label: t("passwordMinLength"), met: password.length >= 12 },
|
||||
{
|
||||
label: t("passwordMinLength", { min: passwordMinLength }),
|
||||
met: password.length >= passwordMinLength,
|
||||
},
|
||||
{ label: t("passwordUpper"), met: /[A-Z]/.test(password) },
|
||||
{ label: t("passwordLower"), met: /[a-z]/.test(password) },
|
||||
{ label: t("passwordDigit"), met: /\d/.test(password) },
|
||||
@@ -295,9 +304,13 @@ export function RegisterForm({
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={password}
|
||||
onChange={setPassword}
|
||||
placeholder={t("passwordPlaceholder")}
|
||||
placeholder={t("passwordPlaceholder", {
|
||||
min: passwordMinLength,
|
||||
})}
|
||||
autoComplete="new-password"
|
||||
required
|
||||
minLength={passwordMinLength}
|
||||
maxLength={passwordMaxLength}
|
||||
trailing={
|
||||
<PasswordToggle
|
||||
show={showPassword}
|
||||
|
||||
Reference in new issue
Block a user