auth0-deploy-cli 8.29.3 → 8.31.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/.circleci/config.yml +15 -84
- package/.github/workflows/npm-publish.yml +52 -0
- package/AGENTS.md +21 -1
- package/CHANGELOG.md +30 -1
- package/CLAUDE.md +3 -0
- package/lib/args.d.ts +1 -0
- package/lib/args.js +5 -0
- package/lib/commands/export.js +5 -1
- package/lib/context/directory/handlers/actions.js +1 -2
- package/lib/context/directory/handlers/clientGrants.js +9 -1
- package/lib/context/directory/handlers/clients.js +6 -1
- package/lib/context/directory/handlers/connections.js +7 -1
- package/lib/context/directory/handlers/databases.js +6 -1
- package/lib/context/directory/handlers/resourceServers.js +6 -1
- package/lib/context/directory/handlers/rules.js +6 -1
- package/lib/context/directory/handlers/triggers.js +1 -3
- package/lib/context/yaml/handlers/clientGrants.js +9 -1
- package/lib/context/yaml/handlers/clients.js +5 -0
- package/lib/context/yaml/handlers/connections.js +7 -1
- package/lib/context/yaml/handlers/databases.js +7 -1
- package/lib/context/yaml/handlers/resourceServers.js +30 -3
- package/lib/context/yaml/handlers/rules.js +5 -0
- package/lib/context/yaml/index.js +2 -1
- package/lib/tools/auth0/handlers/actions.js +13 -15
- package/lib/tools/auth0/handlers/clientGrants.js +31 -4
- package/lib/tools/auth0/handlers/connections.d.ts +5 -0
- package/lib/tools/auth0/handlers/connections.js +9 -1
- package/lib/tools/auth0/handlers/customDomains.d.ts +5 -0
- package/lib/tools/auth0/handlers/customDomains.js +26 -0
- package/lib/tools/auth0/handlers/prompts.js +18 -0
- package/lib/tools/auth0/handlers/tenant.js +5 -0
- package/lib/types.d.ts +1 -0
- package/lib/utils.js +13 -1
- package/package.json +8 -7
package/.circleci/config.yml
CHANGED
|
@@ -6,7 +6,7 @@ orbs:
|
|
|
6
6
|
jobs:
|
|
7
7
|
e2e_test_as_node_module:
|
|
8
8
|
docker:
|
|
9
|
-
- image: cimg/node:22.
|
|
9
|
+
- image: cimg/node:22.19.0
|
|
10
10
|
working_directory: ~/repo
|
|
11
11
|
steps:
|
|
12
12
|
- checkout
|
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
|
|
16
16
|
e2e_test_as_cli:
|
|
17
17
|
docker:
|
|
18
|
-
- image: cimg/node:22.
|
|
18
|
+
- image: cimg/node:22.19.0
|
|
19
19
|
working_directory: ~/repo
|
|
20
20
|
steps:
|
|
21
21
|
- checkout
|
|
@@ -45,57 +45,31 @@ jobs:
|
|
|
45
45
|
- run: npm run test:coverage
|
|
46
46
|
- persist_to_workspace:
|
|
47
47
|
root: ~/repo
|
|
48
|
-
paths:
|
|
48
|
+
paths:
|
|
49
|
+
- .
|
|
49
50
|
- codecov/upload
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
parameters:
|
|
53
|
-
v:
|
|
54
|
-
type: string
|
|
55
|
-
default: "lts"
|
|
56
|
-
docker:
|
|
57
|
-
- image: cimg/node:<< parameters.v >>
|
|
58
|
-
working_directory: ~/repo
|
|
59
|
-
steps:
|
|
60
|
-
- attach_workspace:
|
|
61
|
-
at: ~/repo
|
|
62
|
-
- run:
|
|
63
|
-
name: Authenticate with registry
|
|
64
|
-
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
|
65
|
-
- run:
|
|
66
|
-
name: Publish package
|
|
67
|
-
command: npm publish
|
|
68
|
-
|
|
69
|
-
deploy_beta:
|
|
70
|
-
parameters:
|
|
71
|
-
v:
|
|
72
|
-
type: string
|
|
73
|
-
default: "lts"
|
|
52
|
+
does_typescript_compile:
|
|
74
53
|
docker:
|
|
75
|
-
- image: cimg/node
|
|
54
|
+
- image: cimg/node:22.19.0
|
|
76
55
|
working_directory: ~/repo
|
|
77
56
|
steps:
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
- run:
|
|
81
|
-
name: Authenticate with registry
|
|
82
|
-
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
|
83
|
-
- run:
|
|
84
|
-
name: Publish beta package
|
|
85
|
-
command: npm publish --tag beta
|
|
57
|
+
- checkout
|
|
58
|
+
- run: npm i
|
|
59
|
+
- run: npx tsc --noEmit
|
|
86
60
|
|
|
87
|
-
|
|
61
|
+
does_format_pass:
|
|
88
62
|
docker:
|
|
89
|
-
- image: cimg/node:22.
|
|
63
|
+
- image: cimg/node:22.19.0
|
|
90
64
|
working_directory: ~/repo
|
|
91
65
|
steps:
|
|
92
66
|
- checkout
|
|
93
67
|
- run: npm i
|
|
94
|
-
- run:
|
|
68
|
+
- run: npm run format:check
|
|
95
69
|
|
|
96
70
|
does_lint_pass:
|
|
97
71
|
docker:
|
|
98
|
-
- image: cimg/node:22.
|
|
72
|
+
- image: cimg/node:22.19.0
|
|
99
73
|
working_directory: ~/repo
|
|
100
74
|
steps:
|
|
101
75
|
- checkout
|
|
@@ -119,6 +93,8 @@ workflows:
|
|
|
119
93
|
jobs:
|
|
120
94
|
- does_typescript_compile:
|
|
121
95
|
name: Does Typescript compile?
|
|
96
|
+
- does_format_pass:
|
|
97
|
+
name: Does format pass?
|
|
122
98
|
- does_lint_pass:
|
|
123
99
|
name: Does lint pass?
|
|
124
100
|
- unit_test:
|
|
@@ -128,48 +104,3 @@ workflows:
|
|
|
128
104
|
name: Unit tests with Node current
|
|
129
105
|
v: "22.12.0"
|
|
130
106
|
|
|
131
|
-
test_and_deploy:
|
|
132
|
-
jobs:
|
|
133
|
-
- unit_test:
|
|
134
|
-
name: Unit tests with Node LTS
|
|
135
|
-
v: "lts"
|
|
136
|
-
filters:
|
|
137
|
-
branches:
|
|
138
|
-
only: master
|
|
139
|
-
tags:
|
|
140
|
-
only: /^v.*/
|
|
141
|
-
- deploy:
|
|
142
|
-
name: Publish to NPM
|
|
143
|
-
v: "lts"
|
|
144
|
-
requires:
|
|
145
|
-
- Unit tests with Node LTS
|
|
146
|
-
filters:
|
|
147
|
-
branches:
|
|
148
|
-
ignore: /.*/
|
|
149
|
-
tags:
|
|
150
|
-
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
|
|
151
|
-
context:
|
|
152
|
-
- publish-npm
|
|
153
|
-
|
|
154
|
-
test_and_deploy_beta:
|
|
155
|
-
jobs:
|
|
156
|
-
- unit_test:
|
|
157
|
-
name: Unit tests with Node LTS (Beta)
|
|
158
|
-
v: "lts"
|
|
159
|
-
filters:
|
|
160
|
-
branches:
|
|
161
|
-
only: beta
|
|
162
|
-
tags:
|
|
163
|
-
only: /^v[0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+$/
|
|
164
|
-
- deploy_beta:
|
|
165
|
-
name: Publish Beta to NPM
|
|
166
|
-
v: "lts"
|
|
167
|
-
requires:
|
|
168
|
-
- Unit tests with Node LTS (Beta)
|
|
169
|
-
filters:
|
|
170
|
-
branches:
|
|
171
|
-
ignore: /.*/
|
|
172
|
-
tags:
|
|
173
|
-
only: /^v[0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+$/
|
|
174
|
-
context:
|
|
175
|
-
- publish-npm
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Publish package to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v6
|
|
20
|
+
with:
|
|
21
|
+
node-version: 22.14.0
|
|
22
|
+
registry-url: https://registry.npmjs.org
|
|
23
|
+
package-manager-cache: false
|
|
24
|
+
|
|
25
|
+
- name: Update npm for trusted publishing
|
|
26
|
+
run: npm install --global npm@11
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: npm install
|
|
30
|
+
|
|
31
|
+
- name: Build package
|
|
32
|
+
run: npm run build
|
|
33
|
+
|
|
34
|
+
- name: Determine npm dist-tag
|
|
35
|
+
id: npm_dist_tag
|
|
36
|
+
shell: bash
|
|
37
|
+
run: |
|
|
38
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
39
|
+
|
|
40
|
+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-beta\.[0-9]+)?$ ]]; then
|
|
41
|
+
echo "Unsupported release tag: ${GITHUB_REF_NAME}" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [[ "$VERSION" == *"-beta."* ]]; then
|
|
46
|
+
echo "value=beta" >> "$GITHUB_OUTPUT"
|
|
47
|
+
else
|
|
48
|
+
echo "value=latest" >> "$GITHUB_OUTPUT"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
- name: Publish package
|
|
52
|
+
run: npm publish --tag "${{ steps.npm_dist_tag.outputs.value }}"
|
package/AGENTS.md
CHANGED
|
@@ -63,7 +63,7 @@ npm run build && node lib/index.js import -c config-dev.json -i ./local-export/t
|
|
|
63
63
|
|
|
64
64
|
### File Structure
|
|
65
65
|
|
|
66
|
-
```
|
|
66
|
+
```text
|
|
67
67
|
src/ # TypeScript source
|
|
68
68
|
├── index.ts # CLI entry point
|
|
69
69
|
├── commands/ # import.ts, export.ts
|
|
@@ -225,6 +225,26 @@ npm run lint
|
|
|
225
225
|
- [ ] Tested with both YAML and directory formats if applicable
|
|
226
226
|
- [ ] Checked backward compatibility
|
|
227
227
|
|
|
228
|
+
### Checklist for Every Feature or Fix
|
|
229
|
+
|
|
230
|
+
#### Completion
|
|
231
|
+
|
|
232
|
+
- [ ] Change is complete and matches the intended feature or fix
|
|
233
|
+
- [ ] TypeScript compiles and tests pass locally
|
|
234
|
+
|
|
235
|
+
#### Format and behavior
|
|
236
|
+
|
|
237
|
+
- [ ] Works for both YAML and directory flows when the resource supports both
|
|
238
|
+
- [ ] Keyword preservation still works as expected
|
|
239
|
+
- [ ] Sorted-key output stays stable; no unintended order changes were introduced
|
|
240
|
+
- [ ] Exported configs do not rely on IDs by default; resources should link by names where possible
|
|
241
|
+
|
|
242
|
+
#### Safety and compatibility
|
|
243
|
+
|
|
244
|
+
- [ ] Dry-run output is correct and non-destructive
|
|
245
|
+
- [ ] Unit tests cover the changed path and any important edge cases
|
|
246
|
+
- [ ] Backward compatibility was checked and no existing behavior was unintentionally broken
|
|
247
|
+
|
|
228
248
|
### Commit Message Format
|
|
229
249
|
|
|
230
250
|
Follow conventional commits style:
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [8.31.0] - 2026-04-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add support for `is_default` on `customDomains`. [#1330]
|
|
15
|
+
- Add `AUTH0_EXPORT_ORDERED` config property and `--export_ordered` flag for stable exports. [#1335]
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Fix `clientGrants` matching when multiple grants share the same `client_id` and `audience`. [#1341]
|
|
20
|
+
|
|
21
|
+
## [8.30.0] - 2026-04-02
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Add support for passkey enrollment in `prompts` partials. [#1328]
|
|
26
|
+
- Add support for `dpop_signing_alg` validation in OIDC and Okta `connections`. [#1343]
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Fix action module fetching logic to resolve correct module IDs. [#1340]
|
|
31
|
+
- Fix `AUTH0_EXCLUDED_*` options not respected during export. [#1342]
|
|
32
|
+
|
|
10
33
|
## [8.29.3] - 2026-03-25
|
|
11
34
|
|
|
12
35
|
### Fixed
|
|
@@ -1692,9 +1715,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1692
1715
|
[#1320]: https://github.com/auth0/auth0-deploy-cli/issues/1320
|
|
1693
1716
|
[#1321]: https://github.com/auth0/auth0-deploy-cli/issues/1321
|
|
1694
1717
|
[#1322]: https://github.com/auth0/auth0-deploy-cli/issues/1322
|
|
1718
|
+
[#1328]: https://github.com/auth0/auth0-deploy-cli/issues/1328
|
|
1695
1719
|
[#1333]: https://github.com/auth0/auth0-deploy-cli/issues/1333
|
|
1696
1720
|
[#1334]: https://github.com/auth0/auth0-deploy-cli/issues/1334
|
|
1697
|
-
[
|
|
1721
|
+
[#1340]: https://github.com/auth0/auth0-deploy-cli/issues/1340
|
|
1722
|
+
[#1342]: https://github.com/auth0/auth0-deploy-cli/issues/1342
|
|
1723
|
+
[#1343]: https://github.com/auth0/auth0-deploy-cli/issues/1343
|
|
1724
|
+
[Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.31.0...HEAD
|
|
1725
|
+
[8.31.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.30.0...v8.31.0
|
|
1726
|
+
[8.30.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.29.3...v8.30.0
|
|
1698
1727
|
[8.29.3]: https://github.com/auth0/auth0-deploy-cli/compare/v8.29.2...v8.29.3
|
|
1699
1728
|
[8.29.2]: https://github.com/auth0/auth0-deploy-cli/compare/v8.29.1...v8.29.2
|
|
1700
1729
|
[8.29.1]: https://github.com/auth0/auth0-deploy-cli/compare/v8.29.0...v8.29.1
|
package/CLAUDE.md
ADDED
package/lib/args.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ type ExportSpecificParams = {
|
|
|
16
16
|
format: 'yaml' | 'directory';
|
|
17
17
|
output_folder: string;
|
|
18
18
|
export_ids?: boolean;
|
|
19
|
+
export_ordered?: boolean;
|
|
19
20
|
};
|
|
20
21
|
export type ExportParams = ExportSpecificParams & SharedParams;
|
|
21
22
|
export type ImportParams = ImportSpecificParams & SharedParams;
|
package/lib/args.js
CHANGED
|
@@ -83,6 +83,11 @@ function getParams() {
|
|
|
83
83
|
type: 'boolean',
|
|
84
84
|
default: false,
|
|
85
85
|
},
|
|
86
|
+
export_ordered: {
|
|
87
|
+
alias: 's',
|
|
88
|
+
describe: 'Order keys in exported JSON files for consistent diffs.',
|
|
89
|
+
type: 'boolean',
|
|
90
|
+
},
|
|
86
91
|
})
|
|
87
92
|
.example('$0 export -c config.json -f yaml -o path/to/export', 'Dump Auth0 config to folder in YAML format')
|
|
88
93
|
.example('$0 export -c config.json -f directory -o path/to/export', 'Dump Auth0 config to folder in directory format')
|
package/lib/commands/export.js
CHANGED
|
@@ -11,7 +11,7 @@ const logger_1 = __importDefault(require("../logger"));
|
|
|
11
11
|
const utils_1 = require("../utils");
|
|
12
12
|
const index_1 = require("../context/index");
|
|
13
13
|
async function exportCMD(params) {
|
|
14
|
-
const { output_folder: outputFolder, base_path: basePath, config_file: configFile, config: configObj, export_ids: exportIds, secret: clientSecret, env: shouldInheritEnv = false, experimental_ea: experimentalEA, } = params;
|
|
14
|
+
const { output_folder: outputFolder, base_path: basePath, config_file: configFile, config: configObj, export_ids: exportIds, export_ordered: exportOrdered, secret: clientSecret, env: shouldInheritEnv = false, experimental_ea: experimentalEA, } = params;
|
|
15
15
|
if (shouldInheritEnv) {
|
|
16
16
|
nconf_1.default.env().use('memory');
|
|
17
17
|
}
|
|
@@ -32,6 +32,10 @@ async function exportCMD(params) {
|
|
|
32
32
|
if (exportIds) {
|
|
33
33
|
overrides.AUTH0_EXPORT_IDENTIFIERS = exportIds;
|
|
34
34
|
}
|
|
35
|
+
// Allow passed in export_ordered to override the configured one
|
|
36
|
+
if (exportOrdered) {
|
|
37
|
+
overrides.AUTH0_EXPORT_ORDERED = exportOrdered;
|
|
38
|
+
}
|
|
35
39
|
// Overrides AUTH0_INCLUDE_EXPERIMENTAL_EA is experimental_ea passed in command line
|
|
36
40
|
if (experimentalEA) {
|
|
37
41
|
overrides.AUTH0_EXPERIMENTAL_EA = experimentalEA;
|
|
@@ -99,8 +99,7 @@ async function dump(context) {
|
|
|
99
99
|
// Dump template metadata
|
|
100
100
|
const name = (0, utils_1.sanitize)(action.name);
|
|
101
101
|
const actionFile = path_1.default.join(actionsFolder, `${name}.json`);
|
|
102
|
-
|
|
103
|
-
fs_extra_1.default.writeFileSync(actionFile, JSON.stringify(mapToAction(context.filePath, action, includeIdentifiers), null, 2));
|
|
102
|
+
(0, utils_1.dumpJSON)(actionFile, mapToAction(context.filePath, action, includeIdentifiers));
|
|
104
103
|
});
|
|
105
104
|
}
|
|
106
105
|
const actionsHandler = {
|
|
@@ -25,13 +25,14 @@ function parse(context) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
async function dump(context) {
|
|
28
|
-
|
|
28
|
+
let { clientGrants } = context.assets;
|
|
29
29
|
if (!clientGrants)
|
|
30
30
|
return; // Skip, nothing to dump
|
|
31
31
|
const grantsFolder = path_1.default.join(context.filePath, tools_1.constants.CLIENTS_GRANTS_DIRECTORY);
|
|
32
32
|
fs_extra_1.default.ensureDirSync(grantsFolder);
|
|
33
33
|
if (clientGrants.length === 0)
|
|
34
34
|
return;
|
|
35
|
+
const excludedClientsByNames = (context.assets.exclude && context.assets.exclude.clients) || [];
|
|
35
36
|
const allResourceServers = await (0, client_1.paginate)(context.mgmtClient.resourceServers.list, {
|
|
36
37
|
paginate: true,
|
|
37
38
|
include_totals: true,
|
|
@@ -40,6 +41,13 @@ async function dump(context) {
|
|
|
40
41
|
paginate: true,
|
|
41
42
|
include_totals: true,
|
|
42
43
|
});
|
|
44
|
+
// Filter out grants for excluded clients
|
|
45
|
+
if (excludedClientsByNames.length) {
|
|
46
|
+
const excludedClientIds = new Set(allClients
|
|
47
|
+
.filter((c) => c.name !== undefined && excludedClientsByNames.includes(c.name))
|
|
48
|
+
.map((c) => c.client_id));
|
|
49
|
+
clientGrants = clientGrants.filter((grant) => !excludedClientIds.has(grant.client_id));
|
|
50
|
+
}
|
|
43
51
|
// Convert client_id to the client name for readability
|
|
44
52
|
clientGrants.forEach((grant) => {
|
|
45
53
|
const dumpGrant = { ...grant };
|
|
@@ -36,10 +36,15 @@ function parse(context) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
async function dump(context) {
|
|
39
|
-
|
|
39
|
+
let { clients } = context.assets;
|
|
40
40
|
const { userAttributeProfiles, connectionProfiles } = context.assets;
|
|
41
41
|
if (!clients)
|
|
42
42
|
return; // Skip, nothing to dump
|
|
43
|
+
// Filter excluded clients
|
|
44
|
+
const excludedClients = (context.assets.exclude && context.assets.exclude.clients) || [];
|
|
45
|
+
if (excludedClients.length) {
|
|
46
|
+
clients = clients.filter((client) => !excludedClients.includes(client.name ?? ''));
|
|
47
|
+
}
|
|
43
48
|
const clientsFolder = path_1.default.join(context.filePath, tools_1.constants.CLIENTS_DIRECTORY);
|
|
44
49
|
fs_extra_1.default.ensureDirSync(clientsFolder);
|
|
45
50
|
clients.forEach((client) => {
|
|
@@ -40,9 +40,15 @@ function parse(context) {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
async function dump(context) {
|
|
43
|
-
|
|
43
|
+
let { connections } = context.assets;
|
|
44
|
+
const { clientsOrig } = context.assets;
|
|
44
45
|
if (!connections)
|
|
45
46
|
return; // Skip, nothing to dump
|
|
47
|
+
// Filter excluded connections
|
|
48
|
+
const excludedConnections = (context.assets.exclude && context.assets.exclude.connections) || [];
|
|
49
|
+
if (excludedConnections.length) {
|
|
50
|
+
connections = connections.filter((connection) => !excludedConnections.includes(connection.name));
|
|
51
|
+
}
|
|
46
52
|
const connectionsFolder = path_1.default.join(context.filePath, tools_1.constants.CONNECTIONS_DIRECTORY);
|
|
47
53
|
fs_extra_1.default.ensureDirSync(connectionsFolder);
|
|
48
54
|
// Convert enabled_clients from id to name
|
|
@@ -65,9 +65,14 @@ function parse(context) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
async function dump(context) {
|
|
68
|
-
|
|
68
|
+
let { databases } = context.assets;
|
|
69
69
|
if (!databases)
|
|
70
70
|
return; // Skip, nothing to dump
|
|
71
|
+
// Filter excluded databases
|
|
72
|
+
const excludedDatabases = (context.assets.exclude && context.assets.exclude.databases) || [];
|
|
73
|
+
if (excludedDatabases.length) {
|
|
74
|
+
databases = databases.filter((database) => !excludedDatabases.includes(database.name));
|
|
75
|
+
}
|
|
71
76
|
const databasesFolder = path_1.default.join(context.filePath, tools_1.constants.DATABASE_CONNECTIONS_DIRECTORY);
|
|
72
77
|
fs_extra_1.default.ensureDirSync(databasesFolder);
|
|
73
78
|
databases.forEach((database) => {
|
|
@@ -24,10 +24,15 @@ function parse(context) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
async function dump(context) {
|
|
27
|
-
|
|
27
|
+
let { resourceServers } = context.assets;
|
|
28
28
|
let { clients } = context.assets;
|
|
29
29
|
if (!resourceServers)
|
|
30
30
|
return; // Skip, nothing to dump
|
|
31
|
+
// Filter excluded resource servers
|
|
32
|
+
const excludedResourceServers = (context.assets.exclude && context.assets.exclude.resourceServers) || [];
|
|
33
|
+
if (excludedResourceServers.length) {
|
|
34
|
+
resourceServers = resourceServers.filter((resourceServer) => !excludedResourceServers.includes(resourceServer.name ?? ''));
|
|
35
|
+
}
|
|
31
36
|
const resourceServersFolder = path_1.default.join(context.filePath, tools_1.constants.RESOURCE_SERVERS_DIRECTORY);
|
|
32
37
|
fs_extra_1.default.ensureDirSync(resourceServersFolder);
|
|
33
38
|
if (clients === undefined) {
|
|
@@ -30,9 +30,14 @@ function parse(context) {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
async function dump(context) {
|
|
33
|
-
|
|
33
|
+
let { rules } = context.assets;
|
|
34
34
|
if (!rules)
|
|
35
35
|
return; // Skip, nothing to dump
|
|
36
|
+
// Filter excluded rules
|
|
37
|
+
const excludedRules = (context.assets.exclude && context.assets.exclude.rules) || [];
|
|
38
|
+
if (excludedRules.length) {
|
|
39
|
+
rules = rules.filter((rule) => !excludedRules.includes(rule.name));
|
|
40
|
+
}
|
|
36
41
|
// Create Rules folder
|
|
37
42
|
const rulesFolder = path_1.default.join(context.filePath, tools_1.constants.RULES_DIRECTORY);
|
|
38
43
|
fs_extra_1.default.ensureDirSync(rulesFolder);
|
|
@@ -7,7 +7,6 @@ const path_1 = __importDefault(require("path"));
|
|
|
7
7
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
8
|
const tools_1 = require("../../../tools");
|
|
9
9
|
const utils_1 = require("../../../utils");
|
|
10
|
-
const logger_1 = __importDefault(require("../../../logger"));
|
|
11
10
|
function parse(context) {
|
|
12
11
|
const triggersFolder = path_1.default.join(context.filePath, tools_1.constants.TRIGGERS_DIRECTORY);
|
|
13
12
|
if (!(0, utils_1.existsMustBeDir)(triggersFolder))
|
|
@@ -29,8 +28,7 @@ async function dump(context) {
|
|
|
29
28
|
const triggersFolder = path_1.default.join(context.filePath, tools_1.constants.TRIGGERS_DIRECTORY);
|
|
30
29
|
fs_extra_1.default.ensureDirSync(triggersFolder);
|
|
31
30
|
const triggerFile = path_1.default.join(triggersFolder, 'triggers.json');
|
|
32
|
-
|
|
33
|
-
fs_extra_1.default.writeFileSync(triggerFile, JSON.stringify(triggers, null, 2));
|
|
31
|
+
(0, utils_1.dumpJSON)(triggerFile, triggers);
|
|
34
32
|
}
|
|
35
33
|
const triggersHandler = {
|
|
36
34
|
parse,
|
|
@@ -12,7 +12,7 @@ async function parse(context) {
|
|
|
12
12
|
}
|
|
13
13
|
async function dump(context) {
|
|
14
14
|
let { clients } = context.assets;
|
|
15
|
-
|
|
15
|
+
let { clientGrants } = context.assets;
|
|
16
16
|
if (!clientGrants)
|
|
17
17
|
return { clientGrants: null };
|
|
18
18
|
if (clients === undefined) {
|
|
@@ -21,6 +21,14 @@ async function dump(context) {
|
|
|
21
21
|
include_totals: true,
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
+
// Filter out grants for excluded clients
|
|
25
|
+
const excludedClientsByNames = (context.assets.exclude && context.assets.exclude.clients) || [];
|
|
26
|
+
if (excludedClientsByNames.length) {
|
|
27
|
+
const excludedClientIds = new Set((clients || [])
|
|
28
|
+
.filter((c) => c.name !== undefined && excludedClientsByNames.includes(c.name))
|
|
29
|
+
.map((c) => c.client_id));
|
|
30
|
+
clientGrants = clientGrants.filter((grant) => !excludedClientIds.has(grant.client_id));
|
|
31
|
+
}
|
|
24
32
|
// Convert client_id to the client name for readability
|
|
25
33
|
return {
|
|
26
34
|
clientGrants: clientGrants.map((grant) => {
|
|
@@ -36,6 +36,11 @@ async function dump(context) {
|
|
|
36
36
|
const { userAttributeProfiles, connectionProfiles } = context.assets;
|
|
37
37
|
if (!clients)
|
|
38
38
|
return { clients: null };
|
|
39
|
+
// Filter excluded clients
|
|
40
|
+
const excludedClients = (context.assets.exclude && context.assets.exclude.clients) || [];
|
|
41
|
+
if (excludedClients.length) {
|
|
42
|
+
clients = clients.filter((client) => !excludedClients.includes(client.name ?? ''));
|
|
43
|
+
}
|
|
39
44
|
// map ids to names for user attribute profiles and connection profiles
|
|
40
45
|
clients = clients.map((client) => {
|
|
41
46
|
const userAttributeProfileId = client?.express_configuration?.user_attribute_profile_id;
|
|
@@ -50,9 +50,15 @@ const getFormattedOptions = (connection, clients) => {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
async function dump(context) {
|
|
53
|
-
|
|
53
|
+
let { connections } = context.assets;
|
|
54
|
+
const { clients } = context.assets;
|
|
54
55
|
if (!connections)
|
|
55
56
|
return { connections: null };
|
|
57
|
+
// Filter excluded connections
|
|
58
|
+
const excludedConnections = (context.assets.exclude && context.assets.exclude.connections) || [];
|
|
59
|
+
if (excludedConnections.length) {
|
|
60
|
+
connections = connections.filter((connection) => !excludedConnections.includes(connection.name));
|
|
61
|
+
}
|
|
56
62
|
return {
|
|
57
63
|
connections: connections.map((connection) => {
|
|
58
64
|
let dumpedConnection = {
|
|
@@ -31,9 +31,15 @@ async function parse(context) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
async function dump(context) {
|
|
34
|
-
|
|
34
|
+
let { databases } = context.assets;
|
|
35
|
+
const { clients } = context.assets;
|
|
35
36
|
if (!databases)
|
|
36
37
|
return { databases: null };
|
|
38
|
+
// Filter excluded databases
|
|
39
|
+
const excludedDatabases = (context.assets.exclude && context.assets.exclude.databases) || [];
|
|
40
|
+
if (excludedDatabases.length) {
|
|
41
|
+
databases = databases.filter((database) => !excludedDatabases.includes(database.name));
|
|
42
|
+
}
|
|
37
43
|
const sortCustomScripts = ([name1], [name2]) => {
|
|
38
44
|
if (name1 === name2)
|
|
39
45
|
return 0;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const client_1 = require("../../../tools/auth0/client");
|
|
4
4
|
const utils_1 = require("../../../utils");
|
|
5
|
-
async function
|
|
5
|
+
async function parse(context) {
|
|
6
6
|
const { resourceServers } = context.assets;
|
|
7
7
|
let { clients } = context.assets;
|
|
8
8
|
if (!resourceServers) {
|
|
@@ -24,8 +24,35 @@ async function dumpAndParse(context) {
|
|
|
24
24
|
}),
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
+
async function dump(context) {
|
|
28
|
+
let { resourceServers } = context.assets;
|
|
29
|
+
let { clients } = context.assets;
|
|
30
|
+
if (!resourceServers) {
|
|
31
|
+
return { resourceServers: null };
|
|
32
|
+
}
|
|
33
|
+
// Filter excluded resource servers
|
|
34
|
+
const excludedResourceServers = (context.assets.exclude && context.assets.exclude.resourceServers) || [];
|
|
35
|
+
if (excludedResourceServers.length) {
|
|
36
|
+
resourceServers = resourceServers.filter((rs) => !excludedResourceServers.includes(rs.name ?? ''));
|
|
37
|
+
}
|
|
38
|
+
if (clients === undefined) {
|
|
39
|
+
clients = await (0, client_1.paginate)(context.mgmtClient.clients.list, {
|
|
40
|
+
paginate: true,
|
|
41
|
+
include_totals: true,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
resourceServers: resourceServers.map((rs) => {
|
|
46
|
+
const dumpResourceServer = { ...rs };
|
|
47
|
+
if (dumpResourceServer.client_id) {
|
|
48
|
+
dumpResourceServer.client_id = (0, utils_1.convertClientIdToName)(dumpResourceServer.client_id, clients || []);
|
|
49
|
+
}
|
|
50
|
+
return dumpResourceServer;
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
27
54
|
const resourceServersHandler = {
|
|
28
|
-
parse
|
|
29
|
-
dump
|
|
55
|
+
parse,
|
|
56
|
+
dump,
|
|
30
57
|
};
|
|
31
58
|
exports.default = resourceServersHandler;
|
|
@@ -25,6 +25,11 @@ async function dump(context) {
|
|
|
25
25
|
if (!rules) {
|
|
26
26
|
return { rules: null };
|
|
27
27
|
}
|
|
28
|
+
// Filter excluded rules
|
|
29
|
+
const excludedRules = (context.assets.exclude && context.assets.exclude.rules) || [];
|
|
30
|
+
if (excludedRules.length) {
|
|
31
|
+
rules = rules.filter((rule) => !excludedRules.includes(rule.name));
|
|
32
|
+
}
|
|
28
33
|
// Create Rules folder
|
|
29
34
|
const rulesFolder = path_1.default.join(context.basePath, 'rules');
|
|
30
35
|
fs_extra_1.default.ensureDirSync(rulesFolder);
|
|
@@ -183,7 +183,8 @@ class YAMLContext {
|
|
|
183
183
|
cleaned = (0, utils_1.stripIdentifiers)(auth0, cleaned);
|
|
184
184
|
}
|
|
185
185
|
// Write YAML File
|
|
186
|
-
const
|
|
186
|
+
const exportOrdered = Boolean(this.config.AUTH0_EXPORT_ORDERED);
|
|
187
|
+
const raw = js_yaml_1.default.dump(cleaned, { sortKeys: exportOrdered });
|
|
187
188
|
logger_1.default.info(`Writing ${this.configFile}`);
|
|
188
189
|
fs_extra_1.default.writeFileSync(this.configFile, raw);
|
|
189
190
|
}
|
|
@@ -249,7 +249,7 @@ class ActionHandler extends default_1.default {
|
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
async calcChanges(assets) {
|
|
252
|
-
let { actions
|
|
252
|
+
let { actions } = assets;
|
|
253
253
|
// Do nothing if not set
|
|
254
254
|
if (!actions)
|
|
255
255
|
return {
|
|
@@ -259,19 +259,14 @@ class ActionHandler extends default_1.default {
|
|
|
259
259
|
conflicts: [],
|
|
260
260
|
};
|
|
261
261
|
let modules = null;
|
|
262
|
-
|
|
263
|
-
modules =
|
|
262
|
+
try {
|
|
263
|
+
modules = await (0, client_1.paginate)(this.client.actions.modules.list, {
|
|
264
|
+
paginate: true,
|
|
265
|
+
});
|
|
264
266
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
paginate: true,
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
catch {
|
|
272
|
-
logger_1.default.debug('Skipping actions modules enrichment because action modules could not be retrieved.');
|
|
273
|
-
modules = null;
|
|
274
|
-
}
|
|
267
|
+
catch {
|
|
268
|
+
logger_1.default.debug('Skipping actions modules enrichment because action modules could not be retrieved.');
|
|
269
|
+
modules = null;
|
|
275
270
|
}
|
|
276
271
|
if (modules != null) {
|
|
277
272
|
// Use task queue to process actions in parallel
|
|
@@ -305,12 +300,15 @@ class ActionHandler extends default_1.default {
|
|
|
305
300
|
moduleVersions = await moduleVersions.getNextPage();
|
|
306
301
|
allModuleVersions.push(...moduleVersions.data);
|
|
307
302
|
}
|
|
303
|
+
const moduleVersionId = allModuleVersions?.find((v) => v.version_number === module.module_version_number)?.id;
|
|
304
|
+
if (!moduleVersionId) {
|
|
305
|
+
throw new Error(`Could not find action module version id for module '${module.module_name}' version '${module.module_version_number}'`);
|
|
306
|
+
}
|
|
308
307
|
return {
|
|
309
308
|
module_name: module.module_name,
|
|
310
309
|
module_id: foundModule.id,
|
|
311
310
|
module_version_number: module.module_version_number,
|
|
312
|
-
module_version_id:
|
|
313
|
-
?.id || '',
|
|
311
|
+
module_version_id: moduleVersionId,
|
|
314
312
|
};
|
|
315
313
|
}
|
|
316
314
|
return module;
|
|
@@ -84,7 +84,9 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
84
84
|
type: 'clientGrants',
|
|
85
85
|
id: 'id',
|
|
86
86
|
// @ts-ignore because not sure why two-dimensional array passed in
|
|
87
|
-
|
|
87
|
+
// Try ['client_id', 'audience', 'subject_type'] first; falls through to
|
|
88
|
+
// ['client_id', 'audience'] when subject_type is null (falsy).
|
|
89
|
+
identifiers: ['id', ['client_id', 'audience', 'subject_type'], ['client_id', 'audience']],
|
|
88
90
|
stripUpdateFields: ['audience', 'client_id', 'subject_type', 'is_system'],
|
|
89
91
|
});
|
|
90
92
|
}
|
|
@@ -155,6 +157,31 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
155
157
|
...assets,
|
|
156
158
|
clientGrants: formatted,
|
|
157
159
|
});
|
|
160
|
+
// subject_type is immutable (in stripUpdateFields). Grants matched via the
|
|
161
|
+
// ['client_id', 'audience'] fallback with a mismatched subject_type must become
|
|
162
|
+
// DELETE + CREATE, not UPDATE, so the tenant converges to the desired state.
|
|
163
|
+
const subjectTypeMismatches = update.filter((localGrant) => {
|
|
164
|
+
// Only flag when local explicitly specifies subject_type (backward compat).
|
|
165
|
+
if (localGrant.subject_type === undefined)
|
|
166
|
+
return false;
|
|
167
|
+
const remoteGrant = (this.existing || []).find((e) => e.id === localGrant.id);
|
|
168
|
+
return (remoteGrant && (remoteGrant.subject_type ?? null) !== (localGrant.subject_type ?? null));
|
|
169
|
+
});
|
|
170
|
+
const adjustedUpdate = update.filter((u) => !subjectTypeMismatches.includes(u));
|
|
171
|
+
const adjustedDel = [
|
|
172
|
+
...del,
|
|
173
|
+
...subjectTypeMismatches
|
|
174
|
+
.map((u) => (this.existing || []).find((e) => e.id === u.id))
|
|
175
|
+
.filter((e) => e !== undefined),
|
|
176
|
+
];
|
|
177
|
+
const adjustedCreate = [
|
|
178
|
+
...create,
|
|
179
|
+
...subjectTypeMismatches
|
|
180
|
+
.map((u) => formatted.find((f) => f.client_id === u.client_id &&
|
|
181
|
+
f.audience === u.audience &&
|
|
182
|
+
(f.subject_type ?? null) === (u.subject_type ?? null)))
|
|
183
|
+
.filter((g) => g !== undefined),
|
|
184
|
+
];
|
|
158
185
|
const filterGrants = (list) => {
|
|
159
186
|
let filtered = list;
|
|
160
187
|
// Filter out the current client (Auth0 Management API client)
|
|
@@ -174,11 +201,11 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
174
201
|
};
|
|
175
202
|
const changes = {
|
|
176
203
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
177
|
-
del: filterGrants(
|
|
204
|
+
del: filterGrants(adjustedDel),
|
|
178
205
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
179
|
-
update: filterGrants(
|
|
206
|
+
update: filterGrants(adjustedUpdate),
|
|
180
207
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
181
|
-
create: filterGrants(
|
|
208
|
+
create: filterGrants(adjustedCreate),
|
|
182
209
|
// @ts-ignore because this expects `client_id` and that's not yet typed on Asset
|
|
183
210
|
conflicts: filterGrants(conflicts),
|
|
184
211
|
};
|
|
@@ -51,6 +51,14 @@ const utils_1 = require("../../utils");
|
|
|
51
51
|
const client_1 = require("../client");
|
|
52
52
|
const scimHandler_1 = __importDefault(require("./scimHandler"));
|
|
53
53
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
54
|
+
const connectionOptionsSchema = {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
dpop_signing_alg: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
54
62
|
exports.schema = {
|
|
55
63
|
type: 'array',
|
|
56
64
|
items: {
|
|
@@ -58,7 +66,7 @@ exports.schema = {
|
|
|
58
66
|
properties: {
|
|
59
67
|
name: { type: 'string' },
|
|
60
68
|
strategy: { type: 'string' },
|
|
61
|
-
options:
|
|
69
|
+
options: connectionOptionsSchema,
|
|
62
70
|
enabled_clients: { type: 'array', items: { type: 'string' } },
|
|
63
71
|
realms: { type: 'array', items: { type: 'string' } },
|
|
64
72
|
metadata: { type: 'object' },
|
|
@@ -50,6 +50,10 @@ export declare const schema: {
|
|
|
50
50
|
type: string[];
|
|
51
51
|
description: string;
|
|
52
52
|
};
|
|
53
|
+
is_default: {
|
|
54
|
+
type: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
53
57
|
};
|
|
54
58
|
required: string[];
|
|
55
59
|
};
|
|
@@ -60,6 +64,7 @@ export default class CustomDomainsHadnler extends DefaultAPIHandler {
|
|
|
60
64
|
constructor(config: DefaultAPIHandler);
|
|
61
65
|
objString(item: Asset): string;
|
|
62
66
|
getType(): Promise<Asset | null>;
|
|
67
|
+
validate(assets: Assets): Promise<void>;
|
|
63
68
|
processChanges(assets: Assets): Promise<void>;
|
|
64
69
|
}
|
|
65
70
|
export {};
|
|
@@ -44,6 +44,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.schema = void 0;
|
|
46
46
|
const default_1 = __importStar(require("./default"));
|
|
47
|
+
const validationError_1 = __importDefault(require("../../validationError"));
|
|
47
48
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
48
49
|
exports.schema = {
|
|
49
50
|
type: 'array',
|
|
@@ -81,6 +82,10 @@ exports.schema = {
|
|
|
81
82
|
type: ['string'],
|
|
82
83
|
description: 'Relying Party ID (rpId) to be used for Passkeys on this custom domain. If not provided or set to null, the full domain will be used.',
|
|
83
84
|
},
|
|
85
|
+
is_default: {
|
|
86
|
+
type: 'boolean',
|
|
87
|
+
description: 'Whether this custom domain is the default domain used for email notifications.',
|
|
88
|
+
},
|
|
84
89
|
},
|
|
85
90
|
required: ['domain', 'type'],
|
|
86
91
|
},
|
|
@@ -136,6 +141,16 @@ class CustomDomainsHadnler extends default_1.default {
|
|
|
136
141
|
throw err;
|
|
137
142
|
}
|
|
138
143
|
}
|
|
144
|
+
async validate(assets) {
|
|
145
|
+
await super.validate(assets);
|
|
146
|
+
const { customDomains } = assets;
|
|
147
|
+
if (!customDomains)
|
|
148
|
+
return;
|
|
149
|
+
const defaultDomains = customDomains.filter((d) => d.is_default === true);
|
|
150
|
+
if (defaultDomains.length > 1) {
|
|
151
|
+
throw new validationError_1.default(`Only one custom domain can be set as default (is_default: true), but found ${defaultDomains.length}: ${defaultDomains.map((d) => d.domain).join(', ')}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
139
154
|
async processChanges(assets) {
|
|
140
155
|
const { customDomains } = assets;
|
|
141
156
|
if (!customDomains)
|
|
@@ -149,6 +164,17 @@ class CustomDomainsHadnler extends default_1.default {
|
|
|
149
164
|
}
|
|
150
165
|
const changes = await this.calcChanges(assets);
|
|
151
166
|
await super.processChanges(assets, changes);
|
|
167
|
+
// If a domain is marked as is_default, set it as the tenant's default custom domain.
|
|
168
|
+
const defaultDomain = customDomains.find((d) => d.is_default === true);
|
|
169
|
+
if (defaultDomain) {
|
|
170
|
+
try {
|
|
171
|
+
await this.client.customDomains.setDefault({ domain: defaultDomain.domain });
|
|
172
|
+
logger_1.default.info(`Set default custom domain: ${defaultDomain.domain}`);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
throw new Error(`Problem setting default custom domain ${defaultDomain.domain}\n${err}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
152
178
|
}
|
|
153
179
|
}
|
|
154
180
|
exports.default = CustomDomainsHadnler;
|
|
@@ -161,7 +161,10 @@ const customPartialsPromptTypes = [
|
|
|
161
161
|
'signup',
|
|
162
162
|
'signup-id',
|
|
163
163
|
'signup-password',
|
|
164
|
+
'passkeys',
|
|
164
165
|
];
|
|
166
|
+
// Prompts that may not be available on all tenants (early access features)
|
|
167
|
+
const optionalPartialsPromptTypes = ['passkeys'];
|
|
165
168
|
const customPartialsScreenTypes = [
|
|
166
169
|
'login',
|
|
167
170
|
'login-id',
|
|
@@ -171,6 +174,8 @@ const customPartialsScreenTypes = [
|
|
|
171
174
|
'signup-password',
|
|
172
175
|
'login-passwordless-sms-otp',
|
|
173
176
|
'login-passwordless-email-code',
|
|
177
|
+
'passkeys-enrollment',
|
|
178
|
+
'passkeys-enrollment-local',
|
|
174
179
|
];
|
|
175
180
|
const customPartialsInsertionPoints = [
|
|
176
181
|
'form-content-start',
|
|
@@ -358,6 +363,19 @@ class PromptsHandler extends default_1.default {
|
|
|
358
363
|
this.IsFeatureSupported = false;
|
|
359
364
|
return null;
|
|
360
365
|
}
|
|
366
|
+
// Handle 400 errors for prompt types not available on all tenants (early access features)
|
|
367
|
+
// Error format: "Path validation error: 'Invalid value \"passkeys\"' on property prompt (Name of the prompt)."
|
|
368
|
+
if (error &&
|
|
369
|
+
error?.statusCode === 400 &&
|
|
370
|
+
error.message?.includes('Path validation error') &&
|
|
371
|
+
error.message?.includes('on property prompt')) {
|
|
372
|
+
// Check if the error message contains any of the optional prompt types
|
|
373
|
+
const unavailablePrompt = optionalPartialsPromptTypes.find((promptType) => error.message?.includes(promptType));
|
|
374
|
+
if (unavailablePrompt) {
|
|
375
|
+
logger_1.default.warn(`Skipping partials for prompt type '${unavailablePrompt}' because it is not available on this tenant.`);
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
361
379
|
if (error && error.statusCode === 429) {
|
|
362
380
|
logger_1.default.error(`The global rate limit has been exceeded, resulting in a ${error.statusCode} error. ${error.message}. Although this is an error, it is not blocking the pipeline.`);
|
|
363
381
|
return null;
|
|
@@ -198,6 +198,11 @@ class TenantHandler extends default_1.default {
|
|
|
198
198
|
delete updatedTenant.flags;
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
if (tenant.flags?.enable_custom_domain_in_emails !== undefined) {
|
|
202
|
+
logger_1.default.warn('The "enable_custom_domain_in_emails" tenant flag is deprecated. ' +
|
|
203
|
+
'Use the "is_default" field on customDomains to configure the default domain instead. ' +
|
|
204
|
+
'The flag will still be applied for now but will be removed in a future release.');
|
|
205
|
+
}
|
|
201
206
|
if (updatedTenant && Object.keys(updatedTenant).length > 0) {
|
|
202
207
|
await this.client.tenants.settings.update(updatedTenant);
|
|
203
208
|
this.updated += 1;
|
package/lib/types.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export type Config = {
|
|
|
71
71
|
AUTH0_RETRY_MAX_DELAY_MS?: number;
|
|
72
72
|
AUTH0_KEYWORD_REPLACE_MAPPINGS?: KeywordMappings;
|
|
73
73
|
AUTH0_EXPORT_IDENTIFIERS?: boolean;
|
|
74
|
+
AUTH0_EXPORT_ORDERED?: boolean;
|
|
74
75
|
AUTH0_CONNECTIONS_DIRECTORY?: string;
|
|
75
76
|
EXCLUDED_PROPS?: {
|
|
76
77
|
[key: string]: string[];
|
package/lib/utils.js
CHANGED
|
@@ -24,6 +24,7 @@ exports.mapClientID2NameSorted = mapClientID2NameSorted;
|
|
|
24
24
|
exports.nomalizedYAMLPath = nomalizedYAMLPath;
|
|
25
25
|
const path_1 = __importDefault(require("path"));
|
|
26
26
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
27
|
+
const nconf_1 = __importDefault(require("nconf"));
|
|
27
28
|
const sanitize_filename_1 = __importDefault(require("sanitize-filename"));
|
|
28
29
|
const dot_prop_1 = __importDefault(require("dot-prop"));
|
|
29
30
|
const lodash_1 = require("lodash");
|
|
@@ -69,10 +70,21 @@ function loadJSON(file, opts = {
|
|
|
69
70
|
throw new Error(`Error parsing JSON from metadata file: ${file}, because: ${e.message}`);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
73
|
+
function orderedKeysReplacer(_key, value) {
|
|
74
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
75
|
+
const obj = value;
|
|
76
|
+
return Object.fromEntries(Object.keys(obj)
|
|
77
|
+
.sort()
|
|
78
|
+
.map((k) => [k, obj[k]]));
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
72
82
|
function dumpJSON(file, mappings) {
|
|
73
83
|
try {
|
|
74
84
|
logger_1.default.info(`Writing ${file}`);
|
|
75
|
-
const
|
|
85
|
+
const exportOrdered = Boolean(nconf_1.default.get('AUTH0_EXPORT_ORDERED'));
|
|
86
|
+
const replacer = exportOrdered ? orderedKeysReplacer : undefined;
|
|
87
|
+
const jsonBody = JSON.stringify(mappings, replacer, 2);
|
|
76
88
|
fs_extra_1.default.writeFileSync(file, jsonBody.endsWith('\n') ? jsonBody : `${jsonBody}\n`);
|
|
77
89
|
}
|
|
78
90
|
catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auth0-deploy-cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.31.0",
|
|
4
4
|
"description": "A command line tool for deploying updates to your Auth0 tenant",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"lint:fix": "eslint --fix . && kacl lint",
|
|
11
11
|
"lint": "eslint . && kacl lint",
|
|
12
|
+
"format:check": "npx prettier --check .",
|
|
12
13
|
"format": "npx prettier --write .",
|
|
13
14
|
"test": "ts-mocha -p tsconfig.json --recursive 'test/**/*.test*' --exclude 'test/e2e/*' --timeout 20000",
|
|
14
|
-
"test:e2e:node-module": "ts-mocha -p tsconfig.json --recursive 'test/e2e/*.test*' --timeout
|
|
15
|
+
"test:e2e:node-module": "ts-mocha -p tsconfig.json --recursive 'test/e2e/*.test*' --timeout 150000",
|
|
15
16
|
"test:e2e:cli": "sh ./test/e2e/e2e-cli.sh",
|
|
16
17
|
"test:coverage": "nyc npm run test && nyc report --reporter=lcov",
|
|
17
18
|
"build": "rimraf ./lib && npx tsc",
|
|
@@ -33,15 +34,15 @@
|
|
|
33
34
|
"homepage": "https://github.com/auth0/auth0-deploy-cli#readme",
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"ajv": "^6.12.6",
|
|
36
|
-
"auth0": "^5.
|
|
37
|
+
"auth0": "^5.6.0",
|
|
37
38
|
"dot-prop": "^5.3.0",
|
|
38
39
|
"fs-extra": "^10.1.0",
|
|
39
40
|
"js-yaml": "^4.1.1",
|
|
40
|
-
"lodash": "^4.
|
|
41
|
+
"lodash": "^4.18.1",
|
|
41
42
|
"mkdirp": "^1.0.4",
|
|
42
43
|
"nconf": "^0.13.0",
|
|
43
44
|
"promise-pool-executor": "^1.1.1",
|
|
44
|
-
"sanitize-filename": "^1.6.
|
|
45
|
+
"sanitize-filename": "^1.6.4",
|
|
45
46
|
"undici": "^7.24.3",
|
|
46
47
|
"winston": "^3.19.0",
|
|
47
48
|
"yargs": "^15.4.1"
|
|
@@ -52,8 +53,8 @@
|
|
|
52
53
|
"@types/lodash": "^4.17.24",
|
|
53
54
|
"@types/mocha": "^10.0.10",
|
|
54
55
|
"@types/nconf": "^0.10.7",
|
|
55
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
56
|
-
"@typescript-eslint/parser": "^8.
|
|
56
|
+
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
|
57
|
+
"@typescript-eslint/parser": "^8.58.0",
|
|
57
58
|
"chai": "^4.5.0",
|
|
58
59
|
"chai-as-promised": "^7.1.2",
|
|
59
60
|
"eslint": "^9.39.2",
|