auth0-deploy-cli 9.0.0-beta.2 → 9.0.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 (126) hide show
  1. package/AGENTS.md +2 -357
  2. package/CHANGELOG.md +158 -10
  3. package/CLAUDE.md +138 -2
  4. package/README.md +1 -1
  5. package/lib/context/defaults.d.ts +7 -0
  6. package/lib/context/defaults.js +74 -30
  7. package/lib/context/directory/handlers/actionModules.js +1 -4
  8. package/lib/context/directory/handlers/actions.js +1 -4
  9. package/lib/context/directory/handlers/attackProtection.js +1 -1
  10. package/lib/context/directory/handlers/branding.js +4 -1
  11. package/lib/context/directory/handlers/clientAuthCredentials.d.ts +5 -0
  12. package/lib/context/directory/handlers/clientAuthCredentials.js +13 -0
  13. package/lib/context/directory/handlers/clientGrants.js +78 -24
  14. package/lib/context/directory/handlers/clients.js +20 -3
  15. package/lib/context/directory/handlers/connections.js +19 -5
  16. package/lib/context/directory/handlers/databases.js +1 -4
  17. package/lib/context/directory/handlers/emailTemplates.js +5 -0
  18. package/lib/context/directory/handlers/guardianEmailFactorSettings.d.ts +5 -0
  19. package/lib/context/directory/handlers/guardianEmailFactorSettings.js +38 -0
  20. package/lib/context/directory/handlers/guardianPhoneFactorSettings.d.ts +5 -0
  21. package/lib/context/directory/handlers/guardianPhoneFactorSettings.js +38 -0
  22. package/lib/context/directory/handlers/guardianSettings.d.ts +5 -0
  23. package/lib/context/directory/handlers/guardianSettings.js +38 -0
  24. package/lib/context/directory/handlers/hooks.js +1 -4
  25. package/lib/context/directory/handlers/index.js +12 -0
  26. package/lib/context/directory/handlers/networkACLKeys.d.ts +6 -0
  27. package/lib/context/directory/handlers/networkACLKeys.js +54 -0
  28. package/lib/context/directory/handlers/prompts.js +7 -3
  29. package/lib/context/directory/handlers/rateLimitPolicies.d.ts +6 -0
  30. package/lib/context/directory/handlers/rateLimitPolicies.js +53 -0
  31. package/lib/context/directory/handlers/rules.js +1 -4
  32. package/lib/context/directory/index.js +1 -4
  33. package/lib/context/index.js +31 -31
  34. package/lib/context/yaml/handlers/attackProtection.js +1 -1
  35. package/lib/context/yaml/handlers/branding.js +4 -1
  36. package/lib/context/yaml/handlers/clientAuthCredentials.d.ts +5 -0
  37. package/lib/context/yaml/handlers/clientAuthCredentials.js +13 -0
  38. package/lib/context/yaml/handlers/clients.js +19 -1
  39. package/lib/context/yaml/handlers/connections.js +8 -1
  40. package/lib/context/yaml/handlers/flows.js +3 -0
  41. package/lib/context/yaml/handlers/forms.js +3 -0
  42. package/lib/context/yaml/handlers/guardianEmailFactorSettings.d.ts +5 -0
  43. package/lib/context/yaml/handlers/guardianEmailFactorSettings.js +15 -0
  44. package/lib/context/yaml/handlers/guardianPhoneFactorSettings.d.ts +5 -0
  45. package/lib/context/yaml/handlers/guardianPhoneFactorSettings.js +15 -0
  46. package/lib/context/yaml/handlers/guardianSettings.d.ts +5 -0
  47. package/lib/context/yaml/handlers/guardianSettings.js +15 -0
  48. package/lib/context/yaml/handlers/index.js +12 -0
  49. package/lib/context/yaml/handlers/networkACLKeys.d.ts +6 -0
  50. package/lib/context/yaml/handlers/networkACLKeys.js +42 -0
  51. package/lib/context/yaml/handlers/prompts.js +3 -1
  52. package/lib/context/yaml/handlers/rateLimitPolicies.d.ts +6 -0
  53. package/lib/context/yaml/handlers/rateLimitPolicies.js +27 -0
  54. package/lib/context/yaml/index.js +1 -4
  55. package/lib/keywordPreservation.d.ts +1 -1
  56. package/lib/keywordPreservation.js +2 -1
  57. package/lib/readonly.js +5 -0
  58. package/lib/tools/auth0/handlers/attackProtection.js +19 -6
  59. package/lib/tools/auth0/handlers/clientAuthCredentials.d.ts +27 -0
  60. package/lib/tools/auth0/handlers/clientAuthCredentials.js +234 -0
  61. package/lib/tools/auth0/handlers/clientAuthCredentialsPre.d.ts +38 -0
  62. package/lib/tools/auth0/handlers/clientAuthCredentialsPre.js +123 -0
  63. package/lib/tools/auth0/handlers/clientGrants.d.ts +1 -1
  64. package/lib/tools/auth0/handlers/clients.d.ts +106 -0
  65. package/lib/tools/auth0/handlers/clients.js +206 -2
  66. package/lib/tools/auth0/handlers/connectionProfiles.d.ts +27 -0
  67. package/lib/tools/auth0/handlers/connectionProfiles.js +30 -0
  68. package/lib/tools/auth0/handlers/connections.d.ts +32 -6
  69. package/lib/tools/auth0/handlers/connections.js +49 -7
  70. package/lib/tools/auth0/handlers/databases.d.ts +28 -0
  71. package/lib/tools/auth0/handlers/databases.js +58 -24
  72. package/lib/tools/auth0/handlers/default.d.ts +23 -0
  73. package/lib/tools/auth0/handlers/default.js +47 -29
  74. package/lib/tools/auth0/handlers/eventStreams.js +10 -2
  75. package/lib/tools/auth0/handlers/guardianEmailFactorSettings.d.ts +22 -0
  76. package/lib/tools/auth0/handlers/guardianEmailFactorSettings.js +77 -0
  77. package/lib/tools/auth0/handlers/guardianFactorTemplates.js +13 -4
  78. package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +11 -9
  79. package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +11 -9
  80. package/lib/tools/auth0/handlers/guardianPhoneFactorSettings.d.ts +22 -0
  81. package/lib/tools/auth0/handlers/guardianPhoneFactorSettings.js +77 -0
  82. package/lib/tools/auth0/handlers/guardianPolicies.js +11 -1
  83. package/lib/tools/auth0/handlers/guardianSettings.d.ts +30 -0
  84. package/lib/tools/auth0/handlers/guardianSettings.js +85 -0
  85. package/lib/tools/auth0/handlers/hooks.js +1 -1
  86. package/lib/tools/auth0/handlers/index.js +16 -0
  87. package/lib/tools/auth0/handlers/networkACLKeys.d.ts +45 -0
  88. package/lib/tools/auth0/handlers/networkACLKeys.js +177 -0
  89. package/lib/tools/auth0/handlers/networkACLs.d.ts +132 -2
  90. package/lib/tools/auth0/handlers/networkACLs.js +100 -16
  91. package/lib/tools/auth0/handlers/organizations.d.ts +33 -1
  92. package/lib/tools/auth0/handlers/organizations.js +177 -19
  93. package/lib/tools/auth0/handlers/phoneProvider.js +10 -2
  94. package/lib/tools/auth0/handlers/phoneTemplates.d.ts +4 -1
  95. package/lib/tools/auth0/handlers/phoneTemplates.js +67 -10
  96. package/lib/tools/auth0/handlers/prompts.js +1 -1
  97. package/lib/tools/auth0/handlers/rateLimitPolicies.d.ts +81 -0
  98. package/lib/tools/auth0/handlers/rateLimitPolicies.js +203 -0
  99. package/lib/tools/auth0/handlers/resourceServers.d.ts +44 -0
  100. package/lib/tools/auth0/handlers/resourceServers.js +44 -0
  101. package/lib/tools/auth0/handlers/riskAssessment.d.ts +1 -1
  102. package/lib/tools/auth0/handlers/riskAssessment.js +19 -1
  103. package/lib/tools/auth0/handlers/roles.d.ts +4 -0
  104. package/lib/tools/auth0/handlers/roles.js +10 -0
  105. package/lib/tools/auth0/handlers/rules.js +19 -7
  106. package/lib/tools/auth0/handlers/tenant.d.ts +45 -0
  107. package/lib/tools/auth0/handlers/tenant.js +48 -0
  108. package/lib/tools/auth0/handlers/themes.d.ts +37 -1
  109. package/lib/tools/auth0/handlers/themes.js +64 -0
  110. package/lib/tools/auth0/handlers/tokenExchangeProfiles.js +3 -1
  111. package/lib/tools/auth0/index.js +12 -6
  112. package/lib/tools/calculateDryRunChanges.js +12 -8
  113. package/lib/tools/constants.d.ts +2 -0
  114. package/lib/tools/constants.js +3 -0
  115. package/lib/tools/utils.d.ts +3 -0
  116. package/lib/tools/utils.js +41 -2
  117. package/lib/types.d.ts +9 -1
  118. package/lib/utils.d.ts +6 -0
  119. package/lib/utils.js +17 -2
  120. package/package.json +2 -2
  121. package/references/code-style.md +133 -0
  122. package/references/commands.md +98 -0
  123. package/references/docs-update.md +34 -0
  124. package/references/git-workflow.md +135 -0
  125. package/references/pitfalls.md +62 -0
  126. package/references/testing.md +118 -0
