| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- export const PASSWORD_POLICY = Object.freeze({
- minLength: 8,
- requireLetter: true,
- requireNumber: true,
- disallowSameAsCurrent: true,
- });
- export const PASSWORD_POLICY_REASON = Object.freeze({
- MIN_LENGTH: "MIN_LENGTH",
- MISSING_LETTER: "MISSING_LETTER",
- MISSING_NUMBER: "MISSING_NUMBER",
- SAME_AS_CURRENT: "SAME_AS_CURRENT",
- });
- function hasLetter(value) {
- return /[A-Za-z]/.test(String(value || ""));
- }
- function hasNumber(value) {
- return /\d/.test(String(value || ""));
- }
- /**
- * Validate a new password against the project's explicit policy.
- *
- * @param {{ newPassword?: unknown, currentPassword?: unknown }} input
- * @returns {{
- * ok: boolean,
- * reasons: string[],
- * policy: { minLength: number, requireLetter: boolean, requireNumber: boolean, disallowSameAsCurrent: boolean }
- * }}
- */
- export function validateNewPassword({ newPassword, currentPassword } = {}) {
- const pw = typeof newPassword === "string" ? newPassword : "";
- const current = typeof currentPassword === "string" ? currentPassword : null;
- const reasons = [];
- if (pw.length < PASSWORD_POLICY.minLength) {
- reasons.push(PASSWORD_POLICY_REASON.MIN_LENGTH);
- }
- if (PASSWORD_POLICY.requireLetter && !hasLetter(pw)) {
- reasons.push(PASSWORD_POLICY_REASON.MISSING_LETTER);
- }
- if (PASSWORD_POLICY.requireNumber && !hasNumber(pw)) {
- reasons.push(PASSWORD_POLICY_REASON.MISSING_NUMBER);
- }
- if (
- PASSWORD_POLICY.disallowSameAsCurrent &&
- current !== null &&
- pw === current
- ) {
- reasons.push(PASSWORD_POLICY_REASON.SAME_AS_CURRENT);
- }
- const uniqueReasons = Array.from(new Set(reasons));
- return {
- ok: uniqueReasons.length === 0,
- reasons: uniqueReasons,
- policy: { ...PASSWORD_POLICY },
- };
- }
|