@sitecore-content-sdk/core 0.2.0-beta.13 → 0.2.0-beta.14
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/dist/cjs/constants.js +3 -2
- package/dist/cjs/tools/auth/flow.js +70 -0
- package/dist/cjs/tools/auth/index.js +20 -0
- package/dist/cjs/tools/auth/models.js +2 -0
- package/dist/cjs/tools/auth/renewal.js +82 -0
- package/dist/cjs/tools/auth/tenant-state.js +110 -0
- package/dist/cjs/tools/auth/tenant-store.js +243 -0
- package/dist/cjs/tools/index.js +26 -3
- package/dist/esm/constants.js +2 -1
- package/dist/esm/tools/auth/flow.js +67 -0
- package/dist/esm/tools/auth/index.js +4 -0
- package/dist/esm/tools/auth/models.js +1 -0
- package/dist/esm/tools/auth/renewal.js +77 -0
- package/dist/esm/tools/auth/tenant-state.js +73 -0
- package/dist/esm/tools/auth/tenant-store.js +206 -0
- package/dist/esm/tools/index.js +3 -1
- package/package.json +2 -2
- package/types/constants.d.ts +2 -1
- package/types/tools/auth/flow.d.ts +27 -0
- package/types/tools/auth/index.d.ts +4 -0
- package/types/tools/auth/models.d.ts +94 -0
- package/types/tools/auth/renewal.d.ts +22 -0
- package/types/tools/auth/tenant-state.d.ts +21 -0
- package/types/tools/auth/tenant-store.d.ts +63 -0
- package/types/tools/index.d.ts +3 -1
- package/dist/cjs/tools/auth/fetch-bearer-token.js +0 -43
- package/dist/esm/tools/auth/fetch-bearer-token.js +0 -36
- package/types/tools/auth/fetch-bearer-token.d.ts +0 -13
package/dist/cjs/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_SITECORE_AUTH_AUDIENCE = exports.
|
|
3
|
+
exports.DEFAULT_SITECORE_AUTH_BASE_URL = exports.DEFAULT_SITECORE_AUTH_AUDIENCE = exports.DEFAULT_SITECORE_AUTH_DOMAIN = exports.HIDDEN_RENDERING_NAME = exports.SITECORE_EDGE_URL_DEFAULT = exports.siteNameError = exports.SitecoreTemplateId = void 0;
|
|
4
4
|
var SitecoreTemplateId;
|
|
5
5
|
(function (SitecoreTemplateId) {
|
|
6
6
|
// /sitecore/templates/Foundation/JavaScript Services/App
|
|
@@ -11,5 +11,6 @@ var SitecoreTemplateId;
|
|
|
11
11
|
exports.siteNameError = 'The siteName cannot be empty';
|
|
12
12
|
exports.SITECORE_EDGE_URL_DEFAULT = 'https://edge-platform.sitecorecloud.io';
|
|
13
13
|
exports.HIDDEN_RENDERING_NAME = 'Hidden Rendering';
|
|
14
|
-
exports.
|
|
14
|
+
exports.DEFAULT_SITECORE_AUTH_DOMAIN = 'https://auth.sitecorecloud.io';
|
|
15
15
|
exports.DEFAULT_SITECORE_AUTH_AUDIENCE = 'https://api.sitecorecloud.io';
|
|
16
|
+
exports.DEFAULT_SITECORE_AUTH_BASE_URL = 'https://edge-platform.sitecorecloud.io/cs/api';
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unitMocks = exports.clientCredentialsFlow = void 0;
|
|
4
|
+
const tenant_store_1 = require("./tenant-store");
|
|
5
|
+
const constants_1 = require("../../constants");
|
|
6
|
+
const GRANT_TYPE = 'client_credentials';
|
|
7
|
+
/**
|
|
8
|
+
* Performs the OAuth 2.0 client credentials flow to obtain a JWT access token
|
|
9
|
+
* from the Sitecore Identity Provider using the provided client credentials.
|
|
10
|
+
* @param {object} [args] - The arguments for client credentials flow
|
|
11
|
+
* @param {string} [args.clientId] - The client ID registered with Sitecore Identity
|
|
12
|
+
* @param {string} [args.clientSecret] - The client secret associated with the client ID
|
|
13
|
+
* @param {string} [args.organizationId] - The ID of the organization the client belongs to
|
|
14
|
+
* @param {string} [args.tenantId] - The tenant ID representing the specific Sitecore environment
|
|
15
|
+
* @param {string} [args.audience] - The API audience the token is intended for. Defaults to `constants.DEFAULT_SITECORE_AUTH_AUDIENCE`
|
|
16
|
+
* @param {string} [args.authority] - The auth server base URL. Defaults to `constants.DEFAULT_SITECORE_AUTH_DOMAIN`
|
|
17
|
+
* @param {string} [args.baseUrl] - The base URL for the API, used to construct the audience. Defaults to `constants.DEFAULT_SITECORE_AUTH_BASE_URL`
|
|
18
|
+
* @returns A Promise that resolves to the access token response (including access token, token type, expiry, etc.)
|
|
19
|
+
* @throws Will log and exit the process if the request fails or returns a non-OK status
|
|
20
|
+
*/
|
|
21
|
+
exports.clientCredentialsFlow = _clientCredentialsFlow;
|
|
22
|
+
// mock setup for unit tests to make sinon happy and mock-able with esbuild/tsx
|
|
23
|
+
// https://sinonjs.org/how-to/typescript-swc/
|
|
24
|
+
// This, plus the `_` names make the exports writable for sinon
|
|
25
|
+
exports.unitMocks = {
|
|
26
|
+
set clientCredentialsFlow(mockImplementation) {
|
|
27
|
+
exports.clientCredentialsFlow = mockImplementation;
|
|
28
|
+
},
|
|
29
|
+
get clientCredentialsFlow() {
|
|
30
|
+
return _clientCredentialsFlow;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
async function _clientCredentialsFlow({ clientId, clientSecret, organizationId, tenantId, audience = constants_1.DEFAULT_SITECORE_AUTH_AUDIENCE, authority = constants_1.DEFAULT_SITECORE_AUTH_DOMAIN, baseUrl = constants_1.DEFAULT_SITECORE_AUTH_BASE_URL, }) {
|
|
34
|
+
const params = new URLSearchParams({
|
|
35
|
+
client_id: clientId,
|
|
36
|
+
client_secret: clientSecret !== null && clientSecret !== void 0 ? clientSecret : '',
|
|
37
|
+
organization_id: organizationId !== null && organizationId !== void 0 ? organizationId : '',
|
|
38
|
+
tenant_id: tenantId !== null && tenantId !== void 0 ? tenantId : '',
|
|
39
|
+
audience,
|
|
40
|
+
grant_type: GRANT_TYPE,
|
|
41
|
+
baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : '',
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetch(`${authority}/oauth/token`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
47
|
+
body: params.toString(),
|
|
48
|
+
});
|
|
49
|
+
const data = await response.json();
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(data.error_description || data.error || 'Error during client credentials flow');
|
|
52
|
+
}
|
|
53
|
+
const decodedPayload = (0, tenant_store_1.decodeJwtPayload)(data.access_token) || {};
|
|
54
|
+
if (!(decodedPayload === null || decodedPayload === void 0 ? void 0 : decodedPayload.tokenTenantId) || !decodedPayload.tokenOrgId) {
|
|
55
|
+
throw new Error('\n Token is missing required claims tenant_id or org_id.');
|
|
56
|
+
}
|
|
57
|
+
const { tokenTenantId, tokenOrgId, tokenTenantName } = decodedPayload;
|
|
58
|
+
if (tenantId && tenantId !== tokenTenantId) {
|
|
59
|
+
throw new Error('\n Mismatch: Provided tenant ID does not match claims tenant ID.');
|
|
60
|
+
}
|
|
61
|
+
if (organizationId && organizationId !== tokenOrgId) {
|
|
62
|
+
throw new Error('\n Mismatch: Provided organization ID does not match claims organization ID.');
|
|
63
|
+
}
|
|
64
|
+
return { data, tokenOrgId, tokenTenantId, tokenTenantName, accessToken: data.access_token };
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error('\n Error during client credentials flow:', error instanceof Error ? error.message : error);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeTenantInfo = exports.getAllTenantsInfo = exports.readTenantInfo = exports.deleteTenantAuthInfo = exports.readTenantAuthInfo = exports.writeTenantAuthInfo = exports.clearActiveTenant = exports.setActiveTenant = exports.getActiveTenant = exports.validateAuthInfo = exports.renewAuthIfExpired = exports.renewClientToken = exports.clientCredentialsFlow = void 0;
|
|
4
|
+
var flow_1 = require("./flow");
|
|
5
|
+
Object.defineProperty(exports, "clientCredentialsFlow", { enumerable: true, get: function () { return flow_1.clientCredentialsFlow; } });
|
|
6
|
+
var renewal_1 = require("./renewal");
|
|
7
|
+
Object.defineProperty(exports, "renewClientToken", { enumerable: true, get: function () { return renewal_1.renewClientToken; } });
|
|
8
|
+
Object.defineProperty(exports, "renewAuthIfExpired", { enumerable: true, get: function () { return renewal_1.renewAuthIfExpired; } });
|
|
9
|
+
Object.defineProperty(exports, "validateAuthInfo", { enumerable: true, get: function () { return renewal_1.validateAuthInfo; } });
|
|
10
|
+
var tenant_state_1 = require("./tenant-state");
|
|
11
|
+
Object.defineProperty(exports, "getActiveTenant", { enumerable: true, get: function () { return tenant_state_1.getActiveTenant; } });
|
|
12
|
+
Object.defineProperty(exports, "setActiveTenant", { enumerable: true, get: function () { return tenant_state_1.setActiveTenant; } });
|
|
13
|
+
Object.defineProperty(exports, "clearActiveTenant", { enumerable: true, get: function () { return tenant_state_1.clearActiveTenant; } });
|
|
14
|
+
var tenant_store_1 = require("./tenant-store");
|
|
15
|
+
Object.defineProperty(exports, "writeTenantAuthInfo", { enumerable: true, get: function () { return tenant_store_1.writeTenantAuthInfo; } });
|
|
16
|
+
Object.defineProperty(exports, "readTenantAuthInfo", { enumerable: true, get: function () { return tenant_store_1.readTenantAuthInfo; } });
|
|
17
|
+
Object.defineProperty(exports, "deleteTenantAuthInfo", { enumerable: true, get: function () { return tenant_store_1.deleteTenantAuthInfo; } });
|
|
18
|
+
Object.defineProperty(exports, "readTenantInfo", { enumerable: true, get: function () { return tenant_store_1.readTenantInfo; } });
|
|
19
|
+
Object.defineProperty(exports, "getAllTenantsInfo", { enumerable: true, get: function () { return tenant_store_1.getAllTenantsInfo; } });
|
|
20
|
+
Object.defineProperty(exports, "writeTenantInfo", { enumerable: true, get: function () { return tenant_store_1.writeTenantInfo; } });
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateAuthInfo = validateAuthInfo;
|
|
4
|
+
exports.renewClientToken = renewClientToken;
|
|
5
|
+
exports.renewAuthIfExpired = renewAuthIfExpired;
|
|
6
|
+
const tenant_state_1 = require("./tenant-state");
|
|
7
|
+
const flow_1 = require("./flow");
|
|
8
|
+
const tenant_store_1 = require("./tenant-store");
|
|
9
|
+
/**
|
|
10
|
+
* Validates whether a given auth config is still valid (i.e., not expired).
|
|
11
|
+
* @param {TenantAuth} authInfo - The tenant auth configuration.
|
|
12
|
+
* @returns True if the token is still valid, false if expired.
|
|
13
|
+
*/
|
|
14
|
+
function validateAuthInfo(authInfo) {
|
|
15
|
+
const now = new Date();
|
|
16
|
+
const expiry = new Date(authInfo.expires_at);
|
|
17
|
+
return now < expiry;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Renews the token for a given tenant using stored credentials.
|
|
21
|
+
* @param {TenantAuth} authInfo - Current authentication info for the tenant.
|
|
22
|
+
* @param {TenantInfo} tenantInfo - Public metadata about the tenant (e.g., clientId).
|
|
23
|
+
* @returns Promise<void>
|
|
24
|
+
* @throws If credentials are missing or renewal fails.
|
|
25
|
+
*/
|
|
26
|
+
async function renewClientToken(authInfo, tenantInfo) {
|
|
27
|
+
const result = await (0, flow_1.clientCredentialsFlow)({
|
|
28
|
+
clientId: tenantInfo.clientId,
|
|
29
|
+
clientSecret: authInfo.clientSecret,
|
|
30
|
+
organizationId: tenantInfo.organizationId,
|
|
31
|
+
tenantId: tenantInfo.tenantId,
|
|
32
|
+
audience: tenantInfo.audience,
|
|
33
|
+
authority: tenantInfo.authority,
|
|
34
|
+
baseUrl: tenantInfo.baseUrl,
|
|
35
|
+
});
|
|
36
|
+
const tenantId = tenantInfo.tenantId;
|
|
37
|
+
await (0, tenant_store_1.writeTenantAuthInfo)(tenantId, {
|
|
38
|
+
clientSecret: authInfo.clientSecret,
|
|
39
|
+
access_token: result.data.access_token,
|
|
40
|
+
expires_in: result.data.expires_in,
|
|
41
|
+
expires_at: new Date(Date.now() + result.data.expires_in * 1000).toISOString(),
|
|
42
|
+
});
|
|
43
|
+
console.info(`\n Token for tenant ${tenantId} renewed.`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Ensures a valid token exists, renews it if expired.
|
|
47
|
+
* Returns tenant context if successful, otherwise null.
|
|
48
|
+
*/
|
|
49
|
+
async function renewAuthIfExpired() {
|
|
50
|
+
const tenantId = (0, tenant_state_1.getActiveTenant)();
|
|
51
|
+
if (!tenantId)
|
|
52
|
+
return null;
|
|
53
|
+
const authInfo = await (0, tenant_store_1.readTenantAuthInfo)(tenantId);
|
|
54
|
+
if (!authInfo)
|
|
55
|
+
return null;
|
|
56
|
+
const isValid = validateAuthInfo(authInfo);
|
|
57
|
+
if (isValid) {
|
|
58
|
+
return { tenantId };
|
|
59
|
+
}
|
|
60
|
+
const tenantInfo = await (0, tenant_store_1.readTenantInfo)(tenantId);
|
|
61
|
+
if (!tenantInfo)
|
|
62
|
+
return null;
|
|
63
|
+
console.info(`\n Token for tenant ${tenantId} is expired. Renewing...`);
|
|
64
|
+
try {
|
|
65
|
+
if (authInfo.clientSecret) {
|
|
66
|
+
await renewClientToken(authInfo, tenantInfo);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// <TODO>: Implement Device auth token renewal.
|
|
70
|
+
throw new Error('\n Please use clientSecret for authentication.');
|
|
71
|
+
}
|
|
72
|
+
return { tenantId };
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
console.error(`\n Failed to renew token for tenant '${tenantId}'`);
|
|
76
|
+
console.warn(`\n Cleaning up stale authentication data for tenant '${tenantId}'...`);
|
|
77
|
+
await (0, tenant_store_1.deleteTenantAuthInfo)(tenantId);
|
|
78
|
+
(0, tenant_state_1.clearActiveTenant)();
|
|
79
|
+
console.info('\n You will need to login again to re-authenticate.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.unitMocks = exports.clearActiveTenant = exports.getActiveTenant = void 0;
|
|
37
|
+
exports.setActiveTenant = setActiveTenant;
|
|
38
|
+
/* eslint-disable jsdoc/require-jsdoc */
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const configDir = path.join(os.homedir(), '.sitecore', 'sitecore-tools');
|
|
43
|
+
const settingsFile = path.join(configDir, 'settings.json');
|
|
44
|
+
/**
|
|
45
|
+
* Gets the ID of the currently active tenant from settings.json.
|
|
46
|
+
* @returns The active tenant ID if present, otherwise null.
|
|
47
|
+
*/
|
|
48
|
+
exports.getActiveTenant = _getActiveTenant;
|
|
49
|
+
/**
|
|
50
|
+
* Clears the currently active tenant from settings.json by deleting the file.
|
|
51
|
+
*/
|
|
52
|
+
exports.clearActiveTenant = _clearActiveTenant;
|
|
53
|
+
// mock setup for unit tests to make sinon happy and mock-able with esbuild/tsx
|
|
54
|
+
// https://sinonjs.org/how-to/typescript-swc/
|
|
55
|
+
// This, plus the `_` names make the exports writable for sinon
|
|
56
|
+
exports.unitMocks = {
|
|
57
|
+
set clearActiveTenant(mockImplementation) {
|
|
58
|
+
exports.clearActiveTenant = mockImplementation;
|
|
59
|
+
},
|
|
60
|
+
get clearActiveTenant() {
|
|
61
|
+
return _clearActiveTenant;
|
|
62
|
+
},
|
|
63
|
+
set getActiveTenant(mockImplementation) {
|
|
64
|
+
exports.getActiveTenant = mockImplementation;
|
|
65
|
+
},
|
|
66
|
+
get getActiveTenant() {
|
|
67
|
+
return _getActiveTenant;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
function _getActiveTenant() {
|
|
71
|
+
var _a;
|
|
72
|
+
if (!fs.existsSync(settingsFile)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const content = fs.readFileSync(settingsFile, 'utf-8');
|
|
77
|
+
const data = JSON.parse(content);
|
|
78
|
+
return (_a = data.activeTenant) !== null && _a !== void 0 ? _a : null;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error(`\n Failed to read active tenant: ${error.message}`);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Sets the currently active tenant by writing to settings.json.
|
|
87
|
+
* @param {string} tenantId - The tenant ID to set as active.
|
|
88
|
+
*/
|
|
89
|
+
function setActiveTenant(tenantId) {
|
|
90
|
+
try {
|
|
91
|
+
if (!fs.existsSync(configDir)) {
|
|
92
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
const data = { activeTenant: tenantId };
|
|
95
|
+
fs.writeFileSync(settingsFile, JSON.stringify(data, null, 2));
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error(`\n Failed to set active tenant '${tenantId}': ${error.message}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function _clearActiveTenant() {
|
|
102
|
+
try {
|
|
103
|
+
if (fs.existsSync(settingsFile)) {
|
|
104
|
+
fs.unlinkSync(settingsFile);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error(`\n Failed to clear active tenant: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.unitMocks = exports.getAllTenantsInfo = exports.deleteTenantAuthInfo = exports.readTenantInfo = exports.writeTenantInfo = exports.readTenantAuthInfo = exports.writeTenantAuthInfo = exports.decodeJwtPayload = void 0;
|
|
37
|
+
exports.getTenantPath = getTenantPath;
|
|
38
|
+
/* eslint-disable jsdoc/require-jsdoc */
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const CLAIMS = 'https://auth.sitecorecloud.io/claims';
|
|
43
|
+
const rootDir = path.join(os.homedir(), '.sitecore', 'sitecore-tools');
|
|
44
|
+
/**
|
|
45
|
+
* Decodes a JWT without verifying its signature.
|
|
46
|
+
* @param {string} token - The access token string.
|
|
47
|
+
* @returns Decoded payload object or null if invalid.
|
|
48
|
+
*/
|
|
49
|
+
exports.decodeJwtPayload = _decodeJwtPayload;
|
|
50
|
+
/**
|
|
51
|
+
* Write the authentication configuration for a tenant.
|
|
52
|
+
* @param {string} tenantId - The tenant ID.
|
|
53
|
+
* @param {TenantAuth} authInfo - The tenant's auth data.
|
|
54
|
+
*/
|
|
55
|
+
exports.writeTenantAuthInfo = _writeTenantAuthInfo;
|
|
56
|
+
/**
|
|
57
|
+
* Read the authentication configuration for a tenant.
|
|
58
|
+
* @param {string} tenantId - The tenant ID.
|
|
59
|
+
* @returns Parsed auth config or null if not found or failed to read.
|
|
60
|
+
*/
|
|
61
|
+
exports.readTenantAuthInfo = _readTenantAuthInfo;
|
|
62
|
+
/**
|
|
63
|
+
* Write the public metadata information for a tenant.
|
|
64
|
+
* @param {TenantInfo} info - The tenant info object.
|
|
65
|
+
*/
|
|
66
|
+
exports.writeTenantInfo = _writeTenantInfo;
|
|
67
|
+
/**
|
|
68
|
+
* Read the public metadata information for a tenant.
|
|
69
|
+
* @param {string} tenantId - The tenant ID.
|
|
70
|
+
* @returns Parsed tenant info or null if not found or failed to read.
|
|
71
|
+
*/
|
|
72
|
+
exports.readTenantInfo = _readTenantInfo;
|
|
73
|
+
/**
|
|
74
|
+
* Deletes the stored auth.json file for the given tenant.
|
|
75
|
+
* @param {string} tenantId - The tenant ID.
|
|
76
|
+
*/
|
|
77
|
+
exports.deleteTenantAuthInfo = _deleteTenantAuthInfo;
|
|
78
|
+
/**
|
|
79
|
+
* Scans the CLI root directory and returns all valid tenant infos.
|
|
80
|
+
* @returns A list of TenantInfo objects found in {tenant-id}/info.json files.
|
|
81
|
+
*/
|
|
82
|
+
exports.getAllTenantsInfo = _getAllTenantsInfo;
|
|
83
|
+
// mock setup for unit tests to make sinon happy and mock-able with esbuild/tsx
|
|
84
|
+
// https://sinonjs.org/how-to/typescript-swc/
|
|
85
|
+
// This, plus the `_` names make the exports writable for sinon
|
|
86
|
+
exports.unitMocks = {
|
|
87
|
+
set decodeJwtPayload(mockImplementation) {
|
|
88
|
+
exports.decodeJwtPayload = mockImplementation;
|
|
89
|
+
},
|
|
90
|
+
get decodeJwtPayload() {
|
|
91
|
+
return _decodeJwtPayload;
|
|
92
|
+
},
|
|
93
|
+
set writeTenantAuthInfo(mockImplementation) {
|
|
94
|
+
exports.writeTenantAuthInfo = mockImplementation;
|
|
95
|
+
},
|
|
96
|
+
get writeTenantAuthInfo() {
|
|
97
|
+
return _writeTenantAuthInfo;
|
|
98
|
+
},
|
|
99
|
+
set readTenantAuthInfo(mockImplementation) {
|
|
100
|
+
exports.readTenantAuthInfo = mockImplementation;
|
|
101
|
+
},
|
|
102
|
+
get readTenantAuthInfo() {
|
|
103
|
+
return _readTenantAuthInfo;
|
|
104
|
+
},
|
|
105
|
+
set writeTenantInfo(mockImplementation) {
|
|
106
|
+
exports.writeTenantInfo = mockImplementation;
|
|
107
|
+
},
|
|
108
|
+
get writeTenantInfo() {
|
|
109
|
+
return _writeTenantInfo;
|
|
110
|
+
},
|
|
111
|
+
set readTenantInfo(mockImplementation) {
|
|
112
|
+
exports.readTenantInfo = mockImplementation;
|
|
113
|
+
},
|
|
114
|
+
get readTenantInfo() {
|
|
115
|
+
return _readTenantInfo;
|
|
116
|
+
},
|
|
117
|
+
set deleteTenantAuthInfo(mockImplementation) {
|
|
118
|
+
exports.deleteTenantAuthInfo = mockImplementation;
|
|
119
|
+
},
|
|
120
|
+
get deleteTenantAuthInfo() {
|
|
121
|
+
return _deleteTenantAuthInfo;
|
|
122
|
+
},
|
|
123
|
+
set getAllTenantsInfo(mockImplementation) {
|
|
124
|
+
exports.getAllTenantsInfo = mockImplementation;
|
|
125
|
+
},
|
|
126
|
+
get getAllTenantsInfo() {
|
|
127
|
+
return _getAllTenantsInfo;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Get the full path to the tenant-specific folder.
|
|
132
|
+
* @param {string} tenantId - The tenant ID.
|
|
133
|
+
* @returns The absolute path to the tenant directory.
|
|
134
|
+
*/
|
|
135
|
+
function getTenantPath(tenantId) {
|
|
136
|
+
return path.join(rootDir, tenantId);
|
|
137
|
+
}
|
|
138
|
+
async function _writeTenantAuthInfo(tenantId, authInfo) {
|
|
139
|
+
try {
|
|
140
|
+
const dir = getTenantPath(tenantId);
|
|
141
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
142
|
+
fs.writeFileSync(path.join(dir, 'auth.json'), JSON.stringify(authInfo, null, 2));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.error(`\n Failed to write auth.json for tenant '${tenantId}': ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function _readTenantAuthInfo(tenantId) {
|
|
149
|
+
const filePath = path.join(getTenantPath(tenantId), 'auth.json');
|
|
150
|
+
if (!fs.existsSync(filePath))
|
|
151
|
+
return null;
|
|
152
|
+
try {
|
|
153
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
154
|
+
return JSON.parse(raw);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error(`\n Failed to read auth.json for tenant '${tenantId}': ${error.message}`);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function _writeTenantInfo(info) {
|
|
162
|
+
try {
|
|
163
|
+
const dir = getTenantPath(info.tenantId);
|
|
164
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
165
|
+
fs.writeFileSync(path.join(dir, 'info.json'), JSON.stringify(info, null, 2));
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error(`\n Failed to write info.json for tenant '${info.tenantId}': ${error.message}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function _readTenantInfo(tenantId) {
|
|
172
|
+
const infoFilePath = path.join(getTenantPath(tenantId), 'info.json');
|
|
173
|
+
if (!fs.existsSync(infoFilePath)) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const content = fs.readFileSync(infoFilePath, 'utf-8');
|
|
178
|
+
return JSON.parse(content);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error(`\n Failed to read info.json for tenant '${tenantId}': ${error.message}`);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function _deleteTenantAuthInfo(tenantId) {
|
|
186
|
+
const filePath = path.join(getTenantPath(tenantId), 'auth.json');
|
|
187
|
+
try {
|
|
188
|
+
if (fs.existsSync(filePath)) {
|
|
189
|
+
fs.unlinkSync(filePath);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
console.error(`\n Failed to delete auth.json for tenant '${tenantId}': ${error.message}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function _getAllTenantsInfo() {
|
|
197
|
+
if (!fs.existsSync(rootDir))
|
|
198
|
+
return [];
|
|
199
|
+
const subDirs = fs
|
|
200
|
+
.readdirSync(rootDir)
|
|
201
|
+
.filter((entry) => fs.statSync(path.join(rootDir, entry)).isDirectory());
|
|
202
|
+
const tenants = [];
|
|
203
|
+
for (const dir of subDirs) {
|
|
204
|
+
const infoPath = path.join(rootDir, dir, 'info.json');
|
|
205
|
+
if (fs.existsSync(infoPath)) {
|
|
206
|
+
try {
|
|
207
|
+
const content = fs.readFileSync(infoPath, 'utf-8');
|
|
208
|
+
const data = JSON.parse(content);
|
|
209
|
+
if (data.tenantId && data.tenantName && data.organizationId && data.clientId) {
|
|
210
|
+
tenants.push({
|
|
211
|
+
tenantId: data.tenantId,
|
|
212
|
+
tenantName: data.tenantName,
|
|
213
|
+
organizationId: data.organizationId,
|
|
214
|
+
clientId: data.clientId,
|
|
215
|
+
authority: data.authority,
|
|
216
|
+
audience: data.audience,
|
|
217
|
+
baseUrl: data.baseUrl,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.error('\n Failed to read tenant info file', error.message);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return tenants;
|
|
227
|
+
}
|
|
228
|
+
function _decodeJwtPayload(token) {
|
|
229
|
+
try {
|
|
230
|
+
const base64Payload = token.split('.')[1];
|
|
231
|
+
const payload = Buffer.from(base64Payload, 'base64').toString('utf-8');
|
|
232
|
+
const decoded = JSON.parse(payload);
|
|
233
|
+
return {
|
|
234
|
+
tokenTenantId: decoded === null || decoded === void 0 ? void 0 : decoded[`${CLAIMS}/tenant_id`],
|
|
235
|
+
tokenOrgId: decoded === null || decoded === void 0 ? void 0 : decoded[`${CLAIMS}/org_id`],
|
|
236
|
+
tokenTenantName: decoded === null || decoded === void 0 ? void 0 : decoded[`${CLAIMS}/tenant_name`],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
console.error('\n Failed to decode access token:', error.message);
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
package/dist/cjs/tools/index.js
CHANGED
|
@@ -10,11 +10,33 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
10
10
|
if (k2 === undefined) k2 = k;
|
|
11
11
|
o[k2] = m[k];
|
|
12
12
|
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
13
18
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
19
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
20
|
};
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
16
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
39
|
+
exports.auth = exports.scaffoldComponent = exports.generateMetadata = exports.generateSites = void 0;
|
|
18
40
|
var generateSites_1 = require("./generateSites");
|
|
19
41
|
Object.defineProperty(exports, "generateSites", { enumerable: true, get: function () { return generateSites_1.generateSites; } });
|
|
20
42
|
var generateMetadata_1 = require("./generateMetadata");
|
|
@@ -22,5 +44,6 @@ Object.defineProperty(exports, "generateMetadata", { enumerable: true, get: func
|
|
|
22
44
|
var scaffold_1 = require("./scaffold");
|
|
23
45
|
Object.defineProperty(exports, "scaffoldComponent", { enumerable: true, get: function () { return scaffold_1.scaffoldComponent; } });
|
|
24
46
|
__exportStar(require("./templating"), exports);
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
__exportStar(require("./auth/models"), exports);
|
|
48
|
+
const auth = __importStar(require("./auth"));
|
|
49
|
+
exports.auth = auth;
|
package/dist/esm/constants.js
CHANGED
|
@@ -8,5 +8,6 @@ export var SitecoreTemplateId;
|
|
|
8
8
|
export const siteNameError = 'The siteName cannot be empty';
|
|
9
9
|
export const SITECORE_EDGE_URL_DEFAULT = 'https://edge-platform.sitecorecloud.io';
|
|
10
10
|
export const HIDDEN_RENDERING_NAME = 'Hidden Rendering';
|
|
11
|
-
export const
|
|
11
|
+
export const DEFAULT_SITECORE_AUTH_DOMAIN = 'https://auth.sitecorecloud.io';
|
|
12
12
|
export const DEFAULT_SITECORE_AUTH_AUDIENCE = 'https://api.sitecorecloud.io';
|
|
13
|
+
export const DEFAULT_SITECORE_AUTH_BASE_URL = 'https://edge-platform.sitecorecloud.io/cs/api';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { decodeJwtPayload } from './tenant-store';
|
|
2
|
+
import { DEFAULT_SITECORE_AUTH_DOMAIN, DEFAULT_SITECORE_AUTH_AUDIENCE, DEFAULT_SITECORE_AUTH_BASE_URL, } from '../../constants';
|
|
3
|
+
const GRANT_TYPE = 'client_credentials';
|
|
4
|
+
/**
|
|
5
|
+
* Performs the OAuth 2.0 client credentials flow to obtain a JWT access token
|
|
6
|
+
* from the Sitecore Identity Provider using the provided client credentials.
|
|
7
|
+
* @param {object} [args] - The arguments for client credentials flow
|
|
8
|
+
* @param {string} [args.clientId] - The client ID registered with Sitecore Identity
|
|
9
|
+
* @param {string} [args.clientSecret] - The client secret associated with the client ID
|
|
10
|
+
* @param {string} [args.organizationId] - The ID of the organization the client belongs to
|
|
11
|
+
* @param {string} [args.tenantId] - The tenant ID representing the specific Sitecore environment
|
|
12
|
+
* @param {string} [args.audience] - The API audience the token is intended for. Defaults to `constants.DEFAULT_SITECORE_AUTH_AUDIENCE`
|
|
13
|
+
* @param {string} [args.authority] - The auth server base URL. Defaults to `constants.DEFAULT_SITECORE_AUTH_DOMAIN`
|
|
14
|
+
* @param {string} [args.baseUrl] - The base URL for the API, used to construct the audience. Defaults to `constants.DEFAULT_SITECORE_AUTH_BASE_URL`
|
|
15
|
+
* @returns A Promise that resolves to the access token response (including access token, token type, expiry, etc.)
|
|
16
|
+
* @throws Will log and exit the process if the request fails or returns a non-OK status
|
|
17
|
+
*/
|
|
18
|
+
export let clientCredentialsFlow = _clientCredentialsFlow;
|
|
19
|
+
// mock setup for unit tests to make sinon happy and mock-able with esbuild/tsx
|
|
20
|
+
// https://sinonjs.org/how-to/typescript-swc/
|
|
21
|
+
// This, plus the `_` names make the exports writable for sinon
|
|
22
|
+
export const unitMocks = {
|
|
23
|
+
set clientCredentialsFlow(mockImplementation) {
|
|
24
|
+
clientCredentialsFlow = mockImplementation;
|
|
25
|
+
},
|
|
26
|
+
get clientCredentialsFlow() {
|
|
27
|
+
return _clientCredentialsFlow;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
async function _clientCredentialsFlow({ clientId, clientSecret, organizationId, tenantId, audience = DEFAULT_SITECORE_AUTH_AUDIENCE, authority = DEFAULT_SITECORE_AUTH_DOMAIN, baseUrl = DEFAULT_SITECORE_AUTH_BASE_URL, }) {
|
|
31
|
+
const params = new URLSearchParams({
|
|
32
|
+
client_id: clientId,
|
|
33
|
+
client_secret: clientSecret !== null && clientSecret !== void 0 ? clientSecret : '',
|
|
34
|
+
organization_id: organizationId !== null && organizationId !== void 0 ? organizationId : '',
|
|
35
|
+
tenant_id: tenantId !== null && tenantId !== void 0 ? tenantId : '',
|
|
36
|
+
audience,
|
|
37
|
+
grant_type: GRANT_TYPE,
|
|
38
|
+
baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : '',
|
|
39
|
+
});
|
|
40
|
+
try {
|
|
41
|
+
const response = await fetch(`${authority}/oauth/token`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
44
|
+
body: params.toString(),
|
|
45
|
+
});
|
|
46
|
+
const data = await response.json();
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(data.error_description || data.error || 'Error during client credentials flow');
|
|
49
|
+
}
|
|
50
|
+
const decodedPayload = decodeJwtPayload(data.access_token) || {};
|
|
51
|
+
if (!(decodedPayload === null || decodedPayload === void 0 ? void 0 : decodedPayload.tokenTenantId) || !decodedPayload.tokenOrgId) {
|
|
52
|
+
throw new Error('\n Token is missing required claims tenant_id or org_id.');
|
|
53
|
+
}
|
|
54
|
+
const { tokenTenantId, tokenOrgId, tokenTenantName } = decodedPayload;
|
|
55
|
+
if (tenantId && tenantId !== tokenTenantId) {
|
|
56
|
+
throw new Error('\n Mismatch: Provided tenant ID does not match claims tenant ID.');
|
|
57
|
+
}
|
|
58
|
+
if (organizationId && organizationId !== tokenOrgId) {
|
|
59
|
+
throw new Error('\n Mismatch: Provided organization ID does not match claims organization ID.');
|
|
60
|
+
}
|
|
61
|
+
return { data, tokenOrgId, tokenTenantId, tokenTenantName, accessToken: data.access_token };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('\n Error during client credentials flow:', error instanceof Error ? error.message : error);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|