auth0-deploy-cli 8.43.0 → 8.45.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/README.md +1 -1
  3. package/lib/context/defaults.d.ts +7 -0
  4. package/lib/context/defaults.js +74 -30
  5. package/lib/context/directory/handlers/attackProtection.js +1 -1
  6. package/lib/context/directory/handlers/branding.js +9 -1
  7. package/lib/context/directory/handlers/clients.js +9 -3
  8. package/lib/context/directory/handlers/connections.js +10 -4
  9. package/lib/context/directory/handlers/emailTemplates.js +9 -0
  10. package/lib/context/directory/handlers/index.js +2 -0
  11. package/lib/context/directory/handlers/networkACLKeys.d.ts +6 -0
  12. package/lib/context/directory/handlers/networkACLKeys.js +54 -0
  13. package/lib/context/directory/handlers/prompts.js +11 -3
  14. package/lib/context/yaml/handlers/attackProtection.js +1 -1
  15. package/lib/context/yaml/handlers/branding.js +8 -1
  16. package/lib/context/yaml/handlers/clients.js +4 -1
  17. package/lib/context/yaml/handlers/connections.js +3 -1
  18. package/lib/context/yaml/handlers/flows.js +7 -0
  19. package/lib/context/yaml/handlers/forms.js +7 -0
  20. package/lib/context/yaml/handlers/index.js +2 -0
  21. package/lib/context/yaml/handlers/networkACLKeys.d.ts +6 -0
  22. package/lib/context/yaml/handlers/networkACLKeys.js +42 -0
  23. package/lib/context/yaml/handlers/prompts.js +7 -1
  24. package/lib/keywordPreservation.d.ts +1 -1
  25. package/lib/keywordPreservation.js +2 -1
  26. package/lib/readonly.js +5 -0
  27. package/lib/tools/auth0/handlers/attackProtection.js +3 -1
  28. package/lib/tools/auth0/handlers/clients.d.ts +40 -0
  29. package/lib/tools/auth0/handlers/clients.js +58 -1
  30. package/lib/tools/auth0/handlers/connectionProfiles.d.ts +27 -0
  31. package/lib/tools/auth0/handlers/connectionProfiles.js +30 -0
  32. package/lib/tools/auth0/handlers/connections.js +10 -3
  33. package/lib/tools/auth0/handlers/databases.d.ts +28 -0
  34. package/lib/tools/auth0/handlers/databases.js +47 -22
  35. package/lib/tools/auth0/handlers/default.d.ts +23 -0
  36. package/lib/tools/auth0/handlers/default.js +21 -10
  37. package/lib/tools/auth0/handlers/index.js +2 -0
  38. package/lib/tools/auth0/handlers/networkACLKeys.d.ts +45 -0
  39. package/lib/tools/auth0/handlers/networkACLKeys.js +177 -0
  40. package/lib/tools/auth0/handlers/networkACLs.d.ts +114 -2
  41. package/lib/tools/auth0/handlers/networkACLs.js +91 -16
  42. package/lib/tools/auth0/handlers/organizations.d.ts +1 -1
  43. package/lib/tools/auth0/handlers/organizations.js +49 -20
  44. package/lib/tools/auth0/handlers/phoneProvider.js +10 -2
  45. package/lib/tools/calculateDryRunChanges.js +12 -8
  46. package/lib/tools/constants.d.ts +1 -0
  47. package/lib/tools/constants.js +1 -0
  48. package/lib/types.d.ts +3 -1
  49. package/lib/utils.js +5 -2
  50. package/package.json +2 -2
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __importDefault = (this && this.__importDefault) || function (mod) {
42
+ return (mod && mod.__esModule) ? mod : { "default": mod };
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.schema = void 0;
46
+ const default_1 = __importStar(require("./default"));
47
+ const logger_1 = __importDefault(require("../../../logger"));
48
+ exports.schema = {
49
+ type: 'array',
50
+ description: 'List of Network ACL keys for HMAC signature verification',
51
+ items: {
52
+ type: 'object',
53
+ required: ['name', 'alg'],
54
+ properties: {
55
+ id: { type: 'string' },
56
+ name: { type: 'string', maxLength: 255 },
57
+ alg: { type: 'string', enum: ['hmac-sha256'] },
58
+ // value is write-only and never returned by the API.
59
+ // Provide it at deploy time via keyword replacement (e.g. ##HMAC_KEY_VALUE##).
60
+ // It is not exported and is omitted from exported configs.
61
+ value: { type: 'string' },
62
+ fingerprint: { type: 'string' },
63
+ },
64
+ additionalProperties: false,
65
+ },
66
+ };
67
+ class NetworkACLKeysHandler extends default_1.default {
68
+ constructor(config) {
69
+ super({
70
+ ...config,
71
+ type: 'networkACLKeys',
72
+ id: 'id',
73
+ identifiers: ['name'],
74
+ // value is write-only (never returned by API) and fingerprint/timestamps are
75
+ // API-generated — ignore all of them when computing dry-run diffs so keys
76
+ // with a local `value` don't show phantom UPDATEs.
77
+ ignoreDryRunFields: ['value', 'fingerprint', 'created_at', 'updated_at'],
78
+ });
79
+ }
80
+ objString(key) {
81
+ return super.objString({ name: key.name, alg: key.alg });
82
+ }
83
+ async getType() {
84
+ if (this.existing)
85
+ return this.existing;
86
+ try {
87
+ // Not paginated — tenant cap is 10 keys. Returns { keys: [...] }.
88
+ const result = await this.client.keys.networkAcls.list();
89
+ this.existing = result.keys;
90
+ return this.existing;
91
+ }
92
+ catch (err) {
93
+ if (err.statusCode === 404 || err.statusCode === 501) {
94
+ return null;
95
+ }
96
+ if (err.statusCode === 403) {
97
+ logger_1.default.debug('Network ACL key management is not enabled for this tenant. Please verify `scope` or contact Auth0 support.');
98
+ return null;
99
+ }
100
+ throw err;
101
+ }
102
+ }
103
+ async processChanges(assets) {
104
+ const { networkACLKeys } = assets;
105
+ // Do nothing if not set
106
+ if (!networkACLKeys)
107
+ return;
108
+ const { del, create } = await this.calcChanges(assets);
109
+ // Keys are immutable after creation — there is no update operation.
110
+ // A detected "update" (same name, different config) is a no-op: the existing key is kept as-is.
111
+ logger_1.default.debug(`Start processChanges for networkACLKeys [delete:${del.length}] [create:${create.length}]`);
112
+ await Promise.all([this.deleteNetworkACLKeys(del), this.createNetworkACLKeys(create)]);
113
+ }
114
+ async createNetworkACLKey(key) {
115
+ if (!key.value) {
116
+ logger_1.default.warn(`Skipping creation of Network ACL key "${key.name}": ` +
117
+ `'value' is required to create a key but is not present in the config. ` +
118
+ `Supply the key material via keyword replacement (e.g. ##HMAC_KEY_VALUE##) or create the key out-of-band.`);
119
+ return;
120
+ }
121
+ await this.client.keys.networkAcls.create({
122
+ name: key.name,
123
+ alg: key.alg,
124
+ value: key.value,
125
+ });
126
+ this.didCreate(key);
127
+ this.created += 1;
128
+ }
129
+ async createNetworkACLKeys(creates) {
130
+ await this.client.pool
131
+ .addEachTask({
132
+ data: creates || [],
133
+ generator: (item) => this.createNetworkACLKey(item).catch((err) => {
134
+ throw new Error(`Problem creating ${this.type} ${this.objString(item)}\n${err}`);
135
+ }),
136
+ })
137
+ .promise();
138
+ }
139
+ async deleteNetworkACLKey(key) {
140
+ if (!key.id) {
141
+ throw new Error(`Missing id for ${this.type} ${this.objString(key)}`);
142
+ }
143
+ await this.client.keys.networkAcls.delete(key.id);
144
+ }
145
+ async deleteNetworkACLKeys(data) {
146
+ if (!data || data.length === 0)
147
+ return;
148
+ if (this.config('AUTH0_ALLOW_DELETE') === 'true' ||
149
+ this.config('AUTH0_ALLOW_DELETE') === true) {
150
+ await this.client.pool
151
+ .addEachTask({
152
+ data: data || [],
153
+ generator: (item) => this.deleteNetworkACLKey(item)
154
+ .then(() => {
155
+ this.didDelete(item);
156
+ this.deleted += 1;
157
+ })
158
+ .catch((err) => {
159
+ if (err.statusCode === 409) {
160
+ throw new Error(`Cannot delete Network ACL key "${item.name}" — it is still referenced by one or more ACL rules. ` +
161
+ `Remove the key reference from all ACL rules first.\n${err}`);
162
+ }
163
+ throw new Error(`Problem deleting ${this.type} ${this.objString(item)}\n${err}`);
164
+ }),
165
+ })
166
+ .promise();
167
+ }
168
+ else {
169
+ logger_1.default.warn(`Detected the following ${this.type} should be deleted. Doing so may be destructive.\nYou can enable deletes by setting 'AUTH0_ALLOW_DELETE' to true in the config
170
+ \n${data.map((i) => this.objString(i)).join('\n')}`);
171
+ }
172
+ }
173
+ }
174
+ exports.default = NetworkACLKeysHandler;
175
+ __decorate([
176
+ (0, default_1.order)(60)
177
+ ], NetworkACLKeysHandler.prototype, "processChanges", null);
@@ -21,7 +21,7 @@ export declare const schema: {
21
21
  minimum: number;
22
22
  };
23
23
  rule: {
24
- anyOf: {
24
+ anyOf: ({
25
25
  type: string;
26
26
  required: string[];
27
27
  properties: {
@@ -171,6 +171,29 @@ export declare const schema: {
171
171
  };
172
172
  uniqueItems: boolean;
173
173
  };
174
+ http_message_signature: {
175
+ type: string;
176
+ properties: {
177
+ keys: {
178
+ type: string;
179
+ minItems: number;
180
+ maxItems: number;
181
+ uniqueItems: boolean;
182
+ items: {
183
+ type: string;
184
+ properties: {
185
+ id: {
186
+ type: string;
187
+ };
188
+ };
189
+ required: string[];
190
+ additionalProperties: boolean;
191
+ };
192
+ };
193
+ };
194
+ required: string[];
195
+ additionalProperties: boolean;
196
+ };
174
197
  };
175
198
  additionalProperties: boolean;
176
199
  };
@@ -271,6 +294,29 @@ export declare const schema: {
271
294
  };
272
295
  uniqueItems: boolean;
273
296
  };
297
+ http_message_signature: {
298
+ type: string;
299
+ properties: {
300
+ keys: {
301
+ type: string;
302
+ minItems: number;
303
+ maxItems: number;
304
+ uniqueItems: boolean;
305
+ items: {
306
+ type: string;
307
+ properties: {
308
+ id: {
309
+ type: string;
310
+ };
311
+ };
312
+ required: string[];
313
+ additionalProperties: boolean;
314
+ };
315
+ };
316
+ };
317
+ required: string[];
318
+ additionalProperties: boolean;
319
+ };
274
320
  };
275
321
  additionalProperties: boolean;
276
322
  };
