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
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ async function parseAndDump(context) {
4
+ const { guardianSettings } = context.assets;
5
+ if (!guardianSettings)
6
+ return { guardianSettings: null };
7
+ return {
8
+ guardianSettings,
9
+ };
10
+ }
11
+ const guardianSettingsHandler = {
12
+ parse: parseAndDump,
13
+ dump: parseAndDump,
14
+ };
15
+ exports.default = guardianSettingsHandler;
@@ -17,6 +17,9 @@ const guardianFactorProviders_1 = __importDefault(require("./guardianFactorProvi
17
17
  const guardianFactorTemplates_1 = __importDefault(require("./guardianFactorTemplates"));
18
18
  const guardianPhoneFactorMessageTypes_1 = __importDefault(require("./guardianPhoneFactorMessageTypes"));
19
19
  const guardianPhoneFactorSelectedProvider_1 = __importDefault(require("./guardianPhoneFactorSelectedProvider"));
20
+ const guardianPhoneFactorSettings_1 = __importDefault(require("./guardianPhoneFactorSettings"));
21
+ const guardianEmailFactorSettings_1 = __importDefault(require("./guardianEmailFactorSettings"));
22
+ const guardianSettings_1 = __importDefault(require("./guardianSettings"));
20
23
  const guardianPolicies_1 = __importDefault(require("./guardianPolicies"));
21
24
  const roles_1 = __importDefault(require("./roles"));
22
25
  const organizations_1 = __importDefault(require("./organizations"));
@@ -38,12 +41,15 @@ const hooks_1 = __importDefault(require("./hooks"));
38
41
  const forms_1 = __importDefault(require("./forms"));
39
42
  const flows_1 = __importDefault(require("./flows"));
40
43
  const flowVaultConnections_1 = __importDefault(require("./flowVaultConnections"));
44
+ const networkACLKeys_1 = __importDefault(require("./networkACLKeys"));
41
45
  const networkACLs_1 = __importDefault(require("./networkACLs"));
42
46
  const userAttributeProfiles_1 = __importDefault(require("./userAttributeProfiles"));
43
47
  const connectionProfiles_1 = __importDefault(require("./connectionProfiles"));
44
48
  const tokenExchangeProfiles_1 = __importDefault(require("./tokenExchangeProfiles"));
45
49
  const supplementalSignals_1 = __importDefault(require("./supplementalSignals"));
50
+ const rateLimitPolicies_1 = __importDefault(require("./rateLimitPolicies"));
46
51
  const eventStreams_1 = __importDefault(require("./eventStreams"));
52
+ const clientAuthCredentials_1 = __importDefault(require("./clientAuthCredentials"));
47
53
  const selfServiceProfiles_1 = __importDefault(require("./selfServiceProfiles"));
48
54
  const yamlHandlers = {
49
55
  rules: rules_1.default,
@@ -64,6 +70,9 @@ const yamlHandlers = {
64
70
  roles: roles_1.default,
65
71
  guardianPhoneFactorMessageTypes: guardianPhoneFactorMessageTypes_1.default,
66
72
  guardianPhoneFactorSelectedProvider: guardianPhoneFactorSelectedProvider_1.default,
73
+ guardianPhoneFactorSettings: guardianPhoneFactorSettings_1.default,
74
+ guardianEmailFactorSettings: guardianEmailFactorSettings_1.default,
75
+ guardianSettings: guardianSettings_1.default,
67
76
  guardianPolicies: guardianPolicies_1.default,
68
77
  actions: actions_1.default,
69
78
  actionModules: actionModules_1.default,
@@ -82,11 +91,14 @@ const yamlHandlers = {
82
91
  flows: flows_1.default,
83
92
  flowVaultConnections: flowVaultConnections_1.default,
84
93
  selfServiceProfiles: selfServiceProfiles_1.default,
94
+ networkACLKeys: networkACLKeys_1.default,
85
95
  networkACLs: networkACLs_1.default,
86
96
  userAttributeProfiles: userAttributeProfiles_1.default,
87
97
  connectionProfiles: connectionProfiles_1.default,
88
98
  tokenExchangeProfiles: tokenExchangeProfiles_1.default,
89
99
  supplementalSignals: supplementalSignals_1.default,
100
+ rateLimitPolicies: rateLimitPolicies_1.default,
90
101
  eventStreams: eventStreams_1.default,
102
+ clientAuthCredentials: clientAuthCredentials_1.default,
91
103
  };
92
104
  exports.default = yamlHandlers;
@@ -0,0 +1,6 @@
1
+ import { YAMLHandler } from '.';
2
+ import { ParsedAsset } from '../../../types';
3
+ import { NetworkAclKey } from '../../../tools/auth0/handlers/networkACLKeys';
4
+ type ParsedNetworkACLKeys = ParsedAsset<'networkACLKeys', NetworkAclKey[]>;
5
+ declare const networkACLKeysHandler: YAMLHandler<ParsedNetworkACLKeys>;
6
+ export default networkACLKeysHandler;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const logger_1 = __importDefault(require("../../../logger"));
7
+ async function parse(context) {
8
+ const { networkACLKeys } = context.assets;
9
+ if (!networkACLKeys)
10
+ return { networkACLKeys: null };
11
+ return {
12
+ networkACLKeys,
13
+ };
14
+ }
15
+ async function dump(context) {
16
+ let { networkACLKeys } = context.assets;
17
+ if (!networkACLKeys)
18
+ return { networkACLKeys: null };
19
+ if (Array.isArray(networkACLKeys) && networkACLKeys.length === 0) {
20
+ logger_1.default.info('No network ACL keys available, skipping dump');
21
+ return { networkACLKeys: null };
22
+ }
23
+ // value is write-only — never returned by the API and must not be exported.
24
+ // created_at and updated_at are API-generated metadata; not needed in config.
25
+ const removeKeysFromOutput = ['value', 'created_at', 'updated_at'];
26
+ networkACLKeys = networkACLKeys.map((key) => {
27
+ const out = { ...key };
28
+ removeKeysFromOutput.forEach((k) => {
29
+ if (k in out)
30
+ delete out[k];
31
+ });
32
+ return out;
33
+ });
34
+ return {
35
+ networkACLKeys,
36
+ };
37
+ }
38
+ const networkACLKeysHandler = {
39
+ parse,
40
+ dump,
41
+ };
42
+ exports.default = networkACLKeysHandler;
@@ -18,8 +18,10 @@ const loadScreenRenderers = (context, screenRenderArray) => {
18
18
  const screens = promptEntry[prompt];
19
19
  Object.entries(screens).forEach(([, fileName]) => {
20
20
  const filePath = fileName;
21
+ const configRoot = path_1.default.resolve(context.basePath);
22
+ const rendererFile = path_1.default.resolve(context.basePath, filePath);
23
+ (0, utils_1.assertInsideConfigRoot)(filePath, rendererFile, configRoot);
21
24
  try {
22
- const rendererFile = path_1.default.join(context.basePath, filePath);
23
25
  const rendererData = (0, utils_1.loadJSON)(rendererFile, {
24
26
  mappings: context.mappings,
25
27
  disableKeywordReplacement: context.disableKeywordReplacement,
@@ -0,0 +1,6 @@
1
+ import { YAMLHandler } from '.';
2
+ import { ParsedAsset } from '../../../types';
3
+ import { RateLimitPolicy } from '../../../tools/auth0/handlers/rateLimitPolicies';
4
+ type ParsedRateLimitPolicies = ParsedAsset<'rateLimitPolicies', RateLimitPolicy[]>;
5
+ declare const rateLimitPoliciesHandler: YAMLHandler<ParsedRateLimitPolicies>;
6
+ export default rateLimitPoliciesHandler;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ async function parse(context) {
4
+ const { rateLimitPolicies } = context.assets;
5
+ if (!rateLimitPolicies)
6
+ return { rateLimitPolicies: null };
7
+ return { rateLimitPolicies };
8
+ }
9
+ async function dump(context) {
10
+ const { rateLimitPolicies } = context.assets;
11
+ if (!rateLimitPolicies)
12
+ return { rateLimitPolicies: null };
13
+ const removeKeysFromOutput = ['id', 'created_at', 'updated_at'];
14
+ const cleaned = rateLimitPolicies.map((policy) => {
15
+ const policyToWrite = { ...policy };
16
+ removeKeysFromOutput.forEach((key) => {
17
+ delete policyToWrite[key];
18
+ });
19
+ return policyToWrite;
20
+ });
21
+ return { rateLimitPolicies: cleaned };
22
+ }
23
+ const rateLimitPoliciesHandler = {
24
+ parse,
25
+ dump,
26
+ };
27
+ exports.default = rateLimitPoliciesHandler;
@@ -45,10 +45,7 @@ class YAMLContext {
45
45
  loadFile(f) {
46
46
  const configRoot = path_1.default.resolve(this.basePath);
47
47
  const toLoad = path_1.default.resolve(this.basePath, f.replace(/\\/g, '/'));
48
- if (!toLoad.startsWith(configRoot + path_1.default.sep)) {
49
- throw new Error(`Path "${f}" resolves to "${toLoad}" which is outside the config directory "${configRoot}". ` +
50
- `Move the file inside your config directory.`);
51
- }
48
+ (0, utils_1.assertInsideConfigRoot)(f, toLoad, configRoot);
52
49
  return (0, tools_1.loadFileAndReplaceKeywords)(toLoad, {
53
50
  mappings: this.mappings,
54
51
  disableKeywordReplacement: this.disableKeywordReplacement,
@@ -10,6 +10,6 @@ export declare const preserveKeywords: ({ localAssets, remoteAssets, keywordMapp
10
10
  remoteAssets: object;
11
11
  keywordMappings: KeywordMappings;
12
12
  auth0Handlers: (Pick<APIHandler, "id" | "type"> & {
13
- identifiers: (string | string[])[];
13
+ identifiers?: (string | string[])[];
14
14
  })[];
15
15
  }) => object;
@@ -164,7 +164,8 @@ const preserveKeywords = ({ localAssets, remoteAssets, keywordMappings, auth0Han
164
164
  if (Object.keys(keywordMappings).length === 0)
165
165
  return remoteAssets;
166
166
  const resourceSpecificIdentifiers = auth0Handlers.reduce((acc, handler) => {
167
- acc[handler.type] = handler.identifiers.flat();
167
+ // Standalone handlers (e.g. clientAuthCredentials) don't extend APIHandler and have no identifiers.
168
+ acc[handler.type] = (handler.identifiers ?? []).flat();
168
169
  return acc;
169
170
  }, {});
170
171
  const addresses = (0, exports.getPreservableFieldsFromAssets)(localAssets, keywordMappings, resourceSpecificIdentifiers, '');
package/lib/readonly.js CHANGED
@@ -28,6 +28,11 @@ const readOnlyFields = {
28
28
  'config_route',
29
29
  'owners',
30
30
  ],
31
+ attackProtection: [
32
+ // Returned by GET but rejected by PATCH with "Additional properties not allowed"
33
+ 'bruteForceProtection.form_submission_mode',
34
+ 'bruteForceProtection.form_submission_behavior',
35
+ ],
31
36
  };
32
37
  function getExcludedFields(config) {
33
38
  const strippedFields = { ...readOnlyFields };
@@ -243,14 +243,25 @@ class AttackProtectionHandler extends default_1.default {
243
243
  let botDetection = null;
244
244
  let captcha = null;
245
245
  try {
246
- [botDetection, captcha] = await Promise.all([
247
- this.client.attackProtection.botDetection.get(),
248
- this.client.attackProtection.captcha.get(),
249
- ]);
246
+ botDetection = await this.client.attackProtection.botDetection.get();
250
247
  }
251
248
  catch (err) {
252
249
  if (err.statusCode === 403) {
253
- logger_1.default.warn('Bot Detection API are not enabled for this tenant. Please verify `scope` or contact Auth0 support to enable this feature.');
250
+ logger_1.default.warn("Bot detection is not available on this tenant's current plan. Skipping bot detection settings.");
251
+ }
252
+ else {
253
+ throw err;
254
+ }
255
+ }
256
+ try {
257
+ captcha = await this.client.attackProtection.captcha.get();
258
+ }
259
+ catch (err) {
260
+ if (err.statusCode === 403) {
261
+ logger_1.default.warn("Captcha is not available on this tenant's current plan. Skipping captcha settings.");
262
+ }
263
+ else {
264
+ throw err;
254
265
  }
255
266
  }
256
267
  const attackProtection = {
@@ -303,7 +314,9 @@ class AttackProtectionHandler extends default_1.default {
303
314
  updates.push(this.client.attackProtection.captcha.update(attackProtection.captcha));
304
315
  }
305
316
  if (attackProtection.bruteForceProtection) {
306
- updates.push(this.client.attackProtection.bruteForceProtection.update(attackProtection.bruteForceProtection));
317
+ // Strip fields returned by GET but rejected by PATCH
318
+ const { form_submission_mode, form_submission_behavior, ...bruteForcePayload } = attackProtection.bruteForceProtection;
319
+ updates.push(this.client.attackProtection.bruteForceProtection.update(bruteForcePayload));
307
320
  }
308
321
  if (attackProtection.suspiciousIpThrottling) {
309
322
  updates.push(this.client.attackProtection.suspiciousIpThrottling.update(attackProtection.suspiciousIpThrottling));
@@ -0,0 +1,27 @@
1
+ import { Assets, Auth0APIClient, CalculatedChanges } from '../../../types';
2
+ import { ConfigFunction } from '../../../configFactory';
3
+ export declare const schema: {
4
+ type: string;
5
+ items: {
6
+ type: string;
7
+ };
8
+ };
9
+ export default class ClientAuthCredentialsHandler {
10
+ client: Auth0APIClient;
11
+ config: ConfigFunction;
12
+ type: string;
13
+ existing: null;
14
+ constructor({ client, config }: {
15
+ client: Auth0APIClient;
16
+ config: ConfigFunction;
17
+ });
18
+ getType(): Promise<null>;
19
+ load(): Promise<{
20
+ [x: string]: null;
21
+ }>;
22
+ validate(): Promise<null>;
23
+ dryRunChanges(_assets: Assets): Promise<CalculatedChanges>;
24
+ processChanges(assets: Assets): Promise<void>;
25
+ private inferCredentialType;
26
+ private inferMethodFromType;
27
+ }
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.schema = void 0;
13
+ const default_1 = require("./default");
14
+ const client_1 = require("../client");
15
+ const logger_1 = __importDefault(require("../../../logger"));
16
+ exports.schema = {
17
+ type: 'array',
18
+ items: { type: 'object' },
19
+ };
20
+ const CREDENTIAL_TYPE_TO_METHOD = {
21
+ public_key: 'private_key_jwt',
22
+ x509_cert: 'self_signed_tls_client_auth',
23
+ cert_subject_dn: 'tls_client_auth',
24
+ };
25
+ class ClientAuthCredentialsHandler {
26
+ constructor({ client, config }) {
27
+ this.type = 'clientAuthCredentials';
28
+ this.existing = null;
29
+ this.client = client;
30
+ this.config = config;
31
+ }
32
+ async getType() {
33
+ return null;
34
+ }
35
+ async load() {
36
+ logger_1.default.info(`Retrieving ${this.type} data from Auth0`);
37
+ return { [this.type]: null };
38
+ }
39
+ async validate() {
40
+ return null;
41
+ }
42
+ // Credentials are per-client sub-resources with no top-level asset list, so they are not
43
+ // modeled in the dry-run diff. Return empty changes so the dry-run loop (which calls
44
+ // dryRunChanges on every handler) does not crash. Credential changes are still applied
45
+ // during a real import via processChanges.
46
+ async dryRunChanges(_assets) {
47
+ return { del: [], create: [], conflicts: [], update: [] };
48
+ }
49
+ async processChanges(assets) {
50
+ const { clients } = assets;
51
+ if (!clients)
52
+ return;
53
+ const allowDelete = this.config('AUTH0_ALLOW_DELETE') === true || this.config('AUTH0_ALLOW_DELETE') === 'true';
54
+ // Only process clients that have client_authentication_methods in config.
55
+ // Full deletion (absent field) is handled by ClientAuthCredentialsPreHandler at order 40,
56
+ // before clients.ts runs. This handler only manages create and partial delete.
57
+ const clientsWithCam = clients.filter((c) => c.client_authentication_methods !== undefined);
58
+ if (clientsWithCam.length === 0)
59
+ return;
60
+ // In directory mode client_id is stripped. Resolve names for PKI clients only.
61
+ const needsNameResolution = clientsWithCam.some((c) => !c.client_id);
62
+ let clientIdByName = new Map();
63
+ if (needsNameResolution) {
64
+ const existingClients = await (0, client_1.paginate)(this.client.clients.list, {
65
+ paginate: true,
66
+ is_global: false,
67
+ });
68
+ clientIdByName = new Map(existingClients.map((c) => [c.name, c.client_id]));
69
+ }
70
+ for (const client of clientsWithCam) {
71
+ const clientId = client.client_id || (client.name && clientIdByName.get(client.name));
72
+ if (!clientId)
73
+ continue;
74
+ client.client_id = clientId;
75
+ const clientName = client.name || clientId;
76
+ // Collect all desired credentials across all auth methods (only pem-bearing entries)
77
+ const desired = [];
78
+ if (client.client_authentication_methods) {
79
+ for (const [methodKey, methodVal] of Object.entries(client.client_authentication_methods)) {
80
+ const credentials = methodVal?.credentials || [];
81
+ for (const cred of credentials) {
82
+ if (!cred.pem)
83
+ continue;
84
+ desired.push({
85
+ name: cred.name,
86
+ pem: cred.pem,
87
+ credential_type: cred.credential_type || this.inferCredentialType(methodKey),
88
+ method: methodKey,
89
+ kid: cred.kid,
90
+ alg: cred.alg,
91
+ expires_at: cred.expires_at,
92
+ parse_expiry_from_cert: cred.parse_expiry_from_cert,
93
+ });
94
+ }
95
+ }
96
+ }
97
+ // pem is the opt-in signal: present field with no pem = skip (safe export→deploy);
98
+ // absent field = fall through to delete path (intentional removal).
99
+ if (desired.length === 0 && client.client_authentication_methods)
100
+ continue;
101
+ let existing = [];
102
+ try {
103
+ existing = (await this.client.clients.credentials.list(clientId));
104
+ }
105
+ catch (err) {
106
+ logger_1.default.warn(`clientAuthCredentials: failed to list credentials for client "${clientName}": ${err}`);
107
+ continue;
108
+ }
109
+ // No credentials in Auth0 and none desired — nothing to do.
110
+ if (existing.length === 0 && desired.length === 0)
111
+ continue;
112
+ // Warn if duplicate names exist in Auth0 — we can't match safely
113
+ const existingNames = existing.map((c) => c.name);
114
+ const duplicateNames = existingNames.filter((n, i) => existingNames.indexOf(n) !== i);
115
+ if (duplicateNames.length > 0) {
116
+ logger_1.default.warn(`clientAuthCredentials: client "${clientName}" has duplicate credential names in Auth0 [${duplicateNames.join(', ')}] — skipping credential reconciliation for this client`);
117
+ continue;
118
+ }
119
+ const existingByName = new Map(existing.map((c) => [c.name, c]));
120
+ const desiredNames = new Set(desired.map((d) => d.name));
121
+ const toCreate = desired.filter((d) => !existingByName.has(d.name));
122
+ const toDelete = existing.filter((e) => !desiredNames.has(e.name));
123
+ // Creates first — Auth0 allows max 2 credentials; both must exist simultaneously during rotation
124
+ // Track name→id so we can correctly attribute each created credential to its method later.
125
+ const createdIdByName = new Map();
126
+ for (const cred of toCreate) {
127
+ try {
128
+ // Forward all API-accepted fields. `!= null` drops unset (undefined) and empty
129
+ // (YAML `kid:` → null) values the API rejects, while keeping an explicit `false`.
130
+ const createPayload = Object.fromEntries(Object.entries({
131
+ name: cred.name,
132
+ pem: cred.pem,
133
+ credential_type: cred.credential_type,
134
+ kid: cred.kid,
135
+ alg: cred.alg,
136
+ expires_at: cred.expires_at,
137
+ parse_expiry_from_cert: cred.parse_expiry_from_cert,
138
+ }).filter(([, v]) => v != null));
139
+ const created = await this.client.clients.credentials.create(clientId, createPayload);
140
+ logger_1.default.info(`clientAuthCredentials: created credential "${cred.name}" on client "${clientName}"`);
141
+ createdIdByName.set(cred.name, created.id);
142
+ }
143
+ catch (err) {
144
+ logger_1.default.warn(`clientAuthCredentials: failed to create credential "${cred.name}" on client "${clientName}": ${err}`);
145
+ }
146
+ }
147
+ // Re-wire client_authentication_methods with final IDs (kept + newly created).
148
+ // This must happen BEFORE deletes — Auth0 rejects deleting a credential that
149
+ // is still referenced in client_authentication_methods.
150
+ const keptIds = existing.filter((e) => desiredNames.has(e.name)).map((e) => e.id);
151
+ const createdIds = [...createdIdByName.values()];
152
+ const finalIds = [...keptIds, ...createdIds];
153
+ let updatedAuthMethods = null;
154
+ if (finalIds.length > 0) {
155
+ // Build method→ids map. For kept credentials use Auth0's stored type to infer method;
156
+ // for created credentials use the method from the user's config entry (by name).
157
+ const methodToIds = {};
158
+ for (const id of finalIds) {
159
+ const fromExisting = existing.find((e) => e.id === id);
160
+ const createdName = [...createdIdByName.entries()].find(([, cid]) => cid === id)?.[0];
161
+ const fromDesired = createdName ? desired.find((d) => d.name === createdName) : undefined;
162
+ const methodKey = (fromExisting && this.inferMethodFromType(fromExisting.credential_type)) ||
163
+ (fromDesired && fromDesired.method) ||
164
+ Object.keys(client.client_authentication_methods)[0];
165
+ if (!methodToIds[methodKey])
166
+ methodToIds[methodKey] = [];
167
+ methodToIds[methodKey].push(id);
168
+ }
169
+ updatedAuthMethods = {};
170
+ for (const [methodKey, ids] of Object.entries(methodToIds)) {
171
+ updatedAuthMethods[methodKey] = { credentials: ids.map((id) => ({ id })) };
172
+ }
173
+ }
174
+ // Always update client_authentication_methods before deletes:
175
+ // - When finalIds > 0: re-wire to the remaining credentials
176
+ // - When finalIds === 0 but toDelete > 0: set to null so Auth0 de-references them first
177
+ if (finalIds.length > 0 || toDelete.length > 0) {
178
+ try {
179
+ const payload = {
180
+ client_authentication_methods: updatedAuthMethods ?? null,
181
+ };
182
+ // Auth0 requires token_endpoint_auth_method: null when setting client_authentication_methods,
183
+ // and a non-null value when clearing it.
184
+ if (updatedAuthMethods) {
185
+ payload.token_endpoint_auth_method = null;
186
+ }
187
+ else {
188
+ payload.token_endpoint_auth_method =
189
+ client.token_endpoint_auth_method || 'client_secret_post';
190
+ }
191
+ await this.client.clients.update(clientId, payload);
192
+ logger_1.default.info(`clientAuthCredentials: updated client_authentication_methods on client "${clientName}"`);
193
+ }
194
+ catch (err) {
195
+ logger_1.default.warn(`clientAuthCredentials: failed to update client_authentication_methods on client "${clientName}": ${err}`);
196
+ }
197
+ }
198
+ // Deletes last — after client_authentication_methods no longer references them
199
+ if (toDelete.length > 0) {
200
+ if (!allowDelete) {
201
+ logger_1.default.warn(`clientAuthCredentials: the following credentials on client "${clientName}" would be deleted but AUTH0_ALLOW_DELETE is not set:\n${toDelete
202
+ .map((c) => ` ${c.name} (${c.id})`)
203
+ .join('\n')}`);
204
+ }
205
+ else {
206
+ for (const cred of toDelete) {
207
+ try {
208
+ await this.client.clients.credentials.delete(clientId, cred.id);
209
+ logger_1.default.info(`clientAuthCredentials: deleted credential "${cred.name}" on client "${clientName}"`);
210
+ }
211
+ catch (err) {
212
+ logger_1.default.warn(`clientAuthCredentials: failed to delete credential "${cred.name}" on client "${clientName}": ${err}`);
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ inferCredentialType(methodKey) {
220
+ const map = {
221
+ private_key_jwt: 'public_key',
222
+ self_signed_tls_client_auth: 'x509_cert',
223
+ tls_client_auth: 'cert_subject_dn',
224
+ };
225
+ return map[methodKey] || 'public_key';
226
+ }
227
+ inferMethodFromType(credentialType) {
228
+ return CREDENTIAL_TYPE_TO_METHOD[credentialType] || 'private_key_jwt';
229
+ }
230
+ }
231
+ exports.default = ClientAuthCredentialsHandler;
232
+ __decorate([
233
+ (0, default_1.order)('70')
234
+ ], ClientAuthCredentialsHandler.prototype, "processChanges", null);
@@ -0,0 +1,38 @@
1
+ import { Assets, Auth0APIClient, CalculatedChanges } from '../../../types';
2
+ import { ConfigFunction } from '../../../configFactory';
3
+ export declare const schema: {
4
+ type: string;
5
+ items: {
6
+ type: string;
7
+ };
8
+ };
9
+ /**
10
+ * Handles full credential deletion BEFORE clients.ts (order 50) patches the client.
11
+ *
12
+ * Auth0 rejects a client PATCH containing token_endpoint_auth_method while that client still
13
+ * has active credentials. By running at order 40, this handler clears credentials (and
14
+ * client_authentication_methods) for any client where the field is absent from config, so
15
+ * that clients.ts can safely include token_endpoint_auth_method in its PATCH.
16
+ *
17
+ * Partial deletion (some credentials removed, others kept) is handled by
18
+ * ClientAuthCredentialsHandler at order 70, because those clients still have
19
+ * client_authentication_methods present in config and clients.ts strips token_endpoint_auth_method
20
+ * for them anyway.
21
+ */
22
+ export default class ClientAuthCredentialsPreHandler {
23
+ client: Auth0APIClient;
24
+ config: ConfigFunction;
25
+ type: string;
26
+ existing: null;
27
+ constructor({ client, config }: {
28
+ client: Auth0APIClient;
29
+ config: ConfigFunction;
30
+ });
31
+ getType(): Promise<null>;
32
+ load(): Promise<{
33
+ [x: string]: null;
34
+ }>;
35
+ validate(): Promise<null>;
36
+ dryRunChanges(_assets: Assets): Promise<CalculatedChanges>;
37
+ processChanges(assets: Assets): Promise<void>;
38
+ }