auth0-deploy-cli 8.32.0 → 8.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +437 -13
- package/lib/tools/auth0/handlers/actions.js +32 -29
- package/lib/tools/auth0/handlers/clientGrants.d.ts +5 -0
- package/lib/tools/auth0/handlers/clientGrants.js +30 -9
- package/lib/tools/auth0/handlers/clients.d.ts +10 -0
- package/lib/tools/auth0/handlers/clients.js +12 -0
- package/lib/tools/auth0/handlers/connections.d.ts +17 -0
- package/lib/tools/auth0/handlers/connections.js +44 -0
- package/lib/tools/auth0/handlers/tenant.d.ts +5 -0
- package/lib/tools/auth0/handlers/tenant.js +5 -0
- package/package.json +2 -2
|
@@ -320,37 +320,40 @@ class ActionHandler extends default_1.default {
|
|
|
320
320
|
if (!action.modules || action.modules.length === 0) {
|
|
321
321
|
return action;
|
|
322
322
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
323
|
+
// Process modules sequentially to avoid a pool deadlock.
|
|
324
|
+
// This function is called as a task inside the shared pool (via addEachTask in calcChanges).
|
|
325
|
+
// If we submitted further addEachTask calls on the same pool here, all concurrency slots
|
|
326
|
+
// could be held by outer action tasks waiting for inner module tasks that can never start
|
|
327
|
+
// (because no slots are free), causing a hang with 3+ actions that have modules.
|
|
328
|
+
const updatedModules = [];
|
|
329
|
+
for (const module of action.modules) {
|
|
330
|
+
const foundModule = modules.find((m) => m.name === module.module_name);
|
|
331
|
+
if (foundModule && foundModule.id) {
|
|
332
|
+
// paginate to get all versions of the module
|
|
333
|
+
const allModuleVersions = [];
|
|
334
|
+
let moduleVersions = await this.client.actions.modules.versions.list(foundModule.id);
|
|
335
|
+
// Process first page
|
|
336
|
+
allModuleVersions.push(...moduleVersions.data);
|
|
337
|
+
// Fetch remaining pages
|
|
338
|
+
while (moduleVersions.hasNextPage()) {
|
|
339
|
+
moduleVersions = await moduleVersions.getNextPage();
|
|
333
340
|
allModuleVersions.push(...moduleVersions.data);
|
|
334
|
-
// Fetch remaining pages
|
|
335
|
-
while (moduleVersions.hasNextPage()) {
|
|
336
|
-
moduleVersions = await moduleVersions.getNextPage();
|
|
337
|
-
allModuleVersions.push(...moduleVersions.data);
|
|
338
|
-
}
|
|
339
|
-
const moduleVersionId = allModuleVersions?.find((v) => v.version_number === module.module_version_number)?.id;
|
|
340
|
-
if (!moduleVersionId) {
|
|
341
|
-
throw new Error(`Could not find action module version id for module '${module.module_name}' version '${module.module_version_number}'`);
|
|
342
|
-
}
|
|
343
|
-
return {
|
|
344
|
-
module_name: module.module_name,
|
|
345
|
-
module_id: foundModule.id,
|
|
346
|
-
module_version_number: module.module_version_number,
|
|
347
|
-
module_version_id: moduleVersionId,
|
|
348
|
-
};
|
|
349
341
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
342
|
+
const moduleVersionId = allModuleVersions?.find((v) => v.version_number === module.module_version_number)?.id;
|
|
343
|
+
if (!moduleVersionId) {
|
|
344
|
+
throw new Error(`Could not find action module version id for module '${module.module_name}' version '${module.module_version_number}'`);
|
|
345
|
+
}
|
|
346
|
+
updatedModules.push({
|
|
347
|
+
module_name: module.module_name,
|
|
348
|
+
module_id: foundModule.id,
|
|
349
|
+
module_version_number: module.module_version_number,
|
|
350
|
+
module_version_id: moduleVersionId,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
updatedModules.push(module);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
354
357
|
return {
|
|
355
358
|
...action,
|
|
356
359
|
modules: updatedModules,
|
|
@@ -73,8 +73,13 @@ exports.schema = {
|
|
|
73
73
|
type: 'boolean',
|
|
74
74
|
description: 'When enabled, all scopes configured on the resource server are allowed for by this client grant.',
|
|
75
75
|
},
|
|
76
|
+
default_for: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
enum: ['third_party_clients'],
|
|
79
|
+
description: 'Indicates that a client grant is the default client grant for third party clients.',
|
|
80
|
+
},
|
|
76
81
|
},
|
|
77
|
-
required: ['
|
|
82
|
+
required: ['audience'],
|
|
78
83
|
},
|
|
79
84
|
};
|
|
80
85
|
class ClientGrantsHandler extends default_1.default {
|
|
@@ -83,11 +88,17 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
83
88
|
...config,
|
|
84
89
|
type: 'clientGrants',
|
|
85
90
|
id: 'id',
|
|
86
|
-
//
|
|
87
|
-
// Try ['client_id', 'audience', 'subject_type'] first
|
|
88
|
-
// ['
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
// Nested arrays are not reflected in the type but are supported at runtime.
|
|
92
|
+
// Try ['client_id', 'audience', 'subject_type'] first, then ['client_id', 'audience']
|
|
93
|
+
// for regular grants, then ['default_for', 'audience'] for default third-party grants
|
|
94
|
+
// which have no client_id.
|
|
95
|
+
identifiers: [
|
|
96
|
+
'id',
|
|
97
|
+
['client_id', 'audience', 'subject_type'],
|
|
98
|
+
['client_id', 'audience'],
|
|
99
|
+
['default_for', 'audience'],
|
|
100
|
+
],
|
|
101
|
+
stripUpdateFields: ['audience', 'client_id', 'subject_type', 'is_system', 'default_for'],
|
|
91
102
|
ignoreDryRunFields: ['_clientName'],
|
|
92
103
|
});
|
|
93
104
|
}
|
|
@@ -101,6 +112,15 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
101
112
|
return;
|
|
102
113
|
// Validate each client grant
|
|
103
114
|
clientGrants.forEach((grant) => {
|
|
115
|
+
// client_id and default_for are mutually exclusive; exactly one must be present
|
|
116
|
+
const hasClientId = !!grant.client_id;
|
|
117
|
+
const hasDefaultFor = !!grant.default_for;
|
|
118
|
+
if (hasClientId && hasDefaultFor) {
|
|
119
|
+
throw new Error(`Client grant for audience "${grant.audience}": Cannot specify both "client_id" and "default_for". They are mutually exclusive.`);
|
|
120
|
+
}
|
|
121
|
+
if (!hasClientId && !hasDefaultFor) {
|
|
122
|
+
throw new Error(`Client grant for audience "${grant.audience}": One of "client_id" or "default_for" is required.`);
|
|
123
|
+
}
|
|
104
124
|
// When allow_all_scopes is true, scope should not be present
|
|
105
125
|
if (grant.allow_all_scopes === true && grant.scope && grant.scope.length > 0) {
|
|
106
126
|
throw new Error(`Client grant for client_id "${grant.client_id}" and audience "${grant.audience}": Cannot specify "scope" when "allow_all_scopes" is set to true. Remove the "scope" property or set "allow_all_scopes" to false.`);
|
|
@@ -127,7 +147,8 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
127
147
|
is_first_party: true,
|
|
128
148
|
});
|
|
129
149
|
const firstPartyClientIds = new Set(clients.map((c) => c.client_id));
|
|
130
|
-
|
|
150
|
+
// default_for grants have no client_id and are not tied to a specific client; always keep them
|
|
151
|
+
this.existing = this.existing.filter((grant) => !grant.client_id || firstPartyClientIds.has(grant.client_id));
|
|
131
152
|
}
|
|
132
153
|
return this.existing;
|
|
133
154
|
}
|
|
@@ -187,9 +208,9 @@ class ClientGrantsHandler extends default_1.default {
|
|
|
187
208
|
let filtered = list;
|
|
188
209
|
// Filter out the current client (Auth0 Management API client)
|
|
189
210
|
filtered = filtered.filter((item) => item.client_id !== currentClient);
|
|
190
|
-
// Filter out excluded clients
|
|
211
|
+
// Filter out excluded clients; default_for grants have no client_id and are never excluded
|
|
191
212
|
if (excludedClients.length) {
|
|
192
|
-
filtered = filtered.filter((item) => item.client_id
|
|
213
|
+
filtered = filtered.filter((item) => !item.client_id ||
|
|
193
214
|
![...excludedClientsByNames, ...excludedClients].includes(item.client_id));
|
|
194
215
|
}
|
|
195
216
|
// Filter out system grants
|
|
@@ -308,6 +308,16 @@ export declare const schema: {
|
|
|
308
308
|
};
|
|
309
309
|
};
|
|
310
310
|
};
|
|
311
|
+
third_party_security_mode: {
|
|
312
|
+
type: string;
|
|
313
|
+
enum: string[];
|
|
314
|
+
description: string;
|
|
315
|
+
};
|
|
316
|
+
redirection_policy: {
|
|
317
|
+
type: string;
|
|
318
|
+
enum: string[];
|
|
319
|
+
description: string;
|
|
320
|
+
};
|
|
311
321
|
oidc_logout: {
|
|
312
322
|
type: string[];
|
|
313
323
|
description: string;
|
|
@@ -312,6 +312,16 @@ exports.schema = {
|
|
|
312
312
|
},
|
|
313
313
|
},
|
|
314
314
|
},
|
|
315
|
+
third_party_security_mode: {
|
|
316
|
+
type: 'string',
|
|
317
|
+
enum: ['strict', 'permissive'],
|
|
318
|
+
description: 'Indicates the security mode for a Third Party Client, to apply new security semantics and improvements.',
|
|
319
|
+
},
|
|
320
|
+
redirection_policy: {
|
|
321
|
+
type: 'string',
|
|
322
|
+
enum: ['allow_always', 'open_redirect_protection'],
|
|
323
|
+
description: 'Determines how OAuth2/OIDC errors are handled when a non-recoverable error happens.',
|
|
324
|
+
},
|
|
315
325
|
oidc_logout: {
|
|
316
326
|
type: ['object', 'null'],
|
|
317
327
|
description: 'Configuration for OIDC backchannel logout',
|
|
@@ -436,6 +446,8 @@ class ClientHandler extends default_1.default {
|
|
|
436
446
|
'external_metadata_type',
|
|
437
447
|
'external_metadata_created_by',
|
|
438
448
|
'jwks_uri',
|
|
449
|
+
// third_party_security_mode is immutable after creation; PATCH returns 400
|
|
450
|
+
'third_party_security_mode',
|
|
439
451
|
],
|
|
440
452
|
functions: {
|
|
441
453
|
create: (client) => this.createClient(client),
|
|
@@ -16,10 +16,26 @@ export declare const schema: {
|
|
|
16
16
|
};
|
|
17
17
|
options: {
|
|
18
18
|
type: string;
|
|
19
|
+
additionalProperties: boolean;
|
|
19
20
|
properties: {
|
|
20
21
|
dpop_signing_alg: {
|
|
21
22
|
type: string;
|
|
22
23
|
};
|
|
24
|
+
token_endpoint_auth_signing_alg: {
|
|
25
|
+
type: string;
|
|
26
|
+
enum: ("RS256" | "RS512" | "PS256" | "ES256" | "ES384" | "PS384" | "RS384")[];
|
|
27
|
+
};
|
|
28
|
+
id_token_signed_response_algs: {
|
|
29
|
+
type: string;
|
|
30
|
+
items: {
|
|
31
|
+
type: string;
|
|
32
|
+
enum: ("RS256" | "RS512" | "PS256" | "ES256" | "ES384" | "PS384" | "RS384")[];
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
token_endpoint_jwtca_aud_format: {
|
|
36
|
+
type: string;
|
|
37
|
+
enum: ("issuer" | "token_endpoint")[];
|
|
38
|
+
};
|
|
23
39
|
};
|
|
24
40
|
};
|
|
25
41
|
enabled_clients: {
|
|
@@ -169,6 +185,7 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
|
|
|
169
185
|
} | {
|
|
170
186
|
options?: undefined;
|
|
171
187
|
};
|
|
188
|
+
validate(assets: Assets): Promise<void>;
|
|
172
189
|
/**
|
|
173
190
|
* Retrieves all directory provisioning configurations for all connections.
|
|
174
191
|
* @returns A promise that resolves to the configurations object, or null if not configured/supported
|
|
@@ -51,14 +51,37 @@ const utils_1 = require("../../utils");
|
|
|
51
51
|
const client_1 = require("../client");
|
|
52
52
|
const scimHandler_1 = __importDefault(require("./scimHandler"));
|
|
53
53
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
54
|
+
const validationError_1 = __importDefault(require("../../validationError"));
|
|
54
55
|
const connectionOptionsSchema = {
|
|
55
56
|
type: 'object',
|
|
57
|
+
additionalProperties: true,
|
|
56
58
|
properties: {
|
|
57
59
|
dpop_signing_alg: {
|
|
58
60
|
type: 'string',
|
|
59
61
|
},
|
|
62
|
+
token_endpoint_auth_signing_alg: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
enum: Object.values(auth0_1.Management.ConnectionTokenEndpointAuthSigningAlgEnum),
|
|
65
|
+
},
|
|
66
|
+
id_token_signed_response_algs: {
|
|
67
|
+
type: 'array',
|
|
68
|
+
items: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
enum: Object.values(auth0_1.Management.ConnectionIdTokenSignedResponseAlgEnum),
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
token_endpoint_jwtca_aud_format: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
enum: Object.values(auth0_1.Management.ConnectionTokenEndpointJwtcaAudFormatEnumOidc),
|
|
76
|
+
},
|
|
60
77
|
},
|
|
61
78
|
};
|
|
79
|
+
const oidcOktaStrategies = new Set(['oidc', 'okta']);
|
|
80
|
+
const oidcOktaOnlyConnectionOptionFields = [
|
|
81
|
+
'token_endpoint_auth_signing_alg',
|
|
82
|
+
'id_token_signed_response_algs',
|
|
83
|
+
'token_endpoint_jwtca_aud_format',
|
|
84
|
+
];
|
|
62
85
|
exports.schema = {
|
|
63
86
|
type: 'array',
|
|
64
87
|
items: {
|
|
@@ -333,6 +356,27 @@ class ConnectionsHandler extends default_1.default {
|
|
|
333
356
|
return {};
|
|
334
357
|
}
|
|
335
358
|
}
|
|
359
|
+
async validate(assets) {
|
|
360
|
+
const { connections } = assets;
|
|
361
|
+
if (!connections) {
|
|
362
|
+
await super.validate(assets);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
connections.forEach((connection) => {
|
|
366
|
+
if (!connection?.options)
|
|
367
|
+
return;
|
|
368
|
+
const strategy = connection?.strategy ?? 'unknown';
|
|
369
|
+
if (oidcOktaStrategies.has(strategy))
|
|
370
|
+
return;
|
|
371
|
+
const unsupportedFields = oidcOktaOnlyConnectionOptionFields.filter((field) => field in connection.options);
|
|
372
|
+
if (unsupportedFields.length > 0) {
|
|
373
|
+
throw new validationError_1.default(`Connection "${connection.name}": option(s) ${unsupportedFields
|
|
374
|
+
.map((field) => `"${field}"`)
|
|
375
|
+
.join(', ')} are only supported for strategies "oidc" and "okta". Found strategy "${strategy}".`);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
await super.validate(assets);
|
|
379
|
+
}
|
|
336
380
|
/**
|
|
337
381
|
* Retrieves all directory provisioning configurations for all connections.
|
|
338
382
|
* @returns A promise that resolves to the configurations object, or null if not configured/supported
|
|
@@ -74,6 +74,11 @@ export declare const schema: {
|
|
|
74
74
|
enum: string[];
|
|
75
75
|
description: string;
|
|
76
76
|
};
|
|
77
|
+
dynamic_client_registration_security_mode: {
|
|
78
|
+
type: string;
|
|
79
|
+
enum: string[];
|
|
80
|
+
description: string;
|
|
81
|
+
};
|
|
77
82
|
};
|
|
78
83
|
};
|
|
79
84
|
export interface Tenant extends Management.GetTenantSettingsResponseContent {
|
|
@@ -101,6 +101,11 @@ exports.schema = {
|
|
|
101
101
|
enum: ['audience', 'compatibility'],
|
|
102
102
|
description: 'OAuth resource parameter compatibility mode for specifying the protected resource.',
|
|
103
103
|
},
|
|
104
|
+
dynamic_client_registration_security_mode: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
enum: ['strict', 'permissive'],
|
|
107
|
+
description: 'Indicates the security mode for new clients created through the Dynamic Client Registration endpoint.',
|
|
108
|
+
},
|
|
104
109
|
},
|
|
105
110
|
};
|
|
106
111
|
const blockPageKeys = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auth0-deploy-cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.33.0",
|
|
4
4
|
"description": "A command line tool for deploying updates to your Auth0 tenant",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@clack/prompts": "1.2.0",
|
|
37
37
|
"ajv": "^6.12.6",
|
|
38
38
|
"chalk": "5.6.2",
|
|
39
|
-
"auth0": "^5.
|
|
39
|
+
"auth0": "^5.9.0",
|
|
40
40
|
"dot-prop": "^5.3.0",
|
|
41
41
|
"fs-extra": "^10.1.0",
|
|
42
42
|
"js-yaml": "^4.1.1",
|