auth0-password-policies 2.2.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -68,9 +68,9 @@ jobs:
68
68
  fi
69
69
 
70
70
  - name: Setup Node
71
- uses: actions/setup-node@5e2628c959b9ade56971c0afcebbe5332d44b398 # Aug 2025
71
+ uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
72
72
  with:
73
- node-version: 20
73
+ node-version: 24
74
74
  cache: 'yarn'
75
75
  registry-url: 'https://registry.npmjs.org'
76
76
 
@@ -95,6 +95,4 @@ jobs:
95
95
  shell: bash
96
96
  run: |
97
97
  echo "About to run: npm publish --provenance --tag ${{ steps.npm-tag.outputs.tag }}"
98
- npm publish --provenance --tag ${{ steps.npm-tag.outputs.tag }}
99
- env:
100
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
98
+ NODE_AUTH_TOKEN="" npm publish --provenance --tag ${{ steps.npm-tag.outputs.tag }}
package/index.js CHANGED
@@ -43,64 +43,56 @@ const CHARACTER_TYPES = {
43
43
  };
44
44
 
45
45
  /**
46
- * @typedef {Object} PasswordOptions
47
- * @property {number} [min_length=15] - Minimum password length (1-72)
48
- * @property {Array<'uppercase'|'lowercase'|'number'|'special'>} [character_types=[]] - Required character types
49
- * @property {'all'|'three_of_four'} [character_type_rule='all'] - How many character types are required
50
- * @property {'allow'|'block'} [identical_characters='allow'] - Whether to allow >2 identical consecutive characters
51
- * @property {'allow'|'block'} [sequential_characters='allow'] - Whether to allow sequential_characters (increasing or decreasing) alphanumeric characters.
52
- * @property {'error'|'truncate'} [max_length_exceeded='error'] - Behavior when password exceeds max length of 72 bytes
46
+ * @typedef {Object} PasswordComplexityOptions
47
+ * @property {number} min_length - Minimum password length (1-72)
48
+ * @property {Array<'uppercase'|'lowercase'|'number'|'special'>} character_types - Required character types
49
+ * @property {'all'|'three_of_four'} character_type_rule - How many character types are required
50
+ * @property {'allow'|'block'} identical_characters - Whether to allow >2 identical consecutive characters
51
+ * @property {'allow'|'block'} sequential_characters - Whether to allow sequential alphanumeric characters
52
+ * @property {'error'|'truncate'} max_length_exceeded - Behavior when password exceeds max length of 72 bytes
53
53
  */
54
54
 
55
- /**
56
- * Default values for password options
57
- * @constant
58
- * @type {PasswordOptions}
59
- */
60
- const DEFAULT_PASSWORD_OPTIONS = {
61
- min_length: 15,
62
- character_types: [],
63
- character_type_rule: "all",
64
- identical_characters: "allow",
65
- sequential_characters: "allow",
66
- max_length_exceeded: "error"
67
- };
68
-
69
55
  /**
70
56
  * Creates a PasswordPolicy rules configuration from an Auth0
71
57
  * `connection.options.password_options.complexity` object.
72
58
  *
73
- * @param {PasswordOptions} options - Auth0 password_options.complexity object
59
+ * @param {PasswordComplexityOptions} options - Auth0 password complexity configuration
74
60
  * @returns {Object} password-sheriff rules configuration object that can be passed to PasswordPolicy constructor
75
61
  */
