auth0-deploy-cli 8.35.0 → 8.36.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/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [8.36.0] - 2026-06-05
11
+
12
+ ### Added
13
+
14
+ - Add optional flag to skip secret masking during export. [#1396]
15
+
16
+ ### Fixed
17
+
18
+ - Remove stale connection files from directory export when connections are deleted. [#1389]
19
+
10
20
  ## [8.35.0] - 2026-05-22
11
21
 
12
22
  ### Added
@@ -1797,9 +1807,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1797
1807
  [#1385]: https://github.com/auth0/auth0-deploy-cli/issues/1385
1798
1808
  [#1386]: https://github.com/auth0/auth0-deploy-cli/issues/1386
1799
1809
  [#1387]: https://github.com/auth0/auth0-deploy-cli/issues/1387
1810
+ [#1389]: https://github.com/auth0/auth0-deploy-cli/issues/1389
1800
1811
  [#1391]: https://github.com/auth0/auth0-deploy-cli/issues/1391
1801
1812
  [#1392]: https://github.com/auth0/auth0-deploy-cli/issues/1392
1802
- [Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.35.0...HEAD
1813
+ [#1396]: https://github.com/auth0/auth0-deploy-cli/issues/1396
1814
+ [Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v8.36.0...HEAD
1815
+ [8.36.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.35.0...v8.36.0
1803
1816
  [8.35.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.34.0...v8.35.0
1804
1817
  [8.34.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.33.0...v8.34.0
1805
1818
  [8.33.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.32.0...v8.33.0
package/lib/args.d.ts CHANGED
@@ -21,6 +21,7 @@ type ExportSpecificParams = {
21
21
  output_folder: string;
22
22
  export_ids?: boolean;
23
23
  export_ordered?: boolean;
24
+ export_secrets?: boolean;
24
25
  };
25
26
  export type ExportParams = ExportSpecificParams & SharedParams;
26
27
  export type ImportParams = ImportSpecificParams & SharedParams;
package/lib/args.js CHANGED
@@ -103,6 +103,11 @@ function getParams() {
103
103
  describe: 'Order keys in exported JSON files for consistent diffs.',
104
104
  type: 'boolean',
105
105
  },
106
+ export_secrets: {
107
+ describe: 'Export actual secret values instead of placeholder markers.',
108
+ type: 'boolean',
109
+ default: false,
110
+ },
106
111
  })
107
112
  .example('$0 export -c config.json -f yaml -o path/to/export', 'Dump Auth0 config to folder in YAML format')
108
113
  .example('$0 export -c config.json -f directory -o path/to/export', 'Dump Auth0 config to folder in directory format')
@@ -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, export_ordered: exportOrdered, 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, export_secrets: exportSecrets, secret: clientSecret, env: shouldInheritEnv = false, experimental_ea: experimentalEA, } = params;
15
15
  if (shouldInheritEnv) {
16
16
  nconf_1.default.env().use('memory');
17
17
  }
@@ -36,6 +36,10 @@ async function exportCMD(params) {
36
36
  if (exportOrdered) {
37
37
  overrides.AUTH0_EXPORT_ORDERED = exportOrdered;
38
38
  }
39
+ // Allow passed in export_secrets to override the configured one
40
+ if (exportSecrets) {
41
+ overrides.AUTH0_EXPORT_SECRETS = exportSecrets;
42
+ }
39
43
  // Overrides AUTH0_INCLUDE_EXPERIMENTAL_EA is experimental_ea passed in command line
40
44
  if (experimentalEA) {
41
45
  overrides.AUTH0_EXPERIMENTAL_EA = experimentalEA;
@@ -1,7 +1,8 @@
1
1
  import { AttackProtection } from '../tools/auth0/handlers/attackProtection';
2
- export declare function emailProviderDefaults(emailProvider: any): any;
2
+ import { Config } from '../types';
3
+ export declare function emailProviderDefaults(emailProvider: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
3
4
  export declare function phoneProviderDefaults(phoneProvider: any): any;
4
5
  export declare function phoneTemplatesDefaults(phoneTemplate: any): any;
5
- export declare function connectionDefaults(connection: any): any;
6
- export declare function logStreamDefaults(logStreams: any): any;
7
- export declare function attackProtectionDefaults(attackProtection: AttackProtection): AttackProtection;
6
+ export declare function connectionDefaults(connection: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
7
+ export declare function logStreamDefaults(logStreams: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
8
+ export declare function attackProtectionDefaults(attackProtection: AttackProtection, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): AttackProtection;
@@ -7,9 +7,15 @@ exports.connectionDefaults = connectionDefaults;
7
7
  exports.logStreamDefaults = logStreamDefaults;
8
8
  exports.attackProtectionDefaults = attackProtectionDefaults;
9
9
  const utils_1 = require("../tools/utils");
10
+ // env vars arrive as strings, so check both forms
11
+ function isExportSecrets(config) {
12
+ return (config?.AUTH0_EXPORT_SECRETS === true || config?.AUTH0_EXPORT_SECRETS === 'true');
13
+ }
10
14
  // eslint-disable-next-line import/prefer-default-export
11
- function emailProviderDefaults(emailProvider) {
15
+ function emailProviderDefaults(emailProvider, config) {
12
16
  // eslint-disable-line
17
+ if (isExportSecrets(config))
18
+ return emailProvider;
13
19
  const updated = { ...emailProvider };
14
20
  const apiKeyProviders = ['mailgun', 'mandrill', 'sendgrid', 'sparkpost'];
15
21
  // Add placeholder for credentials as they cannot be exported
@@ -100,8 +106,8 @@ function phoneTemplatesDefaults(phoneTemplate) {
100
106
  });
101
107
  return updated;
102
108
  }
103
- function connectionDefaults(connection) {
104
- if (connection.options) {
109
+ function connectionDefaults(connection, config) {
110
+ if (!isExportSecrets(config) && connection.options) {
105
111
  // Mask secret for key: connection.options.client_secret
106
112
  (0, utils_1.maskSecretAtPath)({
107
113
  resourceTypeName: 'connections',
@@ -112,7 +118,9 @@ function connectionDefaults(connection) {
112
118
  }
113
119
  return connection;
114
120
  }
115
- function logStreamDefaults(logStreams) {
121
+ function logStreamDefaults(logStreams, config) {
122
+ if (isExportSecrets(config))
123
+ return logStreams;
116
124
  // masked sensitive fields
117
125
  const sensitiveKeys = [
118
126
  'httpAuthorization',
@@ -138,7 +146,9 @@ function logStreamDefaults(logStreams) {
138
146
  });
139
147
  return maskedLogStreams;
140
148
  }
141
- function attackProtectionDefaults(attackProtection) {
149
+ function attackProtectionDefaults(attackProtection, config) {
150
+ if (isExportSecrets(config))
151
+ return attackProtection;
142
152
  const { captcha } = attackProtection;
143
153
  if (captcha) {
144
154
  const providersWithSecrets = ['arkose', 'hcaptcha', 'friendly_captcha', 'recaptcha_v2'];
@@ -65,7 +65,7 @@ async function dump(context) {
65
65
  return;
66
66
  const files = attackProtectionFiles(context.filePath);
67
67
  fs_extra_1.default.ensureDirSync(files.directory);
68
- const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtection);
68
+ const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtection, context.config);
69
69
  if (maskedAttackProtection.botDetection) {
70
70
  (0, utils_1.dumpJSON)(files.botDetection, maskedAttackProtection.botDetection);
71
71
  }
@@ -51,6 +51,14 @@ async function dump(context) {
51
51
  }
52
52
  const connectionsFolder = path_1.default.join(context.filePath, tools_1.constants.CONNECTIONS_DIRECTORY);
53
53
  fs_extra_1.default.ensureDirSync(connectionsFolder);
54
+ // Track files that should remain after dump (written + excluded).
55
+ // Seed from exclude names directly so excluded connections' files are preserved
56
+ // regardless of whether the connection still exists on the tenant.
57
+ const expectedFiles = new Set();
58
+ for (const name of excludedConnections) {
59
+ expectedFiles.add(`${(0, utils_1.sanitize)(name)}.json`);
60
+ expectedFiles.add(`${(0, utils_1.sanitize)(name)}.html`);
61
+ }
54
62
  // Convert enabled_clients from id to name
55
63
  connections.forEach((connection) => {
56
64
  let dumpedConnection = {
@@ -62,7 +70,7 @@ async function dump(context) {
62
70
  };
63
71
  const connectionName = (0, utils_1.sanitize)(dumpedConnection.name);
64
72
  // Mask secrets
65
- dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection);
73
+ dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection, context.config);
66
74
  if (dumpedConnection.strategy === 'email') {
67
75
  (0, utils_1.ensureProp)(dumpedConnection, 'options.email.body');
68
76
  const html = dumpedConnection.options.email.body;
@@ -81,7 +89,19 @@ async function dump(context) {
81
89
  }
82
90
  const connectionFile = path_1.default.join(connectionsFolder, `${connectionName}.json`);
83
91
  (0, utils_1.dumpJSON)(connectionFile, dumpedConnection);
92
+ expectedFiles.add(`${connectionName}.json`);
93
+ if (dumpedConnection.strategy === 'email')
94
+ expectedFiles.add(`${connectionName}.html`);
84
95
  });
96
+ // Remove files that belong to connections no longer present (and not excluded)
97
+ if (fs_extra_1.default.existsSync(connectionsFolder)) {
98
+ for (const existing of fs_extra_1.default.readdirSync(connectionsFolder)) {
99
+ const fullPath = path_1.default.join(connectionsFolder, existing);
100
+ if (fs_extra_1.default.statSync(fullPath).isFile() && !expectedFiles.has(existing)) {
101
+ fs_extra_1.default.removeSync(fullPath);
102
+ }
103
+ }
104
+ }
85
105
  }
86
106
  const connectionsHandler = {
87
107
  parse,
@@ -31,7 +31,7 @@ async function dump(context) {
31
31
  const excludedDefaults = context.assets.exclude?.defaults || [];
32
32
  if (!excludedDefaults.includes('emailProvider')) {
33
33
  // Add placeholder for credentials as they cannot be exported
34
- return (0, defaults_1.emailProviderDefaults)(context.assets.emailProvider);
34
+ return (0, defaults_1.emailProviderDefaults)(context.assets.emailProvider, context.config);
35
35
  }
36
36
  return context.assets.emailProvider;
37
37
  })();
@@ -31,7 +31,7 @@ async function dump(context) {
31
31
  const logStreamsDirectory = path_1.default.join(context.filePath, tools_1.constants.LOG_STREAMS_DIRECTORY);
32
32
  fs_extra_1.default.ensureDirSync(logStreamsDirectory);
33
33
  // masked sensitive fields
34
- const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams);
34
+ const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams, context.config);
35
35
  maskedLogStreams.forEach((logStream) => {
36
36
  const ruleFile = path_1.default.join(logStreamsDirectory, `${(0, utils_1.sanitize)(logStream.name)}.json`);
37
37
  (0, utils_1.dumpJSON)(ruleFile, logStream);
@@ -126,6 +126,18 @@ class DirectoryContext {
126
126
  else {
127
127
  this.assets = auth0.assets;
128
128
  }
129
+ // Re-attach exclude/include config lost when assets was overwritten above
130
+ this.assets.exclude = {
131
+ rules: this.config.AUTH0_EXCLUDED_RULES || [],
132
+ clients: this.config.AUTH0_EXCLUDED_CLIENTS || [],
133
+ databases: this.config.AUTH0_EXCLUDED_DATABASES || [],
134
+ connections: this.config.AUTH0_EXCLUDED_CONNECTIONS || [],
135
+ resourceServers: this.config.AUTH0_EXCLUDED_RESOURCE_SERVERS || [],
136
+ defaults: this.config.AUTH0_EXCLUDED_DEFAULTS || [],
137
+ };
138
+ this.assets.include = {
139
+ connections: this.config.AUTH0_INCLUDED_CONNECTIONS || [],
140
+ };
129
141
  // Clean known read only fields
130
142
  this.assets = (0, readonly_1.default)(this.assets, this.config);
131
143
  // Copy clients to be used by handlers which require converting client_id to the name
@@ -25,7 +25,7 @@ async function dump(context) {
25
25
  if (captcha) {
26
26
  attackProtectionConfig.captcha = captcha;
27
27
  }
28
- const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtectionConfig);
28
+ const maskedAttackProtection = (0, defaults_1.attackProtectionDefaults)(attackProtectionConfig, context.config);
29
29
  return {
30
30
  attackProtection: maskedAttackProtection,
31
31
  };
@@ -53,7 +53,7 @@ async function dump(context) {
53
53
  }),
54
54
  };
55
55
  // Mask secrets
56
- dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection);
56
+ dumpedConnection = (0, defaults_1.connectionDefaults)(dumpedConnection, context.config);
57
57
  if (dumpedConnection.strategy === 'email') {
58
58
  (0, utils_1.ensureProp)(connection, 'options.email.body');
59
59
  const connectionsFolder = path_1.default.join(context.basePath, tools_1.constants.CONNECTIONS_DIRECTORY);
@@ -17,7 +17,7 @@ async function dump(context) {
17
17
  const excludedDefaults = context.assets.exclude?.defaults || [];
18
18
  if (emailProvider && !excludedDefaults.includes('emailProvider')) {
19
19
  // Add placeholder for credentials as they cannot be exported
20
- return (0, defaults_1.emailProviderDefaults)(emailProvider);
20
+ return (0, defaults_1.emailProviderDefaults)(emailProvider, context.config);
21
21
  }
22
22
  return emailProvider;
23
23
  })();
@@ -14,7 +14,7 @@ async function dump(context) {
14
14
  if (!logStreams)
15
15
  return { logStreams: null };
16
16
  // masked sensitive fields
17
- const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams);
17
+ const maskedLogStreams = (0, defaults_1.logStreamDefaults)(logStreams, context.config);
18
18
  return {
19
19
  logStreams: maskedLogStreams,
20
20
  };
package/lib/types.d.ts CHANGED
@@ -72,6 +72,7 @@ export type Config = {
72
72
  AUTH0_KEYWORD_REPLACE_MAPPINGS?: KeywordMappings;
73
73
  AUTH0_EXPORT_IDENTIFIERS?: boolean;
74
74
  AUTH0_EXPORT_ORDERED?: boolean;
75
+ AUTH0_EXPORT_SECRETS?: boolean;
75
76
  AUTH0_CONNECTIONS_DIRECTORY?: string;
76
77
  AUTH0_DRY_RUN?: boolean | 'preview';
77
78
  AUTH0_DRY_RUN_INTERACTIVE?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auth0-deploy-cli",
3
- "version": "8.35.0",
3
+ "version": "8.36.0",
4
4
  "description": "A command line tool for deploying updates to your Auth0 tenant",
5
5
  "main": "lib/index.js",
6
6
  "bin": {