passwordPolicy.js 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. export const PASSWORD_POLICY = Object.freeze({
  2. minLength: 8,
  3. requireLetter: true,
  4. requireNumber: true,
  5. disallowSameAsCurrent: true,
  6. });
  7. export const PASSWORD_POLICY_REASON = Object.freeze({
  8. MIN_LENGTH: "MIN_LENGTH",
  9. MISSING_LETTER: "MISSING_LETTER",
  10. MISSING_NUMBER: "MISSING_NUMBER",
  11. SAME_AS_CURRENT: "SAME_AS_CURRENT",
  12. });
  13. function hasLetter(value) {
  14. return /[A-Za-z]/.test(String(value || ""));
  15. }
  16. function hasNumber(value) {
  17. return /\d/.test(String(value || ""));
  18. }
  19. /**
  20. * Validate a new password against the project's explicit policy.
  21. *
  22. * @param {{ newPassword?: unknown, currentPassword?: unknown }} input
  23. * @returns {{
  24. * ok: boolean,
  25. * reasons: string[],
  26. * policy: { minLength: number, requireLetter: boolean, requireNumber: boolean, disallowSameAsCurrent: boolean }
  27. * }}
  28. */
  29. export function validateNewPassword({ newPassword, currentPassword } = {}) {
  30. const pw = typeof newPassword === "string" ? newPassword : "";
  31. const current = typeof currentPassword === "string" ? currentPassword : null;
  32. const reasons = [];
  33. if (pw.length < PASSWORD_POLICY.minLength) {
  34. reasons.push(PASSWORD_POLICY_REASON.MIN_LENGTH);
  35. }
  36. if (PASSWORD_POLICY.requireLetter && !hasLetter(pw)) {
  37. reasons.push(PASSWORD_POLICY_REASON.MISSING_LETTER);
  38. }
  39. if (PASSWORD_POLICY.requireNumber && !hasNumber(pw)) {
  40. reasons.push(PASSWORD_POLICY_REASON.MISSING_NUMBER);
  41. }
  42. if (
  43. PASSWORD_POLICY.disallowSameAsCurrent &&
  44. current !== null &&
  45. pw === current
  46. ) {
  47. reasons.push(PASSWORD_POLICY_REASON.SAME_AS_CURRENT);
  48. }
  49. const uniqueReasons = Array.from(new Set(reasons));
  50. return {
  51. ok: uniqueReasons.length === 0,
  52. reasons: uniqueReasons,
  53. policy: { ...PASSWORD_POLICY },
  54. };
  55. }