@@ -278,9 +324,75 @@ export declare const schema: {
278
324
  enum: string[];
279
325
  type: string;
280
326
  };
327
+ match_all?: undefined;
328
+ };
329
+ additionalProperties: boolean;
330
+ } | {
331
+ type: string;
332
+ required: string[];
333
+ properties: {
334
+ action: {
335
+ type: string;
336
+ anyOf: ({
337
+ type: string;
338
+ required: string[];
339
+ properties: {
340
+ block: {
341
+ type: string;
342
+ enum: boolean[];
343
+ };
344
+ };
345
+ additionalProperties: boolean;
346
+ } | {
347
+ type: string;
348
+ required: string[];
349
+ properties: {
350
+ allow: {
351
+ type: string;
352
+ enum: boolean[];
353
+ };
354
+ };
355
+ additionalProperties: boolean;
356
+ } | {
357
+ type: string;
358
+ required: string[];
359
+ properties: {
360
+ log: {
361
+ type: string;
362
+ enum: boolean[];
363
+ };
364
+ };
365
+ additionalProperties: boolean;
366
+ } | {
367
+ type: string;
368
+ required: string[];
369
+ properties: {
370
+ redirect: {
371
+ type: string;
372
+ enum: boolean[];
373
+ };
374
+ redirect_uri: {
375
+ type: string;
376
+ minLength: number;
377
+ maxLength: number;
378
+ };
379
+ };
380
+ additionalProperties: boolean;
381
+ })[];
382
+ };
383
+ match_all: {
384
+ type: string;
385
+ enum: boolean[];
386
+ };
387
+ scope: {
388
+ enum: string[];
389
+ type: string;
390
+ };
391
+ match?: undefined;
392
+ not_match?: undefined;
281
393
  };
282
394
  additionalProperties: boolean;
283
- }[];
395
+ })[];
284
396
  };
