auth0-deploy-cli 8.42.0 → 9.0.0-beta.2
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 +10 -50
- package/lib/context/directory/handlers/actionModules.js +10 -12
- package/lib/context/directory/handlers/actions.js +10 -13
- package/lib/context/directory/handlers/clients.js +0 -15
- package/lib/context/directory/handlers/databases.js +3 -4
- package/lib/context/directory/handlers/hooks.js +11 -1
- package/lib/context/directory/handlers/index.js +0 -4
- package/lib/context/directory/handlers/rules.js +11 -1
- package/lib/context/directory/index.js +6 -8
- package/lib/context/yaml/handlers/clients.js +0 -15
- package/lib/context/yaml/handlers/index.js +0 -4
- package/lib/context/yaml/index.js +6 -8
- package/lib/tools/auth0/handlers/attackProtection.js +5 -16
- package/lib/tools/auth0/handlers/clientGrants.d.ts +1 -1
- package/lib/tools/auth0/handlers/clients.d.ts +0 -46
- package/lib/tools/auth0/handlers/clients.js +1 -128
- package/lib/tools/auth0/handlers/connections.d.ts +0 -21
- package/lib/tools/auth0/handlers/connections.js +0 -16
- package/lib/tools/auth0/handlers/default.js +6 -6
- package/lib/tools/auth0/handlers/eventStreams.js +2 -10
- package/lib/tools/auth0/handlers/guardianFactorTemplates.js +4 -13
- package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +9 -11
- package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +9 -11
- package/lib/tools/auth0/handlers/hooks.js +1 -1
- package/lib/tools/auth0/handlers/index.js +0 -8
- package/lib/tools/auth0/handlers/networkACLs.d.ts +0 -18
- package/lib/tools/auth0/handlers/networkACLs.js +0 -9
- package/lib/tools/auth0/handlers/organizations.d.ts +0 -4
- package/lib/tools/auth0/handlers/organizations.js +0 -4
- package/lib/tools/auth0/handlers/prompts.js +1 -1
- package/lib/tools/auth0/handlers/rules.js +7 -19
- package/lib/tools/auth0/handlers/tenant.d.ts +0 -23
- package/lib/tools/auth0/handlers/tenant.js +0 -24
- package/lib/tools/auth0/handlers/themes.d.ts +1 -37
- package/lib/tools/auth0/handlers/themes.js +0 -64
- package/lib/tools/auth0/index.js +6 -12
- package/lib/tools/constants.d.ts +0 -1
- package/lib/tools/constants.js +0 -1
- package/lib/tools/utils.d.ts +0 -1
- package/lib/tools/utils.js +2 -25
- package/lib/types.d.ts +1 -3
- package/package.json +2 -2
- package/lib/context/directory/handlers/clientAuthCredentials.d.ts +0 -5
- package/lib/context/directory/handlers/clientAuthCredentials.js +0 -13
- package/lib/context/directory/handlers/rateLimitPolicies.d.ts +0 -6
- package/lib/context/directory/handlers/rateLimitPolicies.js +0 -53
- package/lib/context/yaml/handlers/clientAuthCredentials.d.ts +0 -5
- package/lib/context/yaml/handlers/clientAuthCredentials.js +0 -13
- package/lib/context/yaml/handlers/rateLimitPolicies.d.ts +0 -6
- package/lib/context/yaml/handlers/rateLimitPolicies.js +0 -27
- package/lib/tools/auth0/handlers/clientAuthCredentials.d.ts +0 -27
- package/lib/tools/auth0/handlers/clientAuthCredentials.js +0 -223
- package/lib/tools/auth0/handlers/clientAuthCredentialsPre.d.ts +0 -38
- package/lib/tools/auth0/handlers/clientAuthCredentialsPre.js +0 -123
- package/lib/tools/auth0/handlers/rateLimitPolicies.d.ts +0 -81
- package/lib/tools/auth0/handlers/rateLimitPolicies.js +0 -203
package/lib/tools/utils.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.shouldExcludeThirdPartyClients = exports.isForbiddenFeatureError = exports.isDeprecatedError = exports.detectInsufficientScopeError = exports.
|
|
39
|
+
exports.shouldExcludeThirdPartyClients = exports.isForbiddenFeatureError = exports.isDeprecatedError = exports.detectInsufficientScopeError = exports.stripObfuscatedFieldsFromPayload = exports.validateNoUnresolvedPlaceholders = exports.obfuscateSensitiveValues = exports.keywordReplaceStringRegExp = exports.keywordReplaceArrayRegExp = void 0;
|
|
40
40
|
exports.keywordArrayReplace = keywordArrayReplace;
|
|
41
41
|
exports.keywordStringReplace = keywordStringReplace;
|
|
42
42
|
exports.keywordReplace = keywordReplace;
|
|
@@ -309,24 +309,6 @@ const stripObfuscatedFieldsFromPayload = (data, obfuscatedFields) => {
|
|
|
309
309
|
return newAsset;
|
|
310
310
|
};
|
|
311
311
|
exports.stripObfuscatedFieldsFromPayload = stripObfuscatedFieldsFromPayload;
|
|
312
|
-
// Strips unresolved ##...## / @@...@@ placeholders before sending to Auth0.
|
|
313
|
-
// Keyword replacement runs first; any remaining placeholder means no mapping was
|
|
314
|
-
// provided. The field is dropped so Auth0 preserves its existing value.
|
|
315
|
-
const stripUnresolvedPlaceholders = (data, resourceType, resourceName) => {
|
|
316
|
-
if (data === null)
|
|
317
|
-
return data;
|
|
318
|
-
const unresolved = collectUnresolvedPlaceholders(data);
|
|
319
|
-
if (unresolved.length === 0)
|
|
320
|
-
return data;
|
|
321
|
-
const newAsset = { ...data };
|
|
322
|
-
unresolved.forEach(({ path, value }) => {
|
|
323
|
-
logger_1.default.warn(`Stripping unresolved placeholder for ${resourceType} "${resourceName}" field "${path}": ${value}. ` +
|
|
324
|
-
`To use this value, define ${value.replace(/^##|##$|^@@|@@$/g, '')} in AUTH0_KEYWORD_REPLACE_MAPPINGS. The existing value on Auth0 will be preserved.`);
|
|
325
|
-
dot_prop_1.default.delete(newAsset, path);
|
|
326
|
-
});
|
|
327
|
-
return newAsset;
|
|
328
|
-
};
|
|
329
|
-
exports.stripUnresolvedPlaceholders = stripUnresolvedPlaceholders;
|
|
330
312
|
const detectInsufficientScopeError = async (fn) => {
|
|
331
313
|
try {
|
|
332
314
|
const data = await fn();
|
|
@@ -360,12 +342,7 @@ const isDeprecatedError = (err) => {
|
|
|
360
342
|
exports.isDeprecatedError = isDeprecatedError;
|
|
361
343
|
const isForbiddenFeatureError = (err, type) => {
|
|
362
344
|
if (err.statusCode === 403) {
|
|
363
|
-
|
|
364
|
-
// human-readable message and errorCode live on the response body itself.
|
|
365
|
-
const body = err.originalError?.response?.body ?? {};
|
|
366
|
-
const message = body.message ?? err.message;
|
|
367
|
-
const errorCode = body.errorCode ?? err.errorCode ?? '';
|
|
368
|
-
logger_1.default.warn(`${message}${errorCode ? ` (${errorCode})` : ''} - Skipping ${type}`);
|
|
345
|
+
logger_1.default.warn(`${err.message};${err.errorCode ?? ''} - Skipping ${type}`);
|
|
369
346
|
return true;
|
|
370
347
|
}
|
|
371
348
|
return false;
|
package/lib/types.d.ts
CHANGED
|
@@ -21,7 +21,6 @@ import { AttackProtection } from './tools/auth0/handlers/attackProtection';
|
|
|
21
21
|
import { TokenExchangeProfile } from './tools/auth0/handlers/tokenExchangeProfiles';
|
|
22
22
|
import { RiskAssessment } from './tools/auth0/handlers/riskAssessment';
|
|
23
23
|
import { SupplementalSignals } from './tools/auth0/handlers/supplementalSignals';
|
|
24
|
-
import { RateLimitPolicy } from './tools/auth0/handlers/rateLimitPolicies';
|
|
25
24
|
import { EventStream } from './tools/auth0/handlers/eventStreams';
|
|
26
25
|
type SharedPaginationParams = {
|
|
27
26
|
checkpoint?: boolean;
|
|
@@ -160,7 +159,6 @@ export type Assets = Partial<{
|
|
|
160
159
|
userAttributeProfilesWithId: UserAttributeProfile[] | null;
|
|
161
160
|
connectionProfiles: Asset[] | null;
|
|
162
161
|
tokenExchangeProfiles: TokenExchangeProfile[] | null;
|
|
163
|
-
rateLimitPolicies: RateLimitPolicy[] | null;
|
|
164
162
|
eventStreams: EventStream[] | null;
|
|
165
163
|
}>;
|
|
166
164
|
export type CalculatedChanges = {
|
|
@@ -182,7 +180,7 @@ export type DetailedDryRunChanges = {
|
|
|
182
180
|
changes: DetailedDryRunChange[];
|
|
183
181
|
};
|
|
184
182
|
};
|
|
185
|
-
export type AssetTypes = 'rules' | 'rulesConfigs' | 'hooks' | 'pages' | 'databases' | 'clientGrants' | 'resourceServers' | 'clients' | 'connections' | 'tenant' | 'emailProvider' | 'emailTemplates' | 'guardianFactors' | 'guardianFactorProviders' | 'guardianFactorTemplates' | 'guardianPhoneFactorMessageTypes' | 'guardianPhoneFactorSelectedProvider' | 'guardianPolicies' | 'roles' | 'actions' | 'actionModules' | 'organizations' | 'triggers' | 'attackProtection' | 'riskAssessment' | 'branding' | 'phoneProviders' | 'phoneTemplates' | 'logStreams' | 'prompts' | 'customDomains' | 'themes' | 'forms' | 'flows' | 'flowVaultConnections' | 'selfServiceProfiles' | 'networkACLs' | 'userAttributeProfiles' | 'connectionProfiles' | 'tokenExchangeProfiles' | 'supplementalSignals' | '
|
|
183
|
+
export type AssetTypes = 'rules' | 'rulesConfigs' | 'hooks' | 'pages' | 'databases' | 'clientGrants' | 'resourceServers' | 'clients' | 'connections' | 'tenant' | 'emailProvider' | 'emailTemplates' | 'guardianFactors' | 'guardianFactorProviders' | 'guardianFactorTemplates' | 'guardianPhoneFactorMessageTypes' | 'guardianPhoneFactorSelectedProvider' | 'guardianPolicies' | 'roles' | 'actions' | 'actionModules' | 'organizations' | 'triggers' | 'attackProtection' | 'riskAssessment' | 'branding' | 'phoneProviders' | 'phoneTemplates' | 'logStreams' | 'prompts' | 'customDomains' | 'themes' | 'forms' | 'flows' | 'flowVaultConnections' | 'selfServiceProfiles' | 'networkACLs' | 'userAttributeProfiles' | 'connectionProfiles' | 'tokenExchangeProfiles' | 'supplementalSignals' | 'eventStreams';
|
|
186
184
|
export type KeywordMappings = {
|
|
187
185
|
[key: string]: (string | number)[] | string | number;
|
|
188
186
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auth0-deploy-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0-beta.2",
|
|
4
4
|
"description": "A command line tool for deploying updates to your Auth0 tenant",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@clack/prompts": "1.4.0",
|
|
37
37
|
"ajv": "^6.12.6",
|
|
38
|
-
"auth0": "^
|
|
38
|
+
"auth0": "^5.13.0",
|
|
39
39
|
"chalk": "5.6.2",
|
|
40
40
|
"dot-prop": "^5.3.0",
|
|
41
41
|
"fs-extra": "^10.1.0",
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { DirectoryHandler } from '.';
|
|
2
|
-
import { ParsedAsset } from '../../../types';
|
|
3
|
-
type ParsedClientAuthCredentials = ParsedAsset<'clientAuthCredentials', null>;
|
|
4
|
-
declare const clientAuthCredentialsHandler: DirectoryHandler<ParsedClientAuthCredentials>;
|
|
5
|
-
export default clientAuthCredentialsHandler;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
function parse(_context) {
|
|
4
|
-
return { clientAuthCredentials: null };
|
|
5
|
-
}
|
|
6
|
-
async function dump(_context) {
|
|
7
|
-
// Credentials are embedded inside each client's JSON file — nothing to dump separately.
|
|
8
|
-
}
|
|
9
|
-
const clientAuthCredentialsHandler = {
|
|
10
|
-
parse,
|
|
11
|
-
dump,
|
|
12
|
-
};
|
|
13
|
-
exports.default = clientAuthCredentialsHandler;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { DirectoryHandler } from '.';
|
|
2
|
-
import { ParsedAsset } from '../../../types';
|
|
3
|
-
import { RateLimitPolicy } from '../../../tools/auth0/handlers/rateLimitPolicies';
|
|
4
|
-
type ParsedRateLimitPolicies = ParsedAsset<'rateLimitPolicies', RateLimitPolicy[]>;
|
|
5
|
-
declare const rateLimitPoliciesHandler: DirectoryHandler<ParsedRateLimitPolicies>;
|
|
6
|
-
export default rateLimitPoliciesHandler;
|
|
@@ -1,53 +0,0 @@
|
|
|
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 path_1 = __importDefault(require("path"));
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const tools_1 = require("../../../tools");
|
|
9
|
-
const utils_1 = require("../../../utils");
|
|
10
|
-
function parse(context) {
|
|
11
|
-
const rateLimitPoliciesDirectory = path_1.default.join(context.filePath, tools_1.constants.RATE_LIMIT_POLICIES_DIRECTORY);
|
|
12
|
-
if (!(0, utils_1.existsMustBeDir)(rateLimitPoliciesDirectory))
|
|
13
|
-
return { rateLimitPolicies: null }; // Skip
|
|
14
|
-
const foundFiles = (0, utils_1.getFiles)(rateLimitPoliciesDirectory, ['.json']);
|
|
15
|
-
const rateLimitPolicies = foundFiles
|
|
16
|
-
.map((f) => (0, utils_1.loadJSON)(f, {
|
|
17
|
-
mappings: context.mappings,
|
|
18
|
-
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
19
|
-
}))
|
|
20
|
-
.filter((p) => Object.keys(p).length > 0);
|
|
21
|
-
return { rateLimitPolicies };
|
|
22
|
-
}
|
|
23
|
-
async function dump(context) {
|
|
24
|
-
const { rateLimitPolicies } = context.assets;
|
|
25
|
-
if (!rateLimitPolicies)
|
|
26
|
-
return; // Skip, nothing to dump
|
|
27
|
-
const rateLimitPoliciesDirectory = path_1.default.join(context.filePath, tools_1.constants.RATE_LIMIT_POLICIES_DIRECTORY);
|
|
28
|
-
fs_extra_1.default.ensureDirSync(rateLimitPoliciesDirectory);
|
|
29
|
-
const removeKeysFromOutput = ['id', 'created_at', 'updated_at'];
|
|
30
|
-
const expectedFiles = new Set();
|
|
31
|
-
rateLimitPolicies.forEach((policy) => {
|
|
32
|
-
const policyToWrite = { ...policy };
|
|
33
|
-
removeKeysFromOutput.forEach((key) => {
|
|
34
|
-
delete policyToWrite[key];
|
|
35
|
-
});
|
|
36
|
-
const fileName = (0, utils_1.sanitize)(policy.consumer_selector);
|
|
37
|
-
const filePath = path_1.default.join(rateLimitPoliciesDirectory, `${fileName}.json`);
|
|
38
|
-
expectedFiles.add(`${fileName}.json`);
|
|
39
|
-
(0, utils_1.dumpJSON)(filePath, policyToWrite);
|
|
40
|
-
});
|
|
41
|
-
// Remove files for policies that no longer exist in the tenant
|
|
42
|
-
for (const existing of fs_extra_1.default.readdirSync(rateLimitPoliciesDirectory)) {
|
|
43
|
-
const fullPath = path_1.default.join(rateLimitPoliciesDirectory, existing);
|
|
44
|
-
if (fs_extra_1.default.statSync(fullPath).isFile() && !expectedFiles.has(existing)) {
|
|
45
|
-
fs_extra_1.default.removeSync(fullPath);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
const rateLimitPoliciesHandler = {
|
|
50
|
-
parse,
|
|
51
|
-
dump,
|
|
52
|
-
};
|
|
53
|
-
exports.default = rateLimitPoliciesHandler;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { YAMLHandler } from '.';
|
|
2
|
-
import { ParsedAsset } from '../../../types';
|
|
3
|
-
type ParsedClientAuthCredentials = ParsedAsset<'clientAuthCredentials', null>;
|
|
4
|
-
declare const clientAuthCredentialsHandler: YAMLHandler<ParsedClientAuthCredentials>;
|
|
5
|
-
export default clientAuthCredentialsHandler;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
async function parse(_context) {
|
|
4
|
-
return { clientAuthCredentials: null };
|
|
5
|
-
}
|
|
6
|
-
async function dump(_context) {
|
|
7
|
-
return { clientAuthCredentials: null };
|
|
8
|
-
}
|
|
9
|
-
const clientAuthCredentialsHandler = {
|
|
10
|
-
parse,
|
|
11
|
-
dump,
|
|
12
|
-
};
|
|
13
|
-
exports.default = clientAuthCredentialsHandler;
|
|
@@ -1,6 +0,0 @@
|
|
|
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;
|
|
@@ -1,27 +0,0 @@
|
|
|
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;
|
|
@@ -1,27 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,223 +0,0 @@
|
|
|
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
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// pem is the opt-in signal: present field with no pem = skip (safe export→deploy);
|
|
94
|
-
// absent field = fall through to delete path (intentional removal).
|
|
95
|
-
if (desired.length === 0 && client.client_authentication_methods)
|
|
96
|
-
continue;
|
|
97
|
-
let existing = [];
|
|
98
|
-
try {
|
|
99
|
-
existing = (await this.client.clients.credentials.list(clientId));
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
logger_1.default.warn(`clientAuthCredentials: failed to list credentials for client "${clientName}": ${err}`);
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
// No credentials in Auth0 and none desired — nothing to do.
|
|
106
|
-
if (existing.length === 0 && desired.length === 0)
|
|
107
|
-
continue;
|
|
108
|
-
// Warn if duplicate names exist in Auth0 — we can't match safely
|
|
109
|
-
const existingNames = existing.map((c) => c.name);
|
|
110
|
-
const duplicateNames = existingNames.filter((n, i) => existingNames.indexOf(n) !== i);
|
|
111
|
-
if (duplicateNames.length > 0) {
|
|
112
|
-
logger_1.default.warn(`clientAuthCredentials: client "${clientName}" has duplicate credential names in Auth0 [${duplicateNames.join(', ')}] — skipping credential reconciliation for this client`);
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
const existingByName = new Map(existing.map((c) => [c.name, c]));
|
|
116
|
-
const desiredNames = new Set(desired.map((d) => d.name));
|
|
117
|
-
const toCreate = desired.filter((d) => !existingByName.has(d.name));
|
|
118
|
-
const toDelete = existing.filter((e) => !desiredNames.has(e.name));
|
|
119
|
-
// Creates first — Auth0 allows max 2 credentials; both must exist simultaneously during rotation
|
|
120
|
-
// Track name→id so we can correctly attribute each created credential to its method later.
|
|
121
|
-
const createdIdByName = new Map();
|
|
122
|
-
for (const cred of toCreate) {
|
|
123
|
-
try {
|
|
124
|
-
const created = await this.client.clients.credentials.create(clientId, {
|
|
125
|
-
name: cred.name,
|
|
126
|
-
pem: cred.pem,
|
|
127
|
-
credential_type: cred.credential_type,
|
|
128
|
-
});
|
|
129
|
-
logger_1.default.info(`clientAuthCredentials: created credential "${cred.name}" on client "${clientName}"`);
|
|
130
|
-
createdIdByName.set(cred.name, created.id);
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
logger_1.default.warn(`clientAuthCredentials: failed to create credential "${cred.name}" on client "${clientName}": ${err}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// Re-wire client_authentication_methods with final IDs (kept + newly created).
|
|
137
|
-
// This must happen BEFORE deletes — Auth0 rejects deleting a credential that
|
|
138
|
-
// is still referenced in client_authentication_methods.
|
|
139
|
-
const keptIds = existing.filter((e) => desiredNames.has(e.name)).map((e) => e.id);
|
|
140
|
-
const createdIds = [...createdIdByName.values()];
|
|
141
|
-
const finalIds = [...keptIds, ...createdIds];
|
|
142
|
-
let updatedAuthMethods = null;
|
|
143
|
-
if (finalIds.length > 0) {
|
|
144
|
-
// Build method→ids map. For kept credentials use Auth0's stored type to infer method;
|
|
145
|
-
// for created credentials use the method from the user's config entry (by name).
|
|
146
|
-
const methodToIds = {};
|
|
147
|
-
for (const id of finalIds) {
|
|
148
|
-
const fromExisting = existing.find((e) => e.id === id);
|
|
149
|
-
const createdName = [...createdIdByName.entries()].find(([, cid]) => cid === id)?.[0];
|
|
150
|
-
const fromDesired = createdName ? desired.find((d) => d.name === createdName) : undefined;
|
|
151
|
-
const methodKey = (fromExisting && this.inferMethodFromType(fromExisting.credential_type)) ||
|
|
152
|
-
(fromDesired && fromDesired.method) ||
|
|
153
|
-
Object.keys(client.client_authentication_methods)[0];
|
|
154
|
-
if (!methodToIds[methodKey])
|
|
155
|
-
methodToIds[methodKey] = [];
|
|
156
|
-
methodToIds[methodKey].push(id);
|
|
157
|
-
}
|
|
158
|
-
updatedAuthMethods = {};
|
|
159
|
-
for (const [methodKey, ids] of Object.entries(methodToIds)) {
|
|
160
|
-
updatedAuthMethods[methodKey] = { credentials: ids.map((id) => ({ id })) };
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Always update client_authentication_methods before deletes:
|
|
164
|
-
// - When finalIds > 0: re-wire to the remaining credentials
|
|
165
|
-
// - When finalIds === 0 but toDelete > 0: set to null so Auth0 de-references them first
|
|
166
|
-
if (finalIds.length > 0 || toDelete.length > 0) {
|
|
167
|
-
try {
|
|
168
|
-
const payload = {
|
|
169
|
-
client_authentication_methods: updatedAuthMethods ?? null,
|
|
170
|
-
};
|
|
171
|
-
// Auth0 requires token_endpoint_auth_method: null when setting client_authentication_methods,
|
|
172
|
-
// and a non-null value when clearing it.
|
|
173
|
-
if (updatedAuthMethods) {
|
|
174
|
-
payload.token_endpoint_auth_method = null;
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
payload.token_endpoint_auth_method =
|
|
178
|
-
client.token_endpoint_auth_method || 'client_secret_post';
|
|
179
|
-
}
|
|
180
|
-
await this.client.clients.update(clientId, payload);
|
|
181
|
-
logger_1.default.info(`clientAuthCredentials: updated client_authentication_methods on client "${clientName}"`);
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
logger_1.default.warn(`clientAuthCredentials: failed to update client_authentication_methods on client "${clientName}": ${err}`);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
// Deletes last — after client_authentication_methods no longer references them
|
|
188
|
-
if (toDelete.length > 0) {
|
|
189
|
-
if (!allowDelete) {
|
|
190
|
-
logger_1.default.warn(`clientAuthCredentials: the following credentials on client "${clientName}" would be deleted but AUTH0_ALLOW_DELETE is not set:\n${toDelete
|
|
191
|
-
.map((c) => ` ${c.name} (${c.id})`)
|
|
192
|
-
.join('\n')}`);
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
for (const cred of toDelete) {
|
|
196
|
-
try {
|
|
197
|
-
await this.client.clients.credentials.delete(clientId, cred.id);
|
|
198
|
-
logger_1.default.info(`clientAuthCredentials: deleted credential "${cred.name}" on client "${clientName}"`);
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
logger_1.default.warn(`clientAuthCredentials: failed to delete credential "${cred.name}" on client "${clientName}": ${err}`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
inferCredentialType(methodKey) {
|
|
209
|
-
const map = {
|
|
210
|
-
private_key_jwt: 'public_key',
|
|
211
|
-
self_signed_tls_client_auth: 'x509_cert',
|
|
212
|
-
tls_client_auth: 'cert_subject_dn',
|
|
213
|
-
};
|
|
214
|
-
return map[methodKey] || 'public_key';
|
|
215
|
-
}
|
|
216
|
-
inferMethodFromType(credentialType) {
|
|
217
|
-
return CREDENTIAL_TYPE_TO_METHOD[credentialType] || 'private_key_jwt';
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
exports.default = ClientAuthCredentialsHandler;
|
|
221
|
-
__decorate([
|
|
222
|
-
(0, default_1.order)('70')
|
|
223
|
-
], ClientAuthCredentialsHandler.prototype, "processChanges", null);
|
|
@@ -1,38 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
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
|
-
/**
|
|
21
|
-
* Handles full credential deletion BEFORE clients.ts (order 50) patches the client.
|
|
22
|
-
*
|
|
23
|
-
* Auth0 rejects a client PATCH containing token_endpoint_auth_method while that client still
|
|
24
|
-
* has active credentials. By running at order 40, this handler clears credentials (and
|
|
25
|
-
* client_authentication_methods) for any client where the field is absent from config, so
|
|
26
|
-
* that clients.ts can safely include token_endpoint_auth_method in its PATCH.
|
|
27
|
-
*
|
|
28
|
-
* Partial deletion (some credentials removed, others kept) is handled by
|
|
29
|
-
* ClientAuthCredentialsHandler at order 70, because those clients still have
|
|
30
|
-
* client_authentication_methods present in config and clients.ts strips token_endpoint_auth_method
|
|
31
|
-
* for them anyway.
|
|
32
|
-
*/
|
|
33
|
-
class ClientAuthCredentialsPreHandler {
|
|
34
|
-
constructor({ client, config }) {
|
|
35
|
-
// Same type as ClientAuthCredentialsHandler so AUTH0_EXCLUDED / AUTH0_INCLUDED_ONLY
|
|
36
|
-
// applies to both handlers together without needing a separate AssetTypes entry.
|
|
37
|
-
this.type = 'clientAuthCredentials';
|
|
38
|
-
this.existing = null;
|
|
39
|
-
this.client = client;
|
|
40
|
-
this.config = config;
|
|
41
|
-
}
|
|
42
|
-
async getType() {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
async load() {
|
|
46
|
-
return { [this.type]: null };
|
|
47
|
-
}
|
|
48
|
-
async validate() {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
// Deploy-only pre-pass handler with no dry-run representation. Return empty changes so the
|
|
52
|
-
// dry-run loop (which calls dryRunChanges on every registered handler) does not crash.
|
|
53
|
-
async dryRunChanges(_assets) {
|
|
54
|
-
return { del: [], create: [], conflicts: [], update: [] };
|
|
55
|
-
}
|
|
56
|
-
async processChanges(assets) {
|
|
57
|
-
const { clients } = assets;
|
|
58
|
-
if (!clients)
|
|
59
|
-
return;
|
|
60
|
-
const allowDelete = this.config('AUTH0_ALLOW_DELETE') === true || this.config('AUTH0_ALLOW_DELETE') === 'true';
|
|
61
|
-
// When allowDelete is false no deletion will happen — skip entirely to avoid
|
|
62
|
-
// unnecessary GET /credentials calls for every client.
|
|
63
|
-
if (!allowDelete)
|
|
64
|
-
return;
|
|
65
|
-
// Only act on clients where client_authentication_methods is absent from config.
|
|
66
|
-
// Absent = intentional full deletion. Clients with the field present (including partial
|
|
67
|
-
// deletion) are fully handled by ClientAuthCredentialsHandler at order 70.
|
|
68
|
-
const deletionCandidates = clients.filter((c) => c.client_authentication_methods === undefined);
|
|
69
|
-
if (deletionCandidates.length === 0)
|
|
70
|
-
return;
|
|
71
|
-
// In directory mode client_id is stripped. Resolve names once for all candidates.
|
|
72
|
-
const needsNameResolution = deletionCandidates.some((c) => !c.client_id);
|
|
73
|
-
let clientIdByName = new Map();
|
|
74
|
-
if (needsNameResolution) {
|
|
75
|
-
const existingClients = await (0, client_1.paginate)(this.client.clients.list, {
|
|
76
|
-
paginate: true,
|
|
77
|
-
is_global: false,
|
|
78
|
-
});
|
|
79
|
-
clientIdByName = new Map(existingClients.map((c) => [c.name, c.client_id]));
|
|
80
|
-
}
|
|
81
|
-
for (const client of deletionCandidates) {
|
|
82
|
-
const clientId = client.client_id || (client.name && clientIdByName.get(client.name));
|
|
83
|
-
if (!clientId)
|
|
84
|
-
continue;
|
|
85
|
-
const clientName = client.name || clientId;
|
|
86
|
-
let existing = [];
|
|
87
|
-
try {
|
|
88
|
-
existing = (await this.client.clients.credentials.list(clientId));
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
logger_1.default.warn(`clientAuthCredentials: failed to list credentials for client "${clientName}": ${err}`);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (existing.length === 0)
|
|
95
|
-
continue;
|
|
96
|
-
// De-reference credentials from client_authentication_methods before deleting them —
|
|
97
|
-
// Auth0 rejects deleting a credential that is still referenced.
|
|
98
|
-
try {
|
|
99
|
-
await this.client.clients.update(clientId, {
|
|
100
|
-
client_authentication_methods: null,
|
|
101
|
-
token_endpoint_auth_method: client.token_endpoint_auth_method || 'client_secret_post',
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
catch (err) {
|
|
105
|
-
logger_1.default.warn(`clientAuthCredentials: failed to clear client_authentication_methods on client "${clientName}": ${err}`);
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
for (const cred of existing) {
|
|
109
|
-
try {
|
|
110
|
-
await this.client.clients.credentials.delete(clientId, cred.id);
|
|
111
|
-
logger_1.default.info(`clientAuthCredentials: deleted credential "${cred.name}" on client "${clientName}"`);
|
|
112
|
-
}
|
|
113
|
-
catch (err) {
|
|
114
|
-
logger_1.default.warn(`clientAuthCredentials: failed to delete credential "${cred.name}" on client "${clientName}": ${err}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
exports.default = ClientAuthCredentialsPreHandler;
|
|
121
|
-
__decorate([
|
|
122
|
-
(0, default_1.order)('40')
|
|
123
|
-
], ClientAuthCredentialsPreHandler.prototype, "processChanges", null);
|