76
- function createRulesFromOptions(options = {}) {
62
+ function createRulesFromOptions(options) {
63
+ if (!options || typeof options !== 'object') {
64
+ throw new Error("options must be a PasswordComplexity object");
65
+ }
66
+
77
67
  const rules = {};
78
68
 
79
- // Apply defaults
80
69
  const {
81
70
  min_length: minLength,
82
71
  character_types: requiredTypes,
83
72
  character_type_rule: characterTypeRule,
84
73
  identical_characters: identicalChars,
85
74
  sequential_characters: sequentialChars,
86
- max_length_exceeded: maxLength
87
- } = { ...DEFAULT_PASSWORD_OPTIONS, ...options };
75
+ max_length_exceeded: maxLengthExceeded
76
+ } = options;
88
77
 
89
- // Validate min_length is within acceptable range
90
- if (minLength < 1 || minLength > 72) {
91
- throw new Error("min_length must be between 1 and 72");
92
- }
78
+ const require3of4 = characterTypeRule === "three_of_four";
79
+
80
+ if (minLength !== undefined && minLength !== null) {
81
+ // Validate min_length is within acceptable range (if provided)
82
+ if (typeof minLength !== 'number' || minLength < 1 || minLength > 72) {
83
+ throw new Error("min_length must be between 1 and 72");
84
+ }
93
85
 
94
- // Handle min_length
95
- rules.length = { minLength: minLength };
86
+ // Handle min_length (if provided)
87
+ rules.length = { minLength: minLength };
88
+ }
96
89
 
97
90
  // Validate '3 of 4' prerequisite
98
- const require3of4 = characterTypeRule === "three_of_four";
99
91
  if (require3of4) {
100
92
  const hasAllFourTypes = Object.values(CHARACTER_TYPES).every(function (
101
93
  type
102
94
  ) {
103
- return requiredTypes.includes(type);
95
+ return requiredTypes?.includes(type);
104
96
  });
105
97
 
106
98
  if (!hasAllFourTypes) {
@@ -112,7 +104,7 @@ function createRulesFromOptions(options = {}) {
112
104
  }
113
105
  }
114
106
 
115
- if (requiredTypes.length > 0 || require3of4) {
107
+ if (requiredTypes?.length > 0 || require3of4) {
116
108
  const expressions = [];
117
109
 
118
110
  if (require3of4) {
@@ -123,16 +115,16 @@ function createRulesFromOptions(options = {}) {
123
115
  };
124
116
  } else {
125
117
  // Map character types to expressions
126
- if (requiredTypes.includes(CHARACTER_TYPES.LOWERCASE)) {
118
+ if (requiredTypes?.includes(CHARACTER_TYPES.LOWERCASE)) {
127
119
  expressions.push(lowerCase);
128
120
  }
129
- if (requiredTypes.includes(CHARACTER_TYPES.UPPERCASE)) {
121
+ if (requiredTypes?.includes(CHARACTER_TYPES.UPPERCASE)) {
130
122
  expressions.push(upperCase);
131
123
  }
132
- if (requiredTypes.includes(CHARACTER_TYPES.NUMBER)) {
124
+ if (requiredTypes?.includes(CHARACTER_TYPES.NUMBER)) {
133
125
  expressions.push(numbers);
134
126
  }
135
- if (requiredTypes.includes(CHARACTER_TYPES.SPECIAL)) {
127
+ if (requiredTypes?.includes(CHARACTER_TYPES.SPECIAL)) {
136
128
  expressions.push(specialCharacters);
137
129
  }
138
130
 
@@ -150,7 +142,7 @@ function createRulesFromOptions(options = {}) {
150
142
  rules.sequentialChars = { max: 2 }
151
143
  }
152
144
 
153
- if (maxLength === "error") {
145
+ if (maxLengthExceeded === "error") {
154
146
  rules.maxLength = { maxBytes: 72 };
155
147
  }
156
148
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auth0-password-policies",
3
- "version": "2.2.0",
3
+ "version": "3.1.0",
4
4
  "main": "index.js",
5
5
  "repository": "git@github.com:auth0/auth0-password-policies.git",
6
6
  "author": "Shaun Starsprung <shaun.starsprung@auth0.com>",
package/test/test.js CHANGED
@@ -23,7 +23,9 @@ describe("password policies", function () {
23
23
  it("should test min_length from 1 to 72", function () {
24
24
  const auth0Config1 = {
25
25
  min_length: 1,
26
+ character_types: [],
26
27
  identical_characters: "allow",
28
+ sequential_characters: "allow",
27
29
  max_length_exceeded: "truncate",
28
30
  };
29
31
  const rules = createRulesFromOptions(auth0Config1);
@@ -35,7 +37,9 @@ describe("password policies", function () {
35
37
 
36
38
  const auth0Config72 = {
37
39
  min_length: 72,
40
+ character_types: [],
38
41
  identical_characters: "allow",
42
+ sequential_characters: "allow",
39
43
  max_length_exceeded: "truncate",
40
44
  };
41
45
  const rules72 = createRulesFromOptions(auth0Config72);
@@ -54,14 +58,38 @@ describe("password policies", function () {
54
58
  createRulesFromOptions(auth0Config);
55
59
  }).toThrow("min_length must be between 1 and 72");
56
60
  });
61
+
62
+ it("should not set length rule when min_length is undefined", function () {
63
+ const auth0Config = {
64
+ character_types: [],
65
+ identical_characters: "allow",
66
+ sequential_characters: "allow",
67
+ max_length_exceeded: "truncate",
68
+ };
69
+ const rules = createRulesFromOptions(auth0Config);
70
+ expect(rules.length).toBeUndefined();
71
+ });
72
+
73
+ it("should not set length rule when min_length is null", function () {
74
+ const auth0Config = {
75
+ min_length: null,
76
+ character_types: [],
77
+ identical_characters: "allow",
78
+ sequential_characters: "allow",
79
+ max_length_exceeded: "truncate",
80
+ };
81
+ const rules = createRulesFromOptions(auth0Config);
82
+ expect(rules.length).toBeUndefined();
83
+ });
57
84
  });
58
85
 
59
86
  describe("character_types", function () {
60
87
  it("should enforce required character types", function () {
61
88
  const auth0Config = {
89
+ min_length: 4,
62
90
  character_types: ["lowercase", "uppercase", "number", "special"],
63
91
  identical_characters: "allow",
64
- min_length: 4,
92
+ sequential_characters: "allow",
65
93
  max_length_exceeded: "truncate",
66
94
  };
67
95
  const rules = createRulesFromOptions(auth0Config);
@@ -78,15 +106,33 @@ describe("password policies", function () {
78
106
  expect(typeof expr.explain).toBe("function");
79
107
  });
80
108
  });
109
+
110
+ it("should handle undefined character_types", function () {
111
+ const auth0Config = {
112
+ min_length: 8,
113
+ identical_characters: "allow",
114
+ sequential_characters: "allow",
115
+ max_length_exceeded: "truncate",
116
+ };
117
+ const rules = createRulesFromOptions(auth0Config);
118
+ expect(rules).toEqual({
119
+ length: {
120
+ minLength: 8,
121
+ },
122
+ });
123
+ expect(rules).not.toHaveProperty("contains");
124
+ expect(rules).not.toHaveProperty("containsAtLeast");
125
+ });
81
126
  });
82
127
 
83
128
  describe("character_type_rule", function () {
84
129
  it("when set to 'three_of_four', should enforce 3 out of 4 character types when all 4 types are specified", function () {
85
130
  const auth0Config = {
131
+ min_length: 3,
86
132
  character_types: ["lowercase", "uppercase", "number", "special"],
87
133
  character_type_rule: "three_of_four",
88
134
  identical_characters: "allow",
89
- min_length: 3,
135
+ sequential_characters: "allow",
90
136
  max_length_exceeded: "truncate",
91
137
  };
92
138
  const rules = createRulesFromOptions(auth0Config);
@@ -108,8 +154,11 @@ describe("password policies", function () {
108
154
  it("when set to 'three_of_four', should throw an error when all 4 character types are NOT specified", function () {
109
155
  expect(function () {
110
156
  const auth0Config = {
157
+ min_length: 8,
111
158
  character_types: ["lowercase", "uppercase"],
112
159
  character_type_rule: "three_of_four",
160
+ identical_characters: "allow",
161
+ sequential_characters: "allow",
113
162
  max_length_exceeded: "error",
114
163
  };
115
164
  createRulesFromOptions(auth0Config);
@@ -122,13 +171,16 @@ describe("password policies", function () {
122
171
  describe("identical_characters", function () {
123
172
  it("should disallow more than 2 identical characters when specified", function () {
124
173
  const auth0Config = {
174
+ min_length: 8,
175
+ character_types: [],
125
176
  identical_characters: "block",
177
+ sequential_characters: "allow",
126
178
  max_length_exceeded: "error",
127
179
  };
128
180
  const rules = createRulesFromOptions(auth0Config);
129
181
  expect(rules).toEqual({
130
182
  length: {
131
- minLength: 15,
183
+ minLength: 8,
132
184
  },
133
185
  identicalChars: {
134
186
  max: 2,
@@ -141,13 +193,16 @@ describe("password policies", function () {
141
193
 
142
194
  it("should allow more than 2 identical characters when specified", function () {
143
195
  const auth0Config = {
196
+ min_length: 8,
197
+ character_types: [],
144
198
  identical_characters: "allow",
199
+ sequential_characters: "allow",
145
200
  max_length_exceeded: "error",
146
201
  };
147
202
  const rules = createRulesFromOptions(auth0Config);
148
203
  expect(rules).toEqual({
149
204
  length: {
150
- minLength: 15,
205
+ minLength: 8,
151
206
  },
152
207
  maxLength: {
153
208
  maxBytes: 72,
@@ -159,12 +214,16 @@ describe("password policies", function () {
159
214
  describe("sequential_characters", function () {
160
215
  it("should disallow more than 2 sequential characters when specified (set to block)", function () {
161
216
  const auth0Config = {
217
+ min_length: 8,
218
+ character_types: [],
219
+ identical_characters: "allow",
162
220
  sequential_characters: "block",
221
+ max_length_exceeded: "error",
163
222
  };
164
223
  const rules = createRulesFromOptions(auth0Config);
165
224
  expect(rules).toEqual({
166
225
  length: {
167
- minLength: 15,
226
+ minLength: 8,
168
227
  },
169
228
  sequentialChars: {
170
229
  max: 2,
@@ -177,12 +236,16 @@ describe("password policies", function () {
177
236
 
178
237
  it("should allow more than 2 sequential characters when specified (set to allow)", function () {
179
238
  const auth0Config = {
239
+ min_length: 8,
240
+ character_types: [],
241
+ identical_characters: "allow",
180
242
  sequential_characters: "allow",
243
+ max_length_exceeded: "error",
181
244
  };
182
245
  const rules = createRulesFromOptions(auth0Config);
183
246
  expect(rules).toEqual({
184
247
  length: {
185
- minLength: 15,
248
+ minLength: 8,
186
249
  },
187
250
  maxLength: {
188
251
  maxBytes: 72,
@@ -193,7 +256,10 @@ describe("password policies", function () {
193
256
  it("should correctly validate a password when sequential_characters is set to allow", function () {
194
257
  const auth0Config = {
195
258
  min_length: 2,
259
+ character_types: [],
260
+ identical_characters: "allow",
196
261
  sequential_characters: "allow",
262
+ max_length_exceeded: "truncate",
197
263
  };
198
264
  const rules = createRulesFromOptions(auth0Config);
199
265
  const policy = new PasswordPolicy(rules);
@@ -204,7 +270,10 @@ describe("password policies", function () {
204
270
  it("should correctly validate a password when sequential_characters is set to block", function () {
205
271
  const auth0Config = {
206
272
  min_length: 2,
273
+ character_types: [],
274
+ identical_characters: "allow",
207
275
  sequential_characters: "block",
276
+ max_length_exceeded: "truncate",
208
277
  };
209
278
  const rules = createRulesFromOptions(auth0Config);
210
279
  const policy = new PasswordPolicy(rules);
@@ -217,12 +286,16 @@ describe("password policies", function () {
217
286
  describe("max_length_exceeded", function () {
218
287
  it("should disallow more than 72 bytes when creating password if max_length_exceeded is set to error", function () {
219
288
  const auth0Config = {
289
+ min_length: 8,
290
+ character_types: [],
291
+ identical_characters: "allow",
292
+ sequential_characters: "allow",
220
293
  max_length_exceeded: "error",
221
294
  };
222
295
  const rules = createRulesFromOptions(auth0Config);
223
296
  expect(rules).toEqual({
224
297
  length: {
225
- minLength: 15,
298
+ minLength: 8,
226
299
  },
227
300
  maxLength: {
228
301
  maxBytes: 72,
@@ -232,12 +305,16 @@ describe("password policies", function () {
232
305
 
233
306
  it("should not set a maxLength on rules when max_length_exceeded is set to truncate", function () {
234
307
  const auth0Config = {
308
+ min_length: 8,
309
+ character_types: [],
310
+ identical_characters: "allow",
311
+ sequential_characters: "allow",
235
312
  max_length_exceeded: "truncate",
236
313
  };
237
314
  const rules = createRulesFromOptions(auth0Config);
238
315
  expect(rules).toEqual({
239
316
  length: {
240
- minLength: 15,
317
+ minLength: 8,
241
318
  },
242
319
  });
243
320
  });
@@ -245,6 +322,9 @@ describe("password policies", function () {
245
322
  it("should correctly validate a password when max_length_exceeded is set to error", function () {
246
323
  const auth0Config = {
247
324
  min_length: 2,
325
+ character_types: [],
326
+ identical_characters: "allow",
327
+ sequential_characters: "allow",
248
328
  max_length_exceeded: "error",
249
329
  };
250
330
  const rules = createRulesFromOptions(auth0Config);
@@ -257,6 +337,9 @@ describe("password policies", function () {
257
337
  it("should correctly validate a password when max_length_exceeded is set to truncate", function () {
258
338
  const auth0Config = {
259
339
  min_length: 2,
340
+ character_types: [],
341
+ identical_characters: "allow",
342
+ sequential_characters: "allow",
260
343
  max_length_exceeded: "truncate",
261
344
  };
262
345
  const rules = createRulesFromOptions(auth0Config);
@@ -267,23 +350,24 @@ describe("password policies", function () {
267
350
  });
268
351
  });
269
352
 
270
- describe("default values", function () {
271
- it("should apply default values when not specified", function () {
353
+ describe("validation", function () {
354
+ it("should throw an error when options parameter is undefined", function () {
355
+ expect(function () {
356
+ const auth0Config = undefined;
357
+ createRulesFromOptions(auth0Config);
358
+ }).toThrow();
359
+ });
360
+
361
+ it("should accept valid configuration with empty options", function () {
272
362
  const auth0Config = {};
273
363
  const rules = createRulesFromOptions(auth0Config);
274
- expect(rules).toEqual({
275
- length: {
276
- minLength: 15,
277
- },
278
- maxLength: {
279
- maxBytes: 72,
280
- },
281
- });
364
+ expect(rules).toEqual({});
282
365
  });
283
366
 
284
- it("should allow overriding default values", function () {
367
+ it("should accept valid configuration with all properties", function () {
285
368
  const auth0Config = {
286
369
  min_length: 5,
370
+ character_types: [],
287
371
  identical_characters: "allow",
288
372
  sequential_characters: "allow",
289
373
  max_length_exceeded: "truncate",