285
397
  };
286
398
  additionalProperties: boolean;
@@ -1,10 +1,49 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
2
41
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
42
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
43
  };
5
44
  Object.defineProperty(exports, "__esModule", { value: true });
6
45
  exports.schema = void 0;
7
- const default_1 = __importDefault(require("./default"));
46
+ const default_1 = __importStar(require("./default"));
8
47
  const client_1 = require("../client");
9
48
  const logger_1 = __importDefault(require("../../../logger"));
10
49
  // Define action types
@@ -57,6 +96,15 @@ const RedirectAction = {
57
96
  },
58
97
  additionalProperties: false,
59
98
  };
99
+ // Shared sub-schemas reused across every rule branch below.
100
+ const ActionSchema = {
101
+ type: 'object',
102
+ anyOf: [BlockAction, AllowAction, LogAction, RedirectAction],
103
+ };
104
+ const ScopeSchema = {
105
+ enum: ['management', 'authentication', 'tenant'],
106
+ type: 'string',
107
+ };
60
108
  // Define MatchSchema
61
109
  const MatchSchema = {
62
110
  type: 'object',
@@ -155,6 +203,27 @@ const MatchSchema = {
155
203
  },
156
204
  uniqueItems: true,
157
205
  },
206
+ http_message_signature: {
207
+ type: 'object',
208
+ properties: {
209
+ keys: {
210
+ type: 'array',
211
+ minItems: 1,
212
+ maxItems: 10,
213
+ uniqueItems: true,
214
+ items: {
215
+ type: 'object',
216
+ properties: {
217
+ id: { type: 'string' },
218
+ },
219
+ required: ['id'],
220
+ additionalProperties: false,
221
+ },
222
+ },
223
+ },
224
+ required: ['keys'],
225
+ additionalProperties: false,
226
+ },
158
227
  },
