auth0-password-policies 1.0.2 → 1.1.1
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 +100 -0
- package/CODEOWNERS +2 -0
- package/CONTRIBUTING.md +39 -0
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/index.js +124 -15
- package/jest.config.json +5 -0
- package/opslevel.yml +6 -0
- package/package.json +8 -2
- package/test/test.js +173 -0
- package/yarn.lock +0 -7
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: Test and Publish
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [ master ]
|
|
7
|
+
tags: [ 'v*' ]
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: [ master ]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
node-version: [16.x, 18.x, 20.x]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout code
|
|
21
|
+
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # Aug 2025
|
|
22
|
+
|
|
23
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
24
|
+
uses: actions/setup-node@5e2628c959b9ade56971c0afcebbe5332d44b398 # Aug 2025
|
|
25
|
+
with:
|
|
26
|
+
node-version: ${{ matrix.node-version }}
|
|
27
|
+
cache: 'yarn'
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: yarn install
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: yarn test
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
needs: test
|
|
38
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
|
39
|
+
permissions:
|
|
40
|
+
contents: read
|
|
41
|
+
id-token: write
|
|
42
|
+
steps:
|
|
43
|
+
- name: Checkout code
|
|
44
|
+
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # Aug 2025
|
|
45
|
+
|
|
46
|
+
- name: Verify tag is on master branch
|
|
47
|
+
shell: bash
|
|
48
|
+
run: |
|
|
49
|
+
|
|
50
|
+
git fetch origin master
|
|
51
|
+
|
|
52
|
+
# Check if the current commit (tag) is reachable from master
|
|
53
|
+
if ! git merge-base --is-ancestor HEAD origin/master; then
|
|
54
|
+
echo "Error: Tag ${{ github.ref_name }} is not on the master branch"
|
|
55
|
+
echo "Tags can only be published from commits that exist on master"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
echo "✅ Tag ${{ github.ref_name }} is on master branch"
|
|
59
|
+
|
|
60
|
+
- name: Validate tag format
|
|
61
|
+
shell: bash
|
|
62
|
+
run: |
|
|
63
|
+
TAG=${GITHUB_REF#refs/tags/}
|
|
64
|
+
echo "Publishing tag: $TAG"
|
|
65
|
+
if [[ ! $TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-beta(\.[0-9]+)?)?$ ]]; then
|
|
66
|
+
echo "Invalid tag format. Expected: v1.2.3 or v1.2.3-beta or v1.2.3-beta.1"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
- name: Setup Node
|
|
71
|
+
uses: actions/setup-node@5e2628c959b9ade56971c0afcebbe5332d44b398 # Aug 2025
|
|
72
|
+
with:
|
|
73
|
+
node-version: 20
|
|
74
|
+
cache: 'yarn'
|
|
75
|
+
registry-url: 'https://registry.npmjs.org'
|
|
76
|
+
|
|
77
|
+
- name: Install dependencies
|
|
78
|
+
shell: bash
|
|
79
|
+
run: yarn install --frozen-lockfile
|
|
80
|
+
|
|
81
|
+
- name: Determine npm tag
|
|
82
|
+
id: npm-tag
|
|
83
|
+
shell: bash
|
|
84
|
+
run: |
|
|
85
|
+
TAG="${GITHUB_REF#refs/tags/}"
|
|
86
|
+
if [[ "$TAG" == *"-beta"* ]]; then
|
|
87
|
+
echo "tag=beta" >> $GITHUB_OUTPUT
|
|
88
|
+
echo "Publishing as beta release"
|
|
89
|
+
else
|
|
90
|
+
echo "tag=latest" >> $GITHUB_OUTPUT
|
|
91
|
+
echo "Publishing as latest release"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
- name: Publish release to NPM
|
|
95
|
+
shell: bash
|
|
96
|
+
run: |
|
|
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 }}
|
package/CODEOWNERS
ADDED
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
# Contributing to Auth0 projects
|
|
3
|
+
|
|
4
|
+
A big welcome and thank you for considering contributing to the Auth0 projects. It’s people like you that make it a reality for users in our community.
|
|
5
|
+
|
|
6
|
+
Reading and following these guidelines will help us make the contribution process easy and effective for everyone involved. It also communicates that you agree to respect the time of the developers managing and developing these open source projects. In return, we will reciprocate that respect by addressing your issue.
|
|
7
|
+
|
|
8
|
+
### Quicklinks
|
|
9
|
+
|
|
10
|
+
* [Code of Conduct](#code-of-conduct)
|
|
11
|
+
* [Getting Started](#getting-started)
|
|
12
|
+
* [Making Changes](#making-changes)
|
|
13
|
+
* [Getting in Touch](#getting-in-touch)
|
|
14
|
+
* [Got a question or problem?](#got-a-question-or-problem?)
|
|
15
|
+
* [Vulnerability Reporting](#vulnerability-reporting)
|
|
16
|
+
|
|
17
|
+
## Code of Conduct
|
|
18
|
+
|
|
19
|
+
By participating and contributing to this project, you are expected to uphold our [Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md).
|
|
20
|
+
|
|
21
|
+
## Getting Started
|
|
22
|
+
|
|
23
|
+
### Making Changes
|
|
24
|
+
|
|
25
|
+
To contribute to a repository, the first step is to open a support ticket
|
|
26
|
+
|
|
27
|
+
[Opening a ticket] (https://auth0.com/docs/troubleshoot/customer-support/open-and-manage-support-tickets).
|
|
28
|
+
|
|
29
|
+
## Getting in touch
|
|
30
|
+
|
|
31
|
+
### Have a question or problem?
|
|
32
|
+
|
|
33
|
+
Please do not open issues for general support or usage questions. Instead, join us over in the Auth0 community at [community.auth0.com](https://community.auth0.com) and post your question there in the correct category with a descriptive tag.
|
|
34
|
+
|
|
35
|
+
### Vulnerability Reporting
|
|
36
|
+
|
|
37
|
+
Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues.
|
|
38
|
+
|
|
39
|
+
# More Information about this service is located in README.md at the root of this repository
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Auth0, Inc. <support@auth0.com> (http://auth0.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -23,3 +23,65 @@ Password policies presets used by Auth0. Extracted from [password-sheriff](https
|
|
|
23
23
|
* minimum characters: 10
|
|
24
24
|
* contains at least one character in three different groups out of: lowerCase, upperCase, numbers, specialCharacters
|
|
25
25
|
* may not contain any character repeated more than twice
|
|
26
|
+
|
|
27
|
+
## Helpers
|
|
28
|
+
|
|
29
|
+
### createRulesFromOptions
|
|
30
|
+
Converts an Auth0 `connection.options.password_options.complexity` object into a `password-sheriff` compatible rules object, and applies default values.
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
```js
|
|
34
|
+
const { PasswordPolicy } = require('password-sheriff');
|
|
35
|
+
const { createRulesFromOptions } = require('auth0-password-policies');
|
|
36
|
+
|
|
37
|
+
const passwordOptions = {
|
|
38
|
+
character_types: ["uppercase","lowercase","number","special"],
|
|
39
|
+
"require_3of4_character_types": true,
|
|
40
|
+
identical_characters: "disallow"
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const rules = createRulesFromOptions(passwordOptions);
|
|
44
|
+
const customPolicy = new PasswordPolicy(rules);
|
|
45
|
+
console.log(customPolicy.toString());
|
|
46
|
+
/**
|
|
47
|
+
* Output is:
|
|
48
|
+
* * At least 15 characters in length
|
|
49
|
+
* * At least 3 of the following 4 types of characters:
|
|
50
|
+
* * lower case letters (a-z)
|
|
51
|
+
* * upper case letters (A-Z)
|
|
52
|
+
* * numbers (i.e. 0-9)
|
|
53
|
+
* * special characters (e.g. !@#$%^&*)
|
|
54
|
+
* * No more than 2 identical characters in a row (e.g., "aaa" not allowed)
|
|
55
|
+
*/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Publishing
|
|
59
|
+
|
|
60
|
+
This package is automatically published to npm when:
|
|
61
|
+
|
|
62
|
+
1. **Tests pass**: All tests must pass across Node.js versions 16, 18, and 20
|
|
63
|
+
2. **Version tag is pushed**: Create and push a git tag following semantic versioning
|
|
64
|
+
|
|
65
|
+
### Publishing Steps
|
|
66
|
+
|
|
67
|
+
1. Update the version in `package.json`:
|
|
68
|
+
```bash
|
|
69
|
+
npm version patch # For bug fixes (1.0.0 -> 1.0.1)
|
|
70
|
+
npm version minor # For new features (1.0.0 -> 1.1.0)
|
|
71
|
+
npm version major # For breaking changes (1.0.0 -> 2.0.0)
|
|
72
|
+
npm version prerelease --preid=beta # For beta releases (1.0.0 -> 1.0.1-beta.0)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
2. Push the tag to trigger publishing:
|
|
76
|
+
```bash
|
|
77
|
+
git push origin master --tags
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The workflow will:
|
|
81
|
+
- Run tests across multiple Node.js versions
|
|
82
|
+
- Only publish if all tests pass (using `needs: test`)
|
|
83
|
+
- Publish with the appropriate npm tag (`latest` for stable, `beta` for pre-releases)
|
|
84
|
+
- Use provenance for enhanced security
|
|
85
|
+
|
|
86
|
+
### Tag Format
|
|
87
|
+
Tags must follow the format: `v1.2.3` or `v1.2.3-beta` or `v1.2.3-beta.1`
|
package/index.js
CHANGED
|
@@ -1,38 +1,147 @@
|
|
|
1
|
-
|
|
1
|
+
const charsets = require("password-sheriff").charsets;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const upperCase = charsets.upperCase;
|
|
4
|
+
const lowerCase = charsets.lowerCase;
|
|
5
|
+
const numbers = charsets.numbers;
|
|
6
|
+
const specialCharacters = charsets.specialCharacters;
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const policies = {
|
|
9
9
|
none: {
|
|
10
|
-
length: { minLength: 1 }
|
|
10
|
+
length: { minLength: 1 },
|
|
11
11
|
},
|
|
12
12
|
low: {
|
|
13
|
-
length: { minLength: 6 }
|
|
13
|
+
length: { minLength: 6 },
|
|
14
14
|
},
|
|
15
15
|
fair: {
|
|
16
16
|
length: { minLength: 8 },
|
|
17
17
|
contains: {
|
|
18
|
-
expressions: [lowerCase, upperCase, numbers]
|
|
19
|
-
}
|
|
18
|
+
expressions: [lowerCase, upperCase, numbers],
|
|
19
|
+
},
|
|
20
20
|
},
|
|
21
21
|
good: {
|
|
22
22
|
length: { minLength: 8 },
|
|
23
23
|
containsAtLeast: {
|
|
24
24
|
atLeast: 3,
|
|
25
|
-
expressions: [lowerCase, upperCase, numbers, specialCharacters]
|
|
26
|
-
}
|
|
25
|
+
expressions: [lowerCase, upperCase, numbers, specialCharacters],
|
|
26
|
+
},
|
|
27
27
|
},
|
|
28
28
|
excellent: {
|
|
29
29
|
length: { minLength: 10 },
|
|
30
30
|
containsAtLeast: {
|
|
31
31
|
atLeast: 3,
|
|
32
|
-
expressions: [lowerCase, upperCase, numbers, specialCharacters]
|
|
32
|
+
expressions: [lowerCase, upperCase, numbers, specialCharacters],
|
|
33
33
|
},
|
|
34
|
-
identicalChars: { max: 2 }
|
|
35
|
-
}
|
|
34
|
+
identicalChars: { max: 2 },
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const CHARACTER_TYPES = {
|
|
39
|
+
LOWERCASE: "lowercase",
|
|
40
|
+
UPPERCASE: "uppercase",
|
|
41
|
+
NUMBER: "number",
|
|
42
|
+
SPECIAL: "special",
|
|
36
43
|
};
|
|
37
44
|
|
|
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 {boolean} [require_3of4_character_types=false] - Whether to require 3 out of 4 character types (requires all 4 types to be specified)
|
|
50
|
+
* @property {'allow'|'disallow'} [identical_characters='disallow'] - Whether to allow >2 identical consecutive characters
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Default values for password options
|
|
55
|
+
* @constant
|
|
56
|
+
* @type {PasswordOptions}
|
|
57
|
+
*/
|
|
58
|
+
const DEFAULT_PASSWORD_OPTIONS = {
|
|
59
|
+
min_length: 15,
|
|
60
|
+
character_types: [],
|
|
61
|
+
require_3of4_character_types: false,
|
|
62
|
+
identical_characters: "disallow",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Creates a PasswordPolicy rules configuration from an Auth0
|
|
67
|
+
* `connection.options.password_options.complexity` object.
|
|
68
|
+
*
|
|
69
|
+
* @param {PasswordOptions} options - Auth0 password_options.complexity object
|
|
70
|
+
* @returns {Object} password-sheriff rules configuration object that can be passed to PasswordPolicy constructor
|
|
71
|
+
*/
|
|
72
|
+
function createRulesFromOptions(options = {}) {
|
|
73
|
+
const rules = {};
|
|
74
|
+
|
|
75
|
+
// Apply defaults
|
|
76
|
+
const {
|
|
77
|
+
min_length: minLength,
|
|
78
|
+
character_types: requiredTypes,
|
|
79
|
+
require_3of4_character_types: require3of4,
|
|
80
|
+
identical_characters: identicalChars,
|
|
81
|
+
} = { ...DEFAULT_PASSWORD_OPTIONS, ...options };
|
|
82
|
+
|
|
83
|
+
// Validate min_length is within acceptable range
|
|
84
|
+
if (minLength < 1 || minLength > 72) {
|
|
85
|
+
throw new Error("min_length must be between 1 and 72");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Handle min_length
|
|
89
|
+
rules.length = { minLength: minLength };
|
|
90
|
+
|
|
91
|
+
// Validate require_3of4_character_types prerequisite
|
|
92
|
+
if (require3of4) {
|
|
93
|
+
const hasAllFourTypes = Object.values(CHARACTER_TYPES).every(function (
|
|
94
|
+
type
|
|
95
|
+
) {
|
|
96
|
+
return requiredTypes.includes(type);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (!hasAllFourTypes) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`require_3of4_character_types can only be used when all four character types (${Object.values(
|
|
102
|
+
CHARACTER_TYPES
|
|
103
|
+
).join(", ")}) are selected`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (requiredTypes.length > 0 || require3of4) {
|
|
109
|
+
const expressions = [];
|
|
110
|
+
|
|
111
|
+
if (require3of4) {
|
|
112
|
+
// Use containsAtLeast with 3 out of 4
|
|
113
|
+
rules.containsAtLeast = {
|
|
114
|
+
atLeast: 3,
|
|
115
|
+
expressions: [lowerCase, upperCase, numbers, specialCharacters],
|
|
116
|
+
};
|
|
117
|
+
} else {
|
|
118
|
+
// Map character types to expressions
|
|
119
|
+
if (requiredTypes.includes(CHARACTER_TYPES.LOWERCASE)) {
|
|
120
|
+
expressions.push(lowerCase);
|
|
121
|
+
}
|
|
122
|
+
if (requiredTypes.includes(CHARACTER_TYPES.UPPERCASE)) {
|
|
123
|
+
expressions.push(upperCase);
|
|
124
|
+
}
|
|
125
|
+
if (requiredTypes.includes(CHARACTER_TYPES.NUMBER)) {
|
|
126
|
+
expressions.push(numbers);
|
|
127
|
+
}
|
|
128
|
+
if (requiredTypes.includes(CHARACTER_TYPES.SPECIAL)) {
|
|
129
|
+
expressions.push(specialCharacters);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
rules.contains = {
|
|
133
|
+
expressions: expressions,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (identicalChars === "disallow") {
|
|
139
|
+
rules.identicalChars = { max: 2 };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return rules;
|
|
143
|
+
}
|
|
144
|
+
|
|
38
145
|
module.exports = policies;
|
|
146
|
+
|
|
147
|
+
module.exports.createRulesFromOptions = createRulesFromOptions;
|
package/jest.config.json
ADDED
package/opslevel.yml
ADDED
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auth0-password-policies",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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>",
|
|
7
|
-
"license": "
|
|
7
|
+
"license": "UNLICENSED",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "jest"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"jest": "^29.0.0"
|
|
13
|
+
},
|
|
8
14
|
"dependencies": {
|
|
9
15
|
"password-sheriff": "^1.1.0"
|
|
10
16
|
}
|
package/test/test.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
const policies = require("..");
|
|
2
|
+
const { createRulesFromOptions } = policies;
|
|
3
|
+
|
|
4
|
+
describe("password policies", function () {
|
|
5
|
+
describe("main export", function () {
|
|
6
|
+
it("should export all props", function () {
|
|
7
|
+
expect(Object.keys(policies)).toEqual(
|
|
8
|
+
expect.arrayContaining([
|
|
9
|
+
"none",
|
|
10
|
+
"low",
|
|
11
|
+
"fair",
|
|
12
|
+
"good",
|
|
13
|
+
"excellent",
|
|
14
|
+
"createRulesFromOptions",
|
|
15
|
+
])
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("createRulesFromOptions helper", function () {
|
|
21
|
+
describe("min_length", function () {
|
|
22
|
+
it("should test min_length from 1 to 72", function () {
|
|
23
|
+
const auth0Config1 = {
|
|
24
|
+
min_length: 1,
|
|
25
|
+
identical_characters: "allow",
|
|
26
|
+
};
|
|
27
|
+
const rules = createRulesFromOptions(auth0Config1);
|
|
28
|
+
expect(rules).toEqual({
|
|
29
|
+
length: {
|
|
30
|
+
minLength: 1,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const auth0Config72 = {
|
|
35
|
+
min_length: 72,
|
|
36
|
+
identical_characters: "allow",
|
|
37
|
+
};
|
|
38
|
+
const rules72 = createRulesFromOptions(auth0Config72);
|
|
39
|
+
expect(rules72).toEqual({
|
|
40
|
+
length: {
|
|
41
|
+
minLength: 72,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should throw an error when min_length > 72", function () {
|
|
47
|
+
expect(function () {
|
|
48
|
+
const auth0Config = {
|
|
49
|
+
min_length: 73,
|
|
50
|
+
};
|
|
51
|
+
createRulesFromOptions(auth0Config);
|
|
52
|
+
}).toThrow("min_length must be between 1 and 72");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("character_types", function () {
|
|
57
|
+
it("should enforce required character types", function () {
|
|
58
|
+
const auth0Config = {
|
|
59
|
+
character_types: ["lowercase", "uppercase", "number", "special"],
|
|
60
|
+
identical_characters: "allow",
|
|
61
|
+
min_length: 4,
|
|
62
|
+
};
|
|
63
|
+
const rules = createRulesFromOptions(auth0Config);
|
|
64
|
+
expect(rules).toHaveProperty("length");
|
|
65
|
+
expect(rules.length).toEqual({ minLength: 4 });
|
|
66
|
+
expect(rules).toHaveProperty("contains");
|
|
67
|
+
expect(rules.contains).toHaveProperty("expressions");
|
|
68
|
+
expect(rules.contains.expressions).toHaveLength(4);
|
|
69
|
+
// Verify each expression has test and explain functions
|
|
70
|
+
rules.contains.expressions.forEach(function (expr) {
|
|
71
|
+
expect(expr).toHaveProperty("test");
|
|
72
|
+
expect(expr).toHaveProperty("explain");
|
|
73
|
+
expect(typeof expr.test).toBe("function");
|
|
74
|
+
expect(typeof expr.explain).toBe("function");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("require_3of4_character_types", function () {
|
|
80
|
+
it("should enforce 3 out of 4 character types when all 4 types are specified", function () {
|
|
81
|
+
const auth0Config = {
|
|
82
|
+
character_types: ["lowercase", "uppercase", "number", "special"],
|
|
83
|
+
require_3of4_character_types: true,
|
|
84
|
+
identical_characters: "allow",
|
|
85
|
+
min_length: 3,
|
|
86
|
+
};
|
|
87
|
+
const rules = createRulesFromOptions(auth0Config);
|
|
88
|
+
expect(rules).toHaveProperty("length");
|
|
89
|
+
expect(rules.length).toEqual({ minLength: 3 });
|
|
90
|
+
expect(rules).toHaveProperty("containsAtLeast");
|
|
91
|
+
expect(rules.containsAtLeast).toHaveProperty("atLeast", 3);
|
|
92
|
+
expect(rules.containsAtLeast).toHaveProperty("expressions");
|
|
93
|
+
expect(rules.containsAtLeast.expressions).toHaveLength(4);
|
|
94
|
+
// Verify each expression has test and explain functions
|
|
95
|
+
rules.containsAtLeast.expressions.forEach(function (expr) {
|
|
96
|
+
expect(expr).toHaveProperty("test");
|
|
97
|
+
expect(expr).toHaveProperty("explain");
|
|
98
|
+
expect(typeof expr.test).toBe("function");
|
|
99
|
+
expect(typeof expr.explain).toBe("function");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should throw an error when require_3of4_character_types is used without all 4 character types", function () {
|
|
104
|
+
expect(function () {
|
|
105
|
+
const auth0Config = {
|
|
106
|
+
character_types: ["lowercase", "uppercase"],
|
|
107
|
+
require_3of4_character_types: true,
|
|
108
|
+
};
|
|
109
|
+
createRulesFromOptions(auth0Config);
|
|
110
|
+
}).toThrow(
|
|
111
|
+
"require_3of4_character_types can only be used when all four character types (lowercase, uppercase, number, special) are selected"
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("identical_characters", function () {
|
|
117
|
+
it("should disallow more than 2 identical characters when specified", function () {
|
|
118
|
+
const auth0Config = {
|
|
119
|
+
identical_characters: "disallow",
|
|
120
|
+
};
|
|
121
|
+
const rules = createRulesFromOptions(auth0Config);
|
|
122
|
+
expect(rules).toEqual({
|
|
123
|
+
length: {
|
|
124
|
+
minLength: 15,
|
|
125
|
+
},
|
|
126
|
+
identicalChars: {
|
|
127
|
+
max: 2,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should allow more than 2 identical characters when specified", function () {
|
|
133
|
+
const auth0Config = {
|
|
134
|
+
identical_characters: "allow",
|
|
135
|
+
};
|
|
136
|
+
const rules = createRulesFromOptions(auth0Config);
|
|
137
|
+
expect(rules).toEqual({
|
|
138
|
+
length: {
|
|
139
|
+
minLength: 15,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe("default values", function () {
|
|
146
|
+
it("should apply default values when not specified", function () {
|
|
147
|
+
const auth0Config = {};
|
|
148
|
+
const rules = createRulesFromOptions(auth0Config);
|
|
149
|
+
expect(rules).toEqual({
|
|
150
|
+
length: {
|
|
151
|
+
minLength: 15,
|
|
152
|
+
},
|
|
153
|
+
identicalChars: {
|
|
154
|
+
max: 2,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("should allow overriding default values", function () {
|
|
160
|
+
const auth0Config = {
|
|
161
|
+
min_length: 5,
|
|
162
|
+
identical_characters: "allow",
|
|
163
|
+
};
|
|
164
|
+
const rules = createRulesFromOptions(auth0Config);
|
|
165
|
+
expect(rules).toEqual({
|
|
166
|
+
length: {
|
|
167
|
+
minLength: 5,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
package/yarn.lock
DELETED