@@ -103,6 +103,27 @@ export declare const schema: {
103
103
  required: string[];
104
104
  additionalProperties: boolean;
105
105
  };
106
+ cross_app_access_requesting_app: {
107
+ type: string;
108
+ properties: {
109
+ active: {
110
+ type: string;
111
+ };
112
+ };
113
+ required: string[];
114
+ additionalProperties: boolean;
115
+ };
116
+ cross_app_access_resource_app: {
117
+ type: string;
118
+ properties: {
119
+ status: {
120
+ type: string;
121
+ enum: string[];
122
+ };
123
+ };
124
+ required: string[];
125
+ additionalProperties: boolean;
126
+ };
106
127
  directory_provisioning_configuration: {
107
128
  type: string;
108
129
  properties: {
@@ -138,6 +159,15 @@ export declare const schema: {
138
159
  id: {
139
160
  type: string;
140
161
  };
162
+ name: {
163
+ type: string;
164
+ };
165
+ email: {
166
+ type: string;
167
+ };
168
+ direct_members_count: {
169
+ type: string;
170
+ };
141
171
  };
142
172
  required: string[];
143
173
  };
@@ -152,9 +182,7 @@ type DirectoryProvisioningConfig = Management.DirectoryProvisioning;
152
182
  export type Connection = Management.ConnectionForList & {
153
183
  enabled_clients?: string[];
154
184
  directory_provisioning_configuration?: Pick<DirectoryProvisioningConfig, 'mapping' | 'synchronize_automatically' | 'synchronize_groups'> & {
155
- synchronized_groups?: Array<{
156
- id: string;
157
- }>;
185
+ synchronized_groups?: Management.SynchronizedGroupPayload[];
158
186
  };
159
187
  };
160
188
  export declare const addExcludedConnectionPropertiesToChanges: ({ proposedChanges, existingConnections, config, }: {
@@ -230,9 +258,7 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
230
258
  /**
231
259
  * Retrieves all synchronized groups for a connection using checkpoint pagination.
232
260
  */
233
- getConnectionSynchronizedGroups(connectionId: string): Promise<Array<{
234
- id: string;
235
- }> | null>;
261
+ getConnectionSynchronizedGroups(connectionId: string): Promise<Management.SynchronizedGroupPayload[] | null>;
236
262
  /**
237
263
  * Replaces the synchronized groups for a connection (PUT replace-all semantics).
238
264
  */
@@ -129,6 +129,22 @@ exports.schema = {
129
129
  required: ['active'],
130
130
  additionalProperties: false,
131
131
  },
132
+ cross_app_access_requesting_app: {
133
+ type: 'object',
134
+ properties: {
135
+ active: { type: 'boolean' },
136
+ },
137
+ required: ['active'],
138
+ additionalProperties: false,
139
+ },
140
+ cross_app_access_resource_app: {
141
+ type: 'object',
142
+ properties: {
143
+ status: { type: 'string', enum: ['enabled', 'disabled'] },
144
+ },
145
+ required: ['status'],
146
+ additionalProperties: false,
147
+ },
132
148
  directory_provisioning_configuration: {
133
149
  type: 'object',
134
150
  properties: {
@@ -156,6 +172,9 @@ exports.schema = {
156
172
  type: 'object',
157
173
  properties: {
158
174
  id: { type: 'string' },
175
+ name: { type: 'string' },
176
+ email: { type: 'string' },
177
+ direct_members_count: { type: 'number' },
159
178
  },
160
179
  required: ['id'],
161
180
  },
@@ -361,12 +380,15 @@ class ConnectionsHandler extends default_1.default {
361
380
  }
362
381
  getFormattedOptions(connection, clients) {
363
382
  try {
383
+ const { idpinitiated } = connection.options;
364
384
  return {
365
385
  options: {
366
386
  ...connection.options,
367
387
  idpinitiated: {
368
- ...connection.options.idpinitiated,
369
- client_id: (0, utils_1.convertClientNameToId)(connection.options.idpinitiated.client_id, clients),
388
+ ...idpinitiated,
389
+ ...(idpinitiated.client_id && {
390
+ client_id: (0, utils_1.convertClientNameToId)(idpinitiated.client_id, clients),
391
+ }),
370
392
  },
371
393
  },
372
394
  };
@@ -467,12 +489,28 @@ class ConnectionsHandler extends default_1.default {
467
489
  try {
468
490
  const groups = [];
469
491
  let page = await this.client.connections.directoryProvisioning.listSynchronizedGroups(connectionId, {});
470
- for (const g of page.data ?? [])
471
- groups.push({ id: g.id });
492
+ for (const g of page.data ?? []) {
493
+ groups.push({
494
+ id: g.id,
495
+ ...(g.name !== undefined && { name: g.name }),
496
+ ...(g.email !== undefined && { email: g.email }),
497
+ ...(g.direct_members_count !== undefined && {
498
+ direct_members_count: g.direct_members_count,
499
+ }),
500
+ });
501
+ }
472
502
  while (page.hasNextPage?.()) {
473
503
  page = await page.getNextPage();
474
- for (const g of page.data ?? [])
475
- groups.push({ id: g.id });
504
+ for (const g of page.data ?? []) {
505
+ groups.push({
506
+ id: g.id,
507
+ ...(g.name !== undefined && { name: g.name }),
508
+ ...(g.email !== undefined && { email: g.email }),
509
+ ...(g.direct_members_count !== undefined && {
510
+ direct_members_count: g.direct_members_count,
511
+ }),
512
+ });
513
+ }
476
514
  }
477
515
  return groups;
478
516
  }
@@ -709,7 +747,11 @@ class ConnectionsHandler extends default_1.default {
709
747
  ...(enabledClients !== undefined && { enabled_clients: enabledClients }),
710
748
  };
711
749
  });
712
- const proposedChanges = await super.dryRunChanges({ ...assets, connections: formatted });
750
+ let proposedChanges = await super.dryRunChanges({ ...assets, connections: formatted });
751
+ const includedConnections = (assets.include && assets.include.connections) || [];
752
+ const excludedConnections = (assets.exclude && assets.exclude.connections) || [];
753
+ proposedChanges = (0, utils_1.filterExcluded)(proposedChanges, excludedConnections);
754
+ proposedChanges = (0, utils_1.filterIncluded)(proposedChanges, includedConnections);
713
755
  return (0, exports.addExcludedConnectionPropertiesToChanges)({
714
756
  proposedChanges,
715
757
  existingConnections,
@@ -195,6 +195,33 @@ export declare const schema: {
195
195
  };
196
196
  };
197
197
  };
198
+ otp_settings: {
199
+ type: string;
200
+ properties: {
201
+ email: {
202
+ type: string;
203
+ properties: {
204
+ otp_length: {
205
+ type: string;
206
+ };
207
+ otp_expiry: {
208
+ type: string;
209
+ };
210
+ };
211
+ };
212
+ phone: {
213
+ type: string;
214
+ properties: {
215
+ otp_length: {
216
+ type: string;
217
+ };
218
+ otp_expiry: {
219
+ type: string;
220
+ };
221
+ };
222
+ };
223
+ };
224
+ };
198
225
  password_options: {
199
226
  type: string;
200
227
  properties: {
@@ -295,6 +322,7 @@ export default class DatabaseHandler extends DefaultAPIHandler {
295
322
  private validatePasswordlessSettings;
296
323
  private validateEmailUniqueConstraints;
297
324
  getClientFN(fn: 'create' | 'delete' | 'getAll' | 'update'): Function;
325
+ private enrichConnectionsWithEnabledClients;
298
326
  getType(): Promise<Connection[]>;
299
327
  calcChanges(assets: Assets): Promise<CalculatedChanges>;
300
328
  dryRunChanges(assets: Assets): Promise<CalculatedChanges>;
@@ -181,6 +181,25 @@ exports.schema = {
181
181
  action_id: { type: 'string' },
182
182
  },
183
183
  },
184
+ otp_settings: {
185
+ type: 'object',
186
+ properties: {
187
+ email: {
188
+ type: 'object',
189
+ properties: {
190
+ otp_length: { type: 'integer' },
191
+ otp_expiry: { type: 'integer' },
192
+ },
193
+ },
194
+ phone: {
195
+ type: 'object',
196
+ properties: {
197
+ otp_length: { type: 'integer' },
198
+ otp_expiry: { type: 'integer' },
199
+ },
200
+ },
201
+ },
202
+ },
184
203
  password_options: {
185
204
  type: 'object',
186
205
  properties: {
@@ -419,6 +438,22 @@ class DatabaseHandler extends default_1.default {
419
438
  }
420
439
  return this.client.connections[fn].bind(this.client.connections);
421
440
  }
441
+ // `enabled_clients` is no longer returned by connections.list; it must be fetched from the
442
+ // dedicated enabled-clients endpoint. Enriching the remote connections keeps the diff and the
443
+ // excluded-client preservation in `getEnabledClients` correct. Without it the dry-run diff
444
+ // reports a false "found in localObj but not in remoteObj" and a real import can silently
445
+ // disable an excluded-but-enabled client.
446
+ async enrichConnectionsWithEnabledClients(connections) {
447
+ return Promise.all(connections.map(async (con) => {
448
+ if (!con?.id)
449
+ return con;
450
+ const enabledClients = await (0, connections_1.getConnectionEnabledClients)(this.client, con.id);
451
+ if (enabledClients?.length) {
452
+ return { ...con, enabled_clients: enabledClients };
453
+ }
454
+ return con;
455
+ }));
456
+ }
422
457
  async getType() {
423
458
  if (this.existing)
424
459
  return this.existing;
@@ -433,16 +468,7 @@ class DatabaseHandler extends default_1.default {
433
468
  include_totals: true,
434
469
  }),
435
470
  ]);
436
- const dbConnectionsWithEnabledClients = await Promise.all(connections.map(async (con) => {
437
- if (!con?.id)
438
- return con;
439
- const enabledClients = await (0, connections_1.getConnectionEnabledClients)(this.client, con.id);
440
- const connection = { ...con };
441
- if (enabledClients && enabledClients?.length) {
442
- connection.enabled_clients = enabledClients;
443
- }
444
- return connection;
445
- }));
471
+ const dbConnectionsWithEnabledClients = await this.enrichConnectionsWithEnabledClients(connections);
446
472
  // Convert action ID back to action name for export
447
473
  const dbConnectionsWithActionNames = dbConnectionsWithEnabledClients.map((connection) => {
448
474
  if (connection.options && 'custom_password_hash' in connection.options) {
@@ -462,12 +488,21 @@ class DatabaseHandler extends default_1.default {
462
488
  }
463
489
  return connection;
464
490
  });
491
+ // A connection can return both legacy password fields and `password_options` on read, but
492
+ // they're mutually exclusive on write. Strip the legacy fields to keep the export deployable.
493
+ const dbConnectionsNormalized = dbConnectionsWithActionNames.map((connection) => {
494
+ if (connection.options && 'password_options' in connection.options) {
495
+ const { passwordPolicy, password_complexity_options, password_history, password_no_personal_info, password_dictionary, ...options } = connection.options;
496
+ return { ...connection, options };
497
+ }
498
+ return connection;
499
+ });
465
500
  // If options option is empty for all connection, log the missing options scope.
466
- const isOptionExists = dbConnectionsWithActionNames.every((c) => c.options && Object.keys(c.options).length > 0);
501
+ const isOptionExists = dbConnectionsNormalized.every((c) => c.options && Object.keys(c.options).length > 0);
467
502
  if (!isOptionExists) {
468
503
  logger_1.default.warn(`Insufficient scope the read:connections_options scope is required to get ${this.type} options.`);
469
504
  }
470
- this.existing = dbConnectionsWithActionNames;
505
+ this.existing = dbConnectionsNormalized;
471
506
  return this.existing;
472
507
  }
473
508
  async calcChanges(assets) {
@@ -480,28 +515,26 @@ class DatabaseHandler extends default_1.default {
480
515
  update: [],
481
516
  conflicts: [],
482
517
  };
483
- // Convert enabled_clients by name to the id
484
- // Fetch clients, connections, and actions concurrently
485
- const [clients, existingDatabasesConnections, actions] = await Promise.all([
518
+ // Convert enabled_clients by name to the id. `getType()` fetches and enriches the existing
519
+ // connections with `enabled_clients` and caches the result in `this.existing`, so the
520
+ // `super.calcChanges()` call below reuses it instead of fetching again.
521
+ const [clients, actions, existingWithEnabledClients] = await Promise.all([
486
522
  (0, client_1.paginate)(this.client.clients.list, {
487
523
  paginate: true,
488
524
  }),
489
- (0, client_1.paginate)(this.client.connections.list, {
490
- strategy: [auth0_1.Management.ConnectionStrategyEnum.Auth0],
491
- checkpoint: true,
492
- include_totals: true,
493
- }),
494
525
  (0, client_1.paginate)(this.client.actions.list, {
495
526
  paginate: true,
496
527
  include_totals: true,
497
528
  }),
529
+ this.getType(),
498
530
  ]);
531
+ const existingConnections = existingWithEnabledClients || [];
499
532
  const formatted = databases.map((db) => {
500
533
  const { options, ...rest } = db;
501
534
  const formattedOptions = this.getFormattedOptions(options, actions);
502
535
  const formattedDb = { ...rest, options: formattedOptions };
503
536
  if (db.enabled_clients) {
504
- formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db, existingDatabasesConnections, clients);
537
+ formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db, existingConnections, clients);
505
538
  }
506
539
  return formattedDb;
507
540
  });
@@ -531,21 +564,22 @@ class DatabaseHandler extends default_1.default {
531
564
  include_totals: true,
532
565
  }),
533
566
  ]);
567
+ const existingWithEnabledClients = await this.enrichConnectionsWithEnabledClients(existingDatabasesConnections);
534
568
  const formatted = databases.map((db) => {
535
569
  const { options, ...rest } = db;
536
570
  const formattedOptions = this.getFormattedOptions(options, actions);
537
571
  const formattedDb = { ...rest, options: formattedOptions };
538
572
  if (db.enabled_clients) {
539
- formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db, existingDatabasesConnections, clients);
573
+ formattedDb.enabled_clients = (0, utils_1.getEnabledClients)(assets, db, existingWithEnabledClients, clients);
540
574
  }
541
575
  return formattedDb;
542
576
  });
543
577
  return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
544
578
  type: this.type,
545
579
  assets: formatted,
546
- existing: existingDatabasesConnections,
580
+ existing: existingWithEnabledClients,
547
581
  identifiers: this.identifiers,
548
- ignoreDryRunFields: this.ignoreDryRunFields,
582
+ ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
549
583
  });
550
584
  }
551
585
  // Run after clients are updated so we can convert all the enabled_clients names to id's
@@ -1,6 +1,20 @@
1
1
  import { Asset, Assets, Auth0APIClient, CalculatedChanges } from '../../../types';
2
2
  import { ConfigFunction } from '../../../configFactory';
3
3
  export declare function order(value: any): (t: any, n: any, descriptor: any) => any;
4
+ export interface RetryOptions {
5
+ maxRetries?: number;
6
+ initialDelay?: number;
7
+ maxDelay?: number;
8
+ onRetry?: (error: any, attempt: number, delay: number) => void;
9
+ }
10
+ /**
11
+ * Executes a function with exponential backoff retry logic for rate limit errors (429).
12
+ *
13
+ * @param fn - The function to execute with retry logic
14
+ * @param options - Configuration options for retry behavior
15
+ * @returns Promise that resolves with the function result or rejects after max retries
16
+ */
17
+ export declare function retryWithExponentialBackoff<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
4
18
  type ApiMethodOverride = string | Function;
5
19
  export default class APIHandler {
6
20
  config: ConfigFunction;
@@ -50,6 +64,15 @@ export default class APIHandler {
50
64
  */
51
65
  getEffectiveIgnoreDryRunFields(): string[];
52
66
  getClientFN(fn: ApiMethodOverride): Function;
67
+ /**
68
+ * Builds the exponential-backoff retry configuration for this handler from the
69
+ * shared `AUTH0_MAX_RETRIES` / `AUTH0_RETRY_INITIAL_DELAY_MS` /
70
+ * `AUTH0_RETRY_MAX_DELAY_MS` config keys. Exposed so that handlers which issue
71
+ * writes outside the default `processChanges` flow (e.g. organizations, which
72
+ * writes nested connections/grants directly) can wrap those calls in the same
73
+ * 429 backoff behaviour as the base handler.
74
+ */
75
+ getRetryConfig(): RetryOptions;
53
76
  didDelete(item: Asset): void;
54
77
  didCreate(item: Asset): void;
55
78
  didUpdate(item: Asset): void;
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.order = order;
7
- const auth0_1 = require("auth0");
7
+ exports.retryWithExponentialBackoff = retryWithExponentialBackoff;
8
8
  const validationError_1 = __importDefault(require("../../validationError"));
9
9
  const utils_1 = require("../../utils");
10
10
  const logger_1 = __importDefault(require("../../../logger"));
@@ -20,6 +20,19 @@ function order(value) {
20
20
  return descriptor;
21
21
  };
22
22
  }
23
+ /**
24
+ * node-auth0 v7 removed the exported `JSONApiResponse` class, so we can no
25
+ * longer use `instanceof` to tell an SDK response envelope apart from a plain
26
+ * asset. Duck-type the `ApiResponse<T>` shape ({ data, status, headers })
27
+ * instead.
28
+ */
29
+ function isApiResponseEnvelope(item) {
30
+ return (typeof item === 'object' &&
31
+ item !== null &&
32
+ 'data' in item &&
33
+ 'status' in item &&
34
+ 'headers' in item);
35
+ }
23
36
  // Retry configuration constants
24
37
  const DEFAULT_MAX_RETRIES = 3;
25
38
  const DEFAULT_INITIAL_DELAY_MS = 1000; // 1 second
@@ -114,24 +127,35 @@ class APIHandler {
114
127
  }
115
128
  return fn;
116
129
  }
130
+ /**
131
+ * Builds the exponential-backoff retry configuration for this handler from the
132
+ * shared `AUTH0_MAX_RETRIES` / `AUTH0_RETRY_INITIAL_DELAY_MS` /
133
+ * `AUTH0_RETRY_MAX_DELAY_MS` config keys. Exposed so that handlers which issue
134
+ * writes outside the default `processChanges` flow (e.g. organizations, which
135
+ * writes nested connections/grants directly) can wrap those calls in the same
136
+ * 429 backoff behaviour as the base handler.
137
+ */
138
+ getRetryConfig() {
139
+ const retryConfig = {
140
+ maxRetries: this.config('AUTH0_MAX_RETRIES') || DEFAULT_MAX_RETRIES,
141
+ initialDelay: this.config('AUTH0_RETRY_INITIAL_DELAY_MS') || DEFAULT_INITIAL_DELAY_MS,
142
+ maxDelay: this.config('AUTH0_RETRY_MAX_DELAY_MS') || DEFAULT_MAX_DELAY_MS,
143
+ onRetry: (error, attempt, delay) => {
144
+ logger_1.default.warn(`Rate limit hit for [${this.type}]. Retrying attempt ${attempt}/${retryConfig.maxRetries} after ${Math.round(delay / 1000)}s...`);
145
+ },
146
+ };
147
+ return retryConfig;
148
+ }
117
149
  didDelete(item) {
118
150
  logger_1.default.info(`Deleted [${this.type}]: ${this.objString(item)}`);
119
151
  }
120
152
  didCreate(item) {
121
- if (typeof item === 'object' && item instanceof auth0_1.JSONApiResponse) {
122
- logger_1.default.info(`Created [${this.type}]: ${this.objString(item?.data)}`);
123
- }
124
- else {
125
- logger_1.default.info(`Created [${this.type}]: ${this.objString(item)}`);
126
- }
153
+ const payload = isApiResponseEnvelope(item) ? item.data : item;
154
+ logger_1.default.info(`Created [${this.type}]: ${this.objString(payload)}`);
127
155
  }
128
156
  didUpdate(item) {
129
- if (typeof item === 'object' && item instanceof auth0_1.JSONApiResponse) {
130
- logger_1.default.info(`Updated [${this.type}]: ${this.objString(item?.data)}`);
131
- }
132
- else {
133
- logger_1.default.info(`Updated [${this.type}]: ${this.objString(item)}`);
134
- }
157
+ const payload = isApiResponseEnvelope(item) ? item.data : item;
158
+ logger_1.default.info(`Updated [${this.type}]: ${this.objString(payload)}`);
135
159
  }
136
160
  objString(item) {
137
161
  return (0, utils_1.convertJsonToString)(item);
@@ -168,6 +192,9 @@ class APIHandler {
168
192
  this.type === 'branding' ||
169
193
  this.type === 'emailProvider' ||
170
194
  this.type === 'guardianPhoneFactorSelectedProvider' ||
195
+ this.type === 'guardianPhoneFactorSettings' ||
196
+ this.type === 'guardianEmailFactorSettings' ||
197
+ this.type === 'guardianSettings' ||
171
198
  this.type === 'guardianPolicies' ||
172
199
  this.type === 'riskAssessment') {
173
200
  return `${this.type} settings`;
@@ -210,7 +237,6 @@ class APIHandler {
210
237
  return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
211
238
  type: this.type,
212
239
  assets: typeAssets,
213
- // @ts-ignore TODO: investigate what happens when `existing` is null
214
240
  existing,
215
241
  identifiers: this.identifiers,
216
242
  ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
@@ -243,7 +269,6 @@ class APIHandler {
243
269
  return (0, calculateDryRunChanges_1.calculateDryRunChanges)({
244
270
  type: this.type,
245
271
  assets: typeAssets,
246
- // @ts-ignore TODO: investigate what happens when `existing` is null
247
272
  existing,
248
273
  identifiers: this.identifiers,
249
274
  ignoreDryRunFields: this.getEffectiveIgnoreDryRunFields(),
@@ -282,14 +307,7 @@ class APIHandler {
282
307
  const conflicts = changes.conflicts || [];
283
308
  logger_1.default.debug(`Start processChanges for ${this.type} [delete:${del.length}] [update:${update.length}], [create:${create.length}], [conflicts:${conflicts.length}]`);
284
309
  // Set retry configuration from config
285
- const retryConfig = {
286
- maxRetries: this.config('AUTH0_MAX_RETRIES') || DEFAULT_MAX_RETRIES,
287
- initialDelay: this.config('AUTH0_RETRY_INITIAL_DELAY_MS') || DEFAULT_INITIAL_DELAY_MS,
288
- maxDelay: this.config('AUTH0_RETRY_MAX_DELAY_MS') || DEFAULT_MAX_DELAY_MS,
289
- onRetry: (error, attempt, delay) => {
290
- logger_1.default.warn(`Rate limit hit for [${this.type}]. Retrying attempt ${attempt}/${retryConfig.maxRetries} after ${Math.round(delay / 1000)}s...`);
291
- },
292
- };
310
+ const retryConfig = this.getRetryConfig();
293
311
  // Process Deleted
294
312
  if (del.length > 0) {
295
313
  const allowDelete = this.config('AUTH0_ALLOW_DELETE') === 'true' || this.config('AUTH0_ALLOW_DELETE') === true;
@@ -328,8 +346,8 @@ class APIHandler {
328
346
  const updateFN = this.getClientFN(this.functions.update);
329
347
  const updatePayload = (() => {
330
348
  const data = (0, utils_1.stripFields)({ ...updateItem }, this.stripUpdateFields);
331
- const stripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(data, this.sensitiveFieldsToObfuscate);
332
- return (0, utils_1.validateNoUnresolvedPlaceholders)(stripped, this.type, this.objString(updateItem));
349
+ const obfuscatedStripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(data, this.sensitiveFieldsToObfuscate);
350
+ return (0, utils_1.stripUnresolvedPlaceholders)(obfuscatedStripped, this.type, this.objString(updateItem));
333
351
  })();
334
352
  return updateFN(updateItem[this.id], updatePayload);
335
353
  }, retryConfig)
@@ -347,8 +365,8 @@ class APIHandler {
347
365
  const createFunction = this.getClientFN(this.functions.create);
348
366
  const createPayload = (() => {
349
367
  const strippedPayload = (0, utils_1.stripFields)(createItem, this.stripCreateFields);
350
- const stripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(strippedPayload, this.sensitiveFieldsToObfuscate);
351
- return (0, utils_1.validateNoUnresolvedPlaceholders)(stripped, this.type, this.objString(createItem));
368
+ const obfuscatedStripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(strippedPayload, this.sensitiveFieldsToObfuscate);
369
+ return (0, utils_1.stripUnresolvedPlaceholders)(obfuscatedStripped, this.type, this.objString(createItem));
352
370
  })();
353
371
  return createFunction(createPayload);
354
372
  }, retryConfig)
@@ -369,8 +387,8 @@ class APIHandler {
369
387
  const updateFN = this.getClientFN(this.functions.update);
370
388
  const updatePayload = (() => {
371
389
  const data = (0, utils_1.stripFields)({ ...updateItem }, this.stripUpdateFields);
372
- const stripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(data, this.sensitiveFieldsToObfuscate);
373
- return (0, utils_1.validateNoUnresolvedPlaceholders)(stripped, this.type, this.objString(updateItem));
390
+ const obfuscatedStripped = (0, utils_1.stripObfuscatedFieldsFromPayload)(data, this.sensitiveFieldsToObfuscate);
391
+ return (0, utils_1.stripUnresolvedPlaceholders)(obfuscatedStripped, this.type, this.objString(updateItem));
374
392
  })();
375
393
  return updateFN(updateItem[this.id], updatePayload);
376
394
  }, retryConfig)
@@ -38,10 +38,14 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  return result;
39
39
  };
40
40
  })();
41
+ var __importDefault = (this && this.__importDefault) || function (mod) {
42
+ return (mod && mod.__esModule) ? mod : { "default": mod };
43
+ };
41
44
  Object.defineProperty(exports, "__esModule", { value: true });
42
45
  exports.schema = void 0;
43
46
  const default_1 = __importStar(require("./default"));
44
47
  const client_1 = require("../client");
48
+ const logger_1 = __importDefault(require("../../../logger"));
45
49
  exports.schema = {
46
50
  type: 'array',
47
51
  items: {
@@ -161,8 +165,12 @@ class EventStreamsHandler extends default_1.default {
161
165
  create: changesResponse.create.map((stream) => this.resolveActionId(stream)),
162
166
  update: changesResponse.update.map((stream) => {
163
167
  const resolved = this.resolveActionId(stream);
164
- // EventBridge destinations cannot be patched — the API rejects destination on PATCH
165
- if (resolved.destination?.type === 'eventbridge') {
168
+ // A destination's type cannot be changed once an event stream is created. The API
169
+ // rejects the destination on PATCH for eventbridge and action streams, so strip it
170
+ // from the update payload and only apply the other mutable fields.
171
+ if (resolved.destination?.type === 'eventbridge' ||
172
+ resolved.destination?.type === 'action') {
173
+ logger_1.default.info(`Skipping destination update for event stream "${resolved.name}": ${resolved.destination.type} destinations cannot be changed after creation.`);
166
174
  const { destination: _dest, ...rest } = resolved;
167
175
  return rest;
168
176
  }
@@ -0,0 +1,22 @@
1
+ import DefaultHandler from './default';
2
+ import { Asset, Assets } from '../../../types';
3
+ export declare const schema: {
4
+ type: string;
5
+ properties: {
6
+ otp_length: {
7
+ type: string;
8
+ description: string;
9
+ };
10
+ otp_expiration_time: {
11
+ type: string;
12
+ description: string;
13
+ };
14
+ };
15
+ additionalProperties: boolean;
16
+ };
17
+ export default class GuardianEmailFactorSettingsHandler extends DefaultHandler {
18
+ existing: Asset;
19
+ constructor(options: any);
20
+ getType(): Promise<Asset | null>;
21
+ processChanges(assets: Assets): Promise<void>;
22
+ }