159
228
  additionalProperties: false,
160
229
  };
@@ -182,16 +251,10 @@ exports.schema = {
182
251
  type: 'object',
183
252
  required: ['action', 'scope', 'match'],
184
253
  properties: {
185
- action: {
186
- type: 'object',
187
- anyOf: [BlockAction, AllowAction, LogAction, RedirectAction],
188
- },
254
+ action: ActionSchema,
189
255
  match: MatchSchema,
190
256
  not_match: MatchSchema,
191
- scope: {
192
- enum: ['management', 'authentication', 'tenant'],
193
- type: 'string',
194
- },
257
+ scope: ScopeSchema,
195
258
  },
196
259
  additionalProperties: false,
197
260
  },
@@ -199,16 +262,25 @@ exports.schema = {
199
262
  type: 'object',
200
263
  required: ['action', 'scope', 'not_match'],
201
264
  properties: {
202
- action: {
203
- type: 'object',
204
- anyOf: [BlockAction, AllowAction, LogAction, RedirectAction],
205
- },
265
+ action: ActionSchema,
206
266
  not_match: MatchSchema,
207
267
  match: MatchSchema,
208
- scope: {
209
- enum: ['management', 'authentication', 'tenant'],
210
- type: 'string',
268
+ scope: ScopeSchema,
269
+ },
270
+ additionalProperties: false,
271
+ },
272
+ {
273
+ // Unconditional match: `match_all` is mutually exclusive with `match`/`not_match`.
274
+ type: 'object',
275
+ required: ['action', 'scope', 'match_all'],
276
+ properties: {
277
+ action: ActionSchema,
278
+ match_all: {
279
+ // Schema only permits `true`; omit the property instead of sending `false`.
280
+ type: 'boolean',
281
+ enum: [true],
211
282
  },
283
+ scope: ScopeSchema,
212
284
  },
213
285
  additionalProperties: false,
214
286
  },
@@ -355,3 +427,6 @@ class NetworkACLsHandler extends default_1.default {
355
427
  }
356
428
  }
357
429
  exports.default = NetworkACLsHandler;
