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.
- package/.github/workflows/publish.yml +3 -5
- package/index.js +32 -40
- package/package.json +1 -1
- package/test/test.js +103 -19
|
@@ -68,9 +68,9 @@ jobs:
|
|
|
68
68
|
fi
|
|
69
69
|
|
|
70
70
|
- name: Setup Node
|
|
71
|
-
uses: actions/setup-node@
|
|
71
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
72
72
|
with:
|
|
73
|
-
node-version:
|
|
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}
|
|
47
|
-
* @property {number}
|
|
48
|
-
* @property {Array<'uppercase'|'lowercase'|'number'|'special'>}
|
|
49
|
-
* @property {'all'|'three_of_four'}
|
|
50
|
-
* @property {'allow'|'block'}
|
|
51
|
-
* @property {'allow'|'block'}
|
|
52
|
-
* @property {'error'|'truncate'}
|
|
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 {
|
|
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:
|
|
87
|
-
} =
|
|
75
|
+
max_length_exceeded: maxLengthExceeded
|
|
76
|
+
} = options;
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
|
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
|
|
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
|
|
118
|
+
if (requiredTypes?.includes(CHARACTER_TYPES.LOWERCASE)) {
|
|
127
119
|
expressions.push(lowerCase);
|
|
128
120
|
}
|
|
129
|
-
if (requiredTypes
|
|
121
|
+
if (requiredTypes?.includes(CHARACTER_TYPES.UPPERCASE)) {
|
|
130
122
|
expressions.push(upperCase);
|
|
131
123
|
}
|
|
132
|
-
if (requiredTypes
|
|
124
|
+
if (requiredTypes?.includes(CHARACTER_TYPES.NUMBER)) {
|
|
133
125
|
expressions.push(numbers);
|
|
134
126
|
}
|
|
135
|
-
if (requiredTypes
|
|
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 (
|
|
145
|
+
if (maxLengthExceeded === "error") {
|
|
154
146
|
rules.maxLength = { maxBytes: 72 };
|
|
155
147
|
}
|
|
156
148
|
|
package/package.json
CHANGED
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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("
|
|
271
|
-
it("should
|
|
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
|
|
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",
|