430
+ __decorate([
431
+ (0, default_1.order)(65)
432
+ ], NetworkACLsHandler.prototype, "processChanges", null);
@@ -147,7 +147,7 @@ export default class OrganizationsHandler extends DefaultHandler {
147
147
  createOrganizations(creates: CalculatedChanges['create']): Promise<void>;
148
148
  updateOrganization(org: any, organizations: any): Promise<{
149
149
  id: any;
150
- }>;
150
+ } | null>;
151
151
  getClientGrantIDByClientName(clientsName: string): string;
152
152
  getClientIdByClientName(clientName: string): string;
153
153
  getFormattedClientGrants(): Promise<FormattedClientGrants[]>;
@@ -51,6 +51,12 @@ const logger_1 = __importDefault(require("../../../logger"));
51
51
  const client_1 = require("../client");
52
52
  const utils_1 = require("../../../utils");
53
53
  const utils_2 = require("../../utils");
54
+ // Org sub-resources are skipped when the tenant can't read them: the EA feature is off
55
+ // (feature_not_enabled, returned as 400 or 403) or the token lacks scope (403). The SDK
56
+ // puts the API code on `err.body.errorCode`, not `err.errorCode`.
57
+ function isOrgSubresourceUnavailable(err) {
58
+ return err?.statusCode === 403 || err?.body?.errorCode === 'feature_not_enabled';
59
+ }
54
60
  exports.schema = {
55
61
  type: 'array',
56
62
  items: {
@@ -194,8 +200,9 @@ class OrganizationsHandler extends default_1.default {
194
200
  throw new Error(`Organization "${organization.name}" was created but the response did not include an ID. Skipping connection/grant association.`);
195
201
  }
196
202
  const createdId = created.id;
203
+ const retryConfig = this.getRetryConfig();
197
204
  if (typeof org.connections !== 'undefined' && org.connections.length > 0) {
198
- await Promise.all(org.connections.map((conn) => this.client.organizations.connections.create(createdId, conn)));
205
+ await Promise.all(org.connections.map((conn) => (0, default_1.retryWithExponentialBackoff)(() => this.client.organizations.connections.create(createdId, conn), retryConfig)));
199
206
  }
200
207
  if (typeof org.client_grants !== 'undefined' && org.client_grants.length > 0) {
201
208
  await Promise.all(org.client_grants.map((organizationClientGrants) => this.createOrganizationClientGrants(createdId, this.getClientGrantIDByClientName(organizationClientGrants.client_id))));
@@ -240,7 +247,8 @@ class OrganizationsHandler extends default_1.default {
240
247
  .promise();
241
248
  }
242
249
  async updateOrganization(org, organizations) {
243
- const { connections: existingConnections, client_grants: existingClientGrants, discovery_domains: existingDiscoveryDomains, clients: existingOrgClients = [], } = await organizations.find((orgToUpdate) => orgToUpdate.name === org.name);
250
+ const existingOrg = organizations.find((orgToUpdate) => orgToUpdate.name === org.name);
251
+ const { connections: existingConnections, client_grants: existingClientGrants, discovery_domains: existingDiscoveryDomains, clients: existingOrgClients = [], } = existingOrg;
244
252
  const params = { id: org.id };
245
253
  const { connections, client_grants: organizationClientGrants, discovery_domains: organizationDiscoveryDomains, clients: organizationClients, } = org;
246
254
  delete org.connections;
@@ -249,7 +257,14 @@ class OrganizationsHandler extends default_1.default {
249
257
  delete org.client_grants;
250
258
  delete org.discovery_domains;
251
259
  delete org.clients;
252
- await this.client.organizations.update(params.id, org);
260
+ // Only PATCH if top-level properties actually differ from the existing state.
261
+ // Compare only the keys present in the desired config against those same keys
262
+ // on the remote org, so that fields the user didn't specify are not considered.
263
+ let changed = false;
264
+ if (!(0, lodash_1.isEqual)(org, (0, lodash_1.pick)(existingOrg, Object.keys(org)))) {
265
+ await this.client.organizations.update(params.id, org);
266
+ changed = true;
267
+ }
253
268
  // organization connections
254
269
  const connectionsToRemove = existingConnections.filter((c) => !connections.find((x) => x.connection_id === c.connection_id));
255
270
  const connectionsToAdd = connections.filter((c) => !existingConnections.find((x) => x.connection_id === c.connection_id));
@@ -260,27 +275,27 @@ class OrganizationsHandler extends default_1.default {
260
275
  x.organization_access_level !== c.organization_access_level ||
261
276
  x.organization_connection_name !== c.organization_connection_name ||
262
277
  x.is_enabled !== (c.is_enabled ?? true))));
278
+ if (connectionsToUpdate.length > 0 ||
279
+ connectionsToAdd.length > 0 ||
280
+ connectionsToRemove.length > 0) {
281
+ changed = true;
282
+ }
283
+ const retryConfig = this.getRetryConfig();
263
284
  // Handle updates first
264
- await Promise.all(connectionsToUpdate.map((conn) => this.client.organizations.connections
265
- .update(params.id, conn.connection_id, {
285
+ await Promise.all(connectionsToUpdate.map((conn) => (0, default_1.retryWithExponentialBackoff)(() => this.client.organizations.connections.update(params.id, conn.connection_id, {
266
286
  organization_connection_name: conn.organization_connection_name,
267
287
  assign_membership_on_login: conn.assign_membership_on_login,
268
288
  show_as_button: conn.show_as_button,
269
289
  is_signup_enabled: conn.is_signup_enabled,
270
290
  is_enabled: conn.is_enabled,
271
291
  organization_access_level: conn.organization_access_level,
272
- })
273
- .catch(() => {
292
+ }), retryConfig).catch(() => {
274
293
  throw new Error(`Problem updating Enabled Connection ${conn.connection_id} for organizations ${params.id}`);
275
294
  })));
276
- await Promise.all(connectionsToAdd.map((conn) => this.client.organizations.connections
277
- .create(params.id, (0, lodash_1.omit)(conn, 'connection'))
278
- .catch(() => {
295
+ await Promise.all(connectionsToAdd.map((conn) => (0, default_1.retryWithExponentialBackoff)(() => this.client.organizations.connections.create(params.id, (0, lodash_1.omit)(conn, 'connection')), retryConfig).catch(() => {
279
296
  throw new Error(`Problem adding Enabled Connection ${conn.connection_id} for organizations ${params.id}`);
280
297
  })));
281
- await Promise.all(connectionsToRemove.map((conn) => this.client.organizations.connections
282
- .delete(params.id, conn.connection_id)
283
- .catch(() => {
298
+ await Promise.all(connectionsToRemove.map((conn) => (0, default_1.retryWithExponentialBackoff)(() => this.client.organizations.connections.delete(params.id, conn.connection_id), retryConfig).catch(() => {
284
299
  throw new Error(`Problem removing Enabled Connection ${conn.connection_id} for organizations ${params.id}`);
285
300
  })));
286
301
  // organization client_grants
@@ -294,6 +309,9 @@ class OrganizationsHandler extends default_1.default {
294
309
  ?.map((clientGrant) => ({
295
310
  grant_id: this.getClientGrantIDByClientName(clientGrant.client_id),
296
311
  })) || [];
312
+ if (orgClientGrantsToAdd.length > 0 || orgClientGrantsToRemove.length > 0) {
313
+ changed = true;
314
+ }
297
315
  // Handle updates first
298
316
  await Promise.all(orgClientGrantsToAdd.map((orgClientGrant) => this.createOrganizationClientGrants(params.id, orgClientGrant.grant_id).catch(() => {
299
317
  throw new Error(`Problem adding organization clientGrant ${orgClientGrant.grant_id} for organizations ${params.id}`);
@@ -315,6 +333,9 @@ class OrganizationsHandler extends default_1.default {
315
333
  };
316
334
  })
317
335
  .filter(Boolean) || [];
336
+ if (orgDiscoveryDomainsToUpdate.length > 0 || orgDiscoveryDomainsToAdd.length > 0) {
337
+ changed = true;
338
+ }
318
339
  for (const { id, domain, ...updateParams } of orgDiscoveryDomainsToUpdate) {
319
340
  try {
320
341
  await this.updateOrganizationDiscoveryDomain(params.id, id, domain, updateParams);
@@ -338,6 +359,7 @@ class OrganizationsHandler extends default_1.default {
338
359
  if (orgDiscoveryDomainsToRemove.length > 0) {
339
360
  if (this.config('AUTH0_ALLOW_DELETE') === 'true' ||
340
361
  this.config('AUTH0_ALLOW_DELETE') === true) {
362
+ changed = true;
341
363
  for (const domain of orgDiscoveryDomainsToRemove) {
342
364
  try {
343
365
  await this.deleteOrganizationDiscoveryDomain(params.id, domain.domain, domain.id);
@@ -375,6 +397,7 @@ class OrganizationsHandler extends default_1.default {
375
397
  }))
376
398
  .filter((oc) => !!oc.client_id);
377
399
  if (orgClientsToAdd.length > 0) {
400
+ changed = true;
378
401
  await this.createOrganizationClients(params.id, orgClientsToAdd).catch((err) => {
379
402
  throw new Error(`Problem adding org clients for organization ${params.id}\n${err}`);
380
403
  });
@@ -382,6 +405,7 @@ class OrganizationsHandler extends default_1.default {
382
405
  if (orgClientsToRemove.length > 0) {
383
406
  if (this.config('AUTH0_ALLOW_DELETE') === 'true' ||
384
407
  this.config('AUTH0_ALLOW_DELETE') === true) {
408
+ changed = true;
385
409
  await this.deleteOrganizationClients(params.id, orgClientsToRemove).catch((err) => {
386
410
  throw new Error(`Problem removing org clients for organization ${params.id}\n${err}`);
387
411
  });
@@ -390,6 +414,9 @@ class OrganizationsHandler extends default_1.default {
390
414
  logger_1.default.warn(`Detected the following organization client associations should be removed. Doing so may be destructive.\nYou can enable deletes by setting 'AUTH0_ALLOW_DELETE' to true in the config\n${orgClientsToRemove.join('\n')}`);
391
415
  }
392
416
  }
417
+ if (orgClientsToUpdate.length > 0) {
418
+ changed = true;
419
+ }
393
420
  await Promise.all(orgClientsToUpdate.map((oc) => this.client.organizations.clients
394
421
  .update(params.id, oc.client_id, {
395
422
  use_for_member_access: oc.use_for_member_access,
@@ -397,7 +424,7 @@ class OrganizationsHandler extends default_1.default {
397
424
  .catch((err) => {
398
425
  throw new Error(`Problem updating org client ${oc.client_id} for organization ${params.id}\n${err}`);
399
426
  })));
400
- return params;
427
+ return changed ? params : null;
401
428
  }
402
429
  getClientGrantIDByClientName(clientsName) {
403
430
  const found = this.formattedClientGrants.find((c) => c.client_id === clientsName);
@@ -435,8 +462,10 @@ class OrganizationsHandler extends default_1.default {
435
462
  data: updates || [],
436
463
  generator: (item) => this.updateOrganization(item, orgs)
437
464
  .then((data) => {
438
- this.didUpdate(data);
439
- this.updated += 1;
465
+ if (data) {
466
+ this.didUpdate(data);
467
+ this.updated += 1;
468
+ }
440
469
  })
441
470
  .catch((err) => {
442
471
  throw new Error(`Problem updating ${this.type} ${this.objString(item)}\n${err}`);
@@ -595,8 +624,8 @@ class OrganizationsHandler extends default_1.default {
595
624
  if (err.statusCode === 404 || err.statusCode === 501) {
596
625
  return null;
597
626
  }
598
- if (err.statusCode === 403 || err.errorCode === 'feature_not_enabled') {
599
- logger_1.default.debug('Organization Discovery domains are not enabled for this tenant. Please verify `scope` or contact Auth0 support to enable this feature.');
627
+ if (isOrgSubresourceUnavailable(err)) {
628
+ logger_1.default.debug(`Skipping organization discovery domains (${err?.body?.errorCode ?? err.statusCode}).`);
600
629
  return null;
601
630
  }
602
631
  throw err;
@@ -641,8 +670,8 @@ class OrganizationsHandler extends default_1.default {
641
670
  if (err.statusCode === 404 || err.statusCode === 501) {
642
671
  return null;
643
672
  }
644
- if (err.statusCode === 403 || err.errorCode === 'feature_not_enabled') {
645
- logger_1.default.debug('Org-to-app entitlement is not enabled for this tenant. Skipping org-client associations.');
673
+ if (isOrgSubresourceUnavailable(err)) {
674
+ logger_1.default.debug(`Skipping org-client associations (${err?.body?.errorCode ?? err.statusCode}).`);
646
675
  return null;
647
676
  }
648
677
  throw err;