@sitecore-content-sdk/core 0.2.0-beta.2 → 0.2.0-beta.21

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 (88) hide show
  1. package/content.d.ts +1 -0
  2. package/content.js +1 -0
  3. package/dist/cjs/client/graphql-edge-proxy.js +3 -3
  4. package/dist/cjs/client/sitecore-client.js +34 -17
  5. package/dist/cjs/config/define-config.js +6 -5
  6. package/dist/cjs/constants.js +12 -1
  7. package/dist/cjs/content/content-client.js +148 -0
  8. package/dist/cjs/content/index.js +13 -0
  9. package/dist/cjs/content/locales.js +32 -0
  10. package/dist/cjs/content/taxonomies.js +78 -0
  11. package/dist/cjs/content/utils.js +16 -0
  12. package/dist/cjs/debug.js +1 -0
  13. package/dist/cjs/editing/design-library.js +2 -1
  14. package/dist/cjs/editing/rest-component-layout-service.js +26 -45
  15. package/dist/cjs/index.js +3 -1
  16. package/dist/cjs/layout/content-styles.js +2 -1
  17. package/dist/cjs/layout/themes.js +2 -1
  18. package/dist/cjs/site/graphql-robots-service.js +3 -2
  19. package/dist/cjs/tools/auth/encryption.js +141 -0
  20. package/dist/cjs/tools/auth/fetcher.js +34 -0
  21. package/dist/cjs/tools/auth/flow.js +123 -0
  22. package/dist/cjs/tools/auth/index.js +27 -0
  23. package/dist/cjs/tools/auth/models.js +2 -0
  24. package/dist/cjs/tools/auth/renewal.js +130 -0
  25. package/dist/cjs/tools/auth/tenant-state.js +110 -0
  26. package/dist/cjs/tools/auth/tenant-store.js +250 -0
  27. package/dist/cjs/tools/index.js +26 -1
  28. package/dist/cjs/utils/normalize-url.js +5 -0
  29. package/dist/cjs/utils/utils.js +5 -3
  30. package/dist/esm/client/graphql-edge-proxy.js +1 -1
  31. package/dist/esm/client/sitecore-client.js +34 -17
  32. package/dist/esm/config/define-config.js +6 -5
  33. package/dist/esm/constants.js +11 -0
  34. package/dist/esm/content/content-client.js +141 -0
  35. package/dist/esm/content/index.js +4 -0
  36. package/dist/esm/content/locales.js +29 -0
  37. package/dist/esm/content/taxonomies.js +75 -0
  38. package/dist/esm/content/utils.js +13 -0
  39. package/dist/esm/debug.js +1 -0
  40. package/dist/esm/editing/design-library.js +2 -1
  41. package/dist/esm/editing/rest-component-layout-service.js +23 -45
  42. package/dist/esm/index.js +2 -0
  43. package/dist/esm/layout/content-styles.js +2 -1
  44. package/dist/esm/layout/themes.js +2 -1
  45. package/dist/esm/site/graphql-robots-service.js +3 -2
  46. package/dist/esm/tools/auth/encryption.js +101 -0
  47. package/dist/esm/tools/auth/fetcher.js +31 -0
  48. package/dist/esm/tools/auth/flow.js +118 -0
  49. package/dist/esm/tools/auth/index.js +5 -0
  50. package/dist/esm/tools/auth/models.js +1 -0
  51. package/dist/esm/tools/auth/renewal.js +124 -0
  52. package/dist/esm/tools/auth/tenant-state.js +73 -0
  53. package/dist/esm/tools/auth/tenant-store.js +213 -0
  54. package/dist/esm/tools/index.js +3 -0
  55. package/dist/esm/utils/normalize-url.js +1 -0
  56. package/dist/esm/utils/utils.js +5 -3
  57. package/package.json +19 -18
  58. package/types/client/index.d.ts +1 -1
  59. package/types/client/models.d.ts +17 -1
  60. package/types/client/sitecore-client.d.ts +50 -22
  61. package/types/config/index.d.ts +1 -1
  62. package/types/config/models.d.ts +12 -2
  63. package/types/constants.d.ts +10 -0
  64. package/types/content/content-client.d.ts +92 -0
  65. package/types/content/index.d.ts +4 -0
  66. package/types/content/locales.d.ts +38 -0
  67. package/types/content/taxonomies.d.ts +125 -0
  68. package/types/content/utils.d.ts +15 -0
  69. package/types/debug.d.ts +1 -0
  70. package/types/editing/rest-component-layout-service.d.ts +23 -58
  71. package/types/index.d.ts +2 -0
  72. package/types/native-fetcher.d.ts +0 -7
  73. package/types/site/graphql-robots-service.d.ts +3 -2
  74. package/types/tools/auth/encryption.d.ts +34 -0
  75. package/types/tools/auth/fetcher.d.ts +13 -0
  76. package/types/tools/auth/flow.d.ts +40 -0
  77. package/types/tools/auth/index.d.ts +5 -0
  78. package/types/tools/auth/models.d.ts +233 -0
  79. package/types/tools/auth/renewal.d.ts +36 -0
  80. package/types/tools/auth/tenant-state.d.ts +21 -0
  81. package/types/tools/auth/tenant-store.d.ts +63 -0
  82. package/types/tools/index.d.ts +3 -0
  83. package/types/utils/normalize-url.d.ts +1 -0
  84. package/dist/cjs/data-fetcher.js +0 -22
  85. package/dist/esm/data-fetcher.js +0 -17
  86. package/form.d.ts +0 -1
  87. package/form.js +0 -1
  88. package/types/data-fetcher.d.ts +0 -34
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.traverseComponent = exports.traverseField = exports.traversePlaceholder = exports.getContentStylesheetUrl = exports.getContentStylesheetLink = void 0;
4
4
  const constants_1 = require("../constants");
5
+ const normalize_url_1 = require("../utils/normalize-url");
5
6
  /**
6
7
  * Regular expression to check if the content styles are used in the field value
7
8
  */
@@ -26,7 +27,7 @@ const getContentStylesheetLink = (layoutData, sitecoreEdgeContextId, sitecoreEdg
26
27
  };
27
28
  };
28
29
  exports.getContentStylesheetLink = getContentStylesheetLink;
29
- const getContentStylesheetUrl = (sitecoreEdgeContextId, sitecoreEdgeUrl = constants_1.SITECORE_EDGE_URL_DEFAULT) => `${sitecoreEdgeUrl}/v1/files/pages/styles/content-styles.css?sitecoreContextId=${sitecoreEdgeContextId}`;
30
+ const getContentStylesheetUrl = (sitecoreEdgeContextId, sitecoreEdgeUrl = constants_1.SITECORE_EDGE_URL_DEFAULT) => `${(0, normalize_url_1.normalizeUrl)(sitecoreEdgeUrl)}/v1/files/pages/styles/content-styles.css?sitecoreContextId=${sitecoreEdgeContextId}`;
30
31
  exports.getContentStylesheetUrl = getContentStylesheetUrl;
31
32
  const traversePlaceholder = (components, config) => {
32
33
  if (config.loadStyles)
@@ -4,6 +4,7 @@ exports.getStylesheetUrl = void 0;
4
4
  exports.getDesignLibraryStylesheetLinks = getDesignLibraryStylesheetLinks;
5
5
  const _1 = require(".");
6
6
  const constants_1 = require("../constants");
7
+ const normalize_url_1 = require("../utils/normalize-url");
7
8
  /**
8
9
  * Pattern for library ids
9
10
  * @example -library--foo
@@ -27,7 +28,7 @@ function getDesignLibraryStylesheetLinks(layoutData, sitecoreEdgeContextId, site
27
28
  }));
28
29
  }
29
30
  const getStylesheetUrl = (id, sitecoreEdgeContextId, sitecoreEdgeUrl = constants_1.SITECORE_EDGE_URL_DEFAULT) => {
30
- return `${sitecoreEdgeUrl}/v1/files/components/styles/${id}.css?sitecoreContextId=${sitecoreEdgeContextId}`;
31
+ return `${(0, normalize_url_1.normalizeUrl)(sitecoreEdgeUrl)}/v1/files/components/styles/${id}.css?sitecoreContextId=${sitecoreEdgeContextId}`;
31
32
  };
32
33
  exports.getStylesheetUrl = getStylesheetUrl;
33
34
  /**
@@ -33,17 +33,18 @@ class GraphQLRobotsService {
33
33
  }
34
34
  /**
35
35
  * Fetch a data of robots.txt from API
36
+ * @param {FetchOptions} fetchOptions - The fetch options to be used for the request.
36
37
  * @returns text of robots.txt
37
38
  * @throws {Error} if the siteName is empty.
38
39
  */
39
- async fetchRobots() {
40
+ async fetchRobots(fetchOptions) {
40
41
  const siteName = this.options.siteName;
41
42
  if (!siteName) {
42
43
  throw new Error(constants_1.siteNameError);
43
44
  }
44
45
  const robotsResult = this.graphQLClient.request(this.query, {
45
46
  siteName,
46
- });
47
+ }, fetchOptions);
47
48
  try {
48
49
  return robotsResult.then((result) => {
49
50
  var _a, _b;
@@ -0,0 +1,141 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.unitMocks = exports.deleteKey = exports.decryptData = exports.encryptData = void 0;
40
+ exports.getKey = getKey;
41
+ /* eslint-disable jsdoc/require-jsdoc */
42
+ const keytar_1 = __importDefault(require("keytar"));
43
+ const crypto = __importStar(require("crypto"));
44
+ const tenant_store_1 = require("./tenant-store");
45
+ const tenant_state_1 = require("./tenant-state");
46
+ const algorithm = 'aes-256-gcm';
47
+ const SERVICE_NAME = 'sitecore-tools-cli';
48
+ /**
49
+ * Encrypts plaintext using AES-256-GCM for a given tenant.
50
+ * @param {string} plaintext
51
+ * @param {string} tenantId
52
+ */
53
+ exports.encryptData = _encryptData;
54
+ /**
55
+ * Decrypts encrypted payload using AES-256-GCM for a specific tenant.
56
+ * If key is corrupted or invalid, optionally clears both key and tenant data.
57
+ * @param {EncryptedPayload} payload
58
+ * @param {string} tenantId
59
+ * @param {string} cleanupOnFailure
60
+ */
61
+ exports.decryptData = _decryptData;
62
+ /**
63
+ * Deletes the encryption key for a tenant (useful for cleanup).
64
+ * @param {string} tenantId
65
+ */
66
+ exports.deleteKey = _deleteKey;
67
+ // mock setup for unit tests to make sinon happy and mock-able with esbuild/tsx
68
+ // https://sinonjs.org/how-to/typescript-swc/
69
+ // This, plus the `_` names make the exports writable for sinon
70
+ exports.unitMocks = {
71
+ set encryptData(mockImplementation) {
72
+ exports.encryptData = mockImplementation;
73
+ },
74
+ get encryptData() {
75
+ return _encryptData;
76
+ },
77
+ set decryptData(mockImplementation) {
78
+ exports.decryptData = mockImplementation;
79
+ },
80
+ get decryptData() {
81
+ return _decryptData;
82
+ },
83
+ set deleteKey(mockImplementation) {
84
+ exports.deleteKey = mockImplementation;
85
+ },
86
+ get deleteKey() {
87
+ return _deleteKey;
88
+ },
89
+ };
90
+ /**
91
+ * Generates or retrieves a 32-byte AES key for a specific tenant.
92
+ * @param {string} tenantId
93
+ */
94
+ async function getKey(tenantId) {
95
+ const account = `encryptionKey-${tenantId}`;
96
+ const key = await keytar_1.default.getPassword(SERVICE_NAME, account);
97
+ if (!key) {
98
+ const keyBuffer = crypto.randomBytes(32);
99
+ await keytar_1.default.setPassword(SERVICE_NAME, account, keyBuffer.toString('base64'));
100
+ return keyBuffer;
101
+ }
102
+ return Buffer.from(key, 'base64');
103
+ }
104
+ async function _encryptData(plaintext, tenantId) {
105
+ const key = await getKey(tenantId);
106
+ const iv = crypto.randomBytes(12);
107
+ const cipher = crypto.createCipheriv(algorithm, key, iv);
108
+ let encrypted = cipher.update(plaintext, 'utf8', 'base64');
109
+ encrypted += cipher.final('base64');
110
+ const authTag = cipher.getAuthTag().toString('base64');
111
+ return {
112
+ iv: iv.toString('base64'),
113
+ authTag,
114
+ encryptedData: encrypted,
115
+ };
116
+ }
117
+ async function _decryptData(payload, tenantId, cleanupOnFailure = true) {
118
+ try {
119
+ const key = await getKey(tenantId);
120
+ const decipher = crypto.createDecipheriv(algorithm, key, Buffer.from(payload.iv, 'base64'));
121
+ decipher.setAuthTag(Buffer.from(payload.authTag, 'base64'));
122
+ let decrypted = decipher.update(payload.encryptedData, 'base64', 'utf8');
123
+ decrypted += decipher.final('utf8');
124
+ return decrypted;
125
+ }
126
+ catch (err) {
127
+ console.error(`\nFailed to decrypt data for tenant '${tenantId}':`, err);
128
+ if (cleanupOnFailure) {
129
+ console.warn(`\nCleaning up key and auth data for corrupted tenant '${tenantId}'...`);
130
+ await (0, tenant_store_1.deleteTenantAuthInfo)(tenantId);
131
+ await (0, exports.deleteKey)(`encryptionKey-${tenantId}`);
132
+ (0, tenant_state_1.clearActiveTenant)();
133
+ console.warn(`\nCleanup completed for tenant '${tenantId}'.`);
134
+ return null;
135
+ }
136
+ throw err;
137
+ }
138
+ }
139
+ async function _deleteKey(tenantId) {
140
+ await keytar_1.default.deletePassword(SERVICE_NAME, `encryptionKey-${tenantId}`);
141
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendPostRequest = exports.unitMocks = void 0;
4
+ /* eslint-disable jsdoc/require-jsdoc */
5
+ exports.unitMocks = {
6
+ set sendPostRequest(mockImplementation) {
7
+ exports.sendPostRequest = mockImplementation;
8
+ },
9
+ get sendPostRequest() {
10
+ return _sendPostRequest;
11
+ },
12
+ };
13
+ /**
14
+ * Performs a POST request with application/x-www-form-urlencoded headers.
15
+ * @param {string} url - The endpoint to post to.
16
+ * @param { URLSearchParams} params - A URLSearchParams instance representing the body.
17
+ * @returns Parsed JSON response.
18
+ * @throws Error if response is not OK.
19
+ */
20
+ exports.sendPostRequest = _sendPostRequest;
21
+ async function _sendPostRequest(url, params, throwOnError = true) {
22
+ const response = await fetch(url, {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/x-www-form-urlencoded',
26
+ },
27
+ body: params.toString(),
28
+ });
29
+ const data = await response.json();
30
+ if (throwOnError && !response.ok) {
31
+ throw new Error(data.error_description || data.error || 'Unknown error occurred');
32
+ }
33
+ return data;
34
+ }
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unitMocks = exports.pollForDeviceToken = exports.startDeviceAuthFlow = exports.clientCredentialsFlow = void 0;
4
+ exports._startDeviceAuthFlow = _startDeviceAuthFlow;
5
+ exports._pollForDeviceToken = _pollForDeviceToken;
6
+ const tenant_store_1 = require("./tenant-store");
7
+ const fetcher_1 = require("./fetcher");
8
+ const constants_1 = require("../../constants");
9
+ /**
10
+ * Performs the OAuth 2.0 client credentials flow to obtain a JWT access token
11
+ * from the Sitecore Identity Provider using the provided client credentials.
12
+ * @param {TenantArgs} params - Parameters including clientId, clientSecret, organizationId, tenantId, audience, authority, and baseUrl.
13
+ * @returns A Promise that resolves to the access token response (including access token, token type, expiry, etc.)
14
+ * @throws Will log and exit the process if the request fails or returns a non-OK status
15
+ */
16
+ exports.clientCredentialsFlow = _clientCredentialsFlow;
17
+ /**
18
+ * Initiates the OAuth 2.0 Device Authorization flow by requesting a device and user code.
19
+ * This flow is typically used by devices or CLI apps that cannot input credentials directly.
20
+ * @param {DeviceAuthRequest} params - Parameters including clientId, audience, authority, and baseUrl.
21
+ * @returns {Promise<DeviceAuthResponse>} A promise resolving to device authorization metadata needed for polling.
22
+ * @throws {Error} If the device authorization request fails or returns an error response.
23
+ */
24
+ exports.startDeviceAuthFlow = _startDeviceAuthFlow;
25
+ /**
26
+ * Polls the OAuth 2.0 device token endpoint to retrieve the access token once the user has authorized the device.
27
+ * This is typically used to continue the device authorization process after a user enters a code on a browser.
28
+ * @param {DeviceTokenPollRequest} params - Parameters for polling including clientId, deviceCode, interval, and authority.
29
+ * @returns {Promise<any>} A promise resolving to the device token response including access token and refresh token.
30
+ * @throws {Error} If polling fails or exceeds the timeout period.
31
+ */
32
+ exports.pollForDeviceToken = _pollForDeviceToken;
33
+ // mock setup for unit tests to make sinon happy and mock-able with esbuild/tsx
34
+ // https://sinonjs.org/how-to/typescript-swc/
35
+ // This, plus the `_` names make the exports writable for sinon
36
+ exports.unitMocks = {
37
+ set clientCredentialsFlow(mockImplementation) {
38
+ exports.clientCredentialsFlow = mockImplementation;
39
+ },
40
+ get clientCredentialsFlow() {
41
+ return _clientCredentialsFlow;
42
+ },
43
+ set startDeviceAuthFlow(mockImplementation) {
44
+ exports.startDeviceAuthFlow = mockImplementation;
45
+ },
46
+ get startDeviceAuthFlow() {
47
+ return _startDeviceAuthFlow;
48
+ },
49
+ set pollForDeviceToken(mockImplementation) {
50
+ exports.pollForDeviceToken = mockImplementation;
51
+ },
52
+ get pollForDeviceToken() {
53
+ return _pollForDeviceToken;
54
+ },
55
+ };
56
+ 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, }) {
57
+ const params = new URLSearchParams({
58
+ client_id: clientId,
59
+ client_secret: clientSecret !== null && clientSecret !== void 0 ? clientSecret : '',
60
+ organization_id: organizationId !== null && organizationId !== void 0 ? organizationId : '',
61
+ tenant_id: tenantId !== null && tenantId !== void 0 ? tenantId : '',
62
+ audience,
63
+ grant_type: constants_1.CLIENT_GRANT_TYPE,
64
+ baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : '',
65
+ });
66
+ const url = `${authority}/oauth/token`;
67
+ const data = await (0, fetcher_1.sendPostRequest)(url, params);
68
+ const decodedPayload = (0, tenant_store_1.decodeJwtPayload)(data.access_token) || {};
69
+ if (!(decodedPayload === null || decodedPayload === void 0 ? void 0 : decodedPayload.tokenTenantId) || !decodedPayload.tokenOrgId) {
70
+ throw new Error('\n Token is missing required claims tenant_id or org_id.');
71
+ }
72
+ const { tokenTenantId, tokenOrgId, tokenTenantName } = decodedPayload;
73
+ if (tenantId && tenantId !== tokenTenantId) {
74
+ throw new Error('\n Mismatch: Provided tenant ID does not match claims tenant ID.');
75
+ }
76
+ if (organizationId && organizationId !== tokenOrgId) {
77
+ throw new Error('\n Mismatch: Provided organization ID does not match claims organization ID.');
78
+ }
79
+ return { data, tokenOrgId, tokenTenantId, tokenTenantName, accessToken: data.access_token };
80
+ }
81
+ async function _startDeviceAuthFlow({ clientId, audience, authority, baseUrl, }) {
82
+ const params = new URLSearchParams({
83
+ client_id: clientId,
84
+ scope: constants_1.SCOPE,
85
+ audience,
86
+ baseUrl,
87
+ });
88
+ const url = `${authority}/oauth/device/code`;
89
+ const responseBody = (await (0, fetcher_1.sendPostRequest)(url, params));
90
+ return responseBody;
91
+ }
92
+ async function _pollForDeviceToken({ clientId, device_code, interval = constants_1.DEFAULT_INTERVAL, authority = constants_1.DEFAULT_SITECORE_AUTH_DOMAIN, }) {
93
+ const startTime = Date.now();
94
+ while (Date.now() - startTime < constants_1.TIMEOUT * 1000) {
95
+ const params = new URLSearchParams({
96
+ grant_type: constants_1.DEVICE_GRANT_TYPE,
97
+ device_code,
98
+ client_id: clientId,
99
+ });
100
+ const url = `${authority}/oauth/token`;
101
+ const responseBody = await (0, fetcher_1.sendPostRequest)(url, params, false);
102
+ if ('error' in responseBody) {
103
+ switch (responseBody.error) {
104
+ case 'authorization_pending':
105
+ console.log('\n ⌛ Waiting for user authorization...');
106
+ break;
107
+ case 'slow_down':
108
+ console.log('🐢 Slowing down polling interval...');
109
+ interval += 5;
110
+ break;
111
+ default:
112
+ throw new Error(responseBody.error_description ||
113
+ responseBody.error ||
114
+ 'Unknown error during device token polling.');
115
+ }
116
+ await new Promise((resolve) => setTimeout(resolve, interval * 1000));
117
+ }
118
+ else {
119
+ return responseBody;
120
+ }
121
+ }
122
+ throw new Error('⏳ Timeout: User did not complete authorization in time.');
123
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteKey = exports.decryptData = exports.encryptData = exports.writeTenantInfo = exports.getAllTenantsInfo = exports.readTenantInfo = exports.deleteTenantAuthInfo = exports.readTenantAuthInfo = exports.writeTenantAuthInfo = exports.clearActiveTenant = exports.setActiveTenant = exports.getActiveTenant = exports.getRefreshAccessToken = exports.validateAuthInfo = exports.validateAndRenewAuthIfExpired = exports.renewClientToken = exports.pollForDeviceToken = exports.startDeviceAuthFlow = exports.clientCredentialsFlow = void 0;
4
+ var flow_1 = require("./flow");
5
+ Object.defineProperty(exports, "clientCredentialsFlow", { enumerable: true, get: function () { return flow_1.clientCredentialsFlow; } });
6
+ Object.defineProperty(exports, "startDeviceAuthFlow", { enumerable: true, get: function () { return flow_1.startDeviceAuthFlow; } });
7
+ Object.defineProperty(exports, "pollForDeviceToken", { enumerable: true, get: function () { return flow_1.pollForDeviceToken; } });
8
+ var renewal_1 = require("./renewal");
9
+ Object.defineProperty(exports, "renewClientToken", { enumerable: true, get: function () { return renewal_1.renewClientToken; } });
10
+ Object.defineProperty(exports, "validateAndRenewAuthIfExpired", { enumerable: true, get: function () { return renewal_1.validateAndRenewAuthIfExpired; } });
11
+ Object.defineProperty(exports, "validateAuthInfo", { enumerable: true, get: function () { return renewal_1.validateAuthInfo; } });
12
+ Object.defineProperty(exports, "getRefreshAccessToken", { enumerable: true, get: function () { return renewal_1.getRefreshAccessToken; } });
13
+ var tenant_state_1 = require("./tenant-state");
14
+ Object.defineProperty(exports, "getActiveTenant", { enumerable: true, get: function () { return tenant_state_1.getActiveTenant; } });
15
+ Object.defineProperty(exports, "setActiveTenant", { enumerable: true, get: function () { return tenant_state_1.setActiveTenant; } });
16
+ Object.defineProperty(exports, "clearActiveTenant", { enumerable: true, get: function () { return tenant_state_1.clearActiveTenant; } });
17
+ var tenant_store_1 = require("./tenant-store");
18
+ Object.defineProperty(exports, "writeTenantAuthInfo", { enumerable: true, get: function () { return tenant_store_1.writeTenantAuthInfo; } });
19
+ Object.defineProperty(exports, "readTenantAuthInfo", { enumerable: true, get: function () { return tenant_store_1.readTenantAuthInfo; } });
20
+ Object.defineProperty(exports, "deleteTenantAuthInfo", { enumerable: true, get: function () { return tenant_store_1.deleteTenantAuthInfo; } });
21
+ Object.defineProperty(exports, "readTenantInfo", { enumerable: true, get: function () { return tenant_store_1.readTenantInfo; } });
22
+ Object.defineProperty(exports, "getAllTenantsInfo", { enumerable: true, get: function () { return tenant_store_1.getAllTenantsInfo; } });
23
+ Object.defineProperty(exports, "writeTenantInfo", { enumerable: true, get: function () { return tenant_store_1.writeTenantInfo; } });
24
+ var encryption_1 = require("./encryption");
25
+ Object.defineProperty(exports, "encryptData", { enumerable: true, get: function () { return encryption_1.encryptData; } });
26
+ Object.defineProperty(exports, "decryptData", { enumerable: true, get: function () { return encryption_1.decryptData; } });
27
+ Object.defineProperty(exports, "deleteKey", { enumerable: true, get: function () { return encryption_1.deleteKey; } });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unitMocks = exports.getRefreshAccessToken = void 0;
4
+ exports.validateAuthInfo = validateAuthInfo;
5
+ exports.renewClientToken = renewClientToken;
6
+ exports.validateAndRenewAuthIfExpired = validateAndRenewAuthIfExpired;
7
+ const tenant_state_1 = require("./tenant-state");
8
+ const flow_1 = require("./flow");
9
+ const tenant_store_1 = require("./tenant-store");
10
+ const encryption_1 = require("./encryption");
11
+ const constants_1 = require("./../../constants");
12
+ const tenant_store_2 = require("./tenant-store");
13
+ const fetcher_1 = require("./fetcher");
14
+ /**
15
+ * Requests a new access token using the OAuth 2.0 refresh token grant type.
16
+ * This is used to "upgrade" an initial device flow token by including tenant-specific context.
17
+ * @param {RefreshTokenRequest} options - Configuration for the refresh token request.
18
+ * @returns {Promise<any>} A promise that resolves to the refreshed token data including tenant context.
19
+ * @throws {Error} If the token request fails or returns an error response.
20
+ */
21
+ exports.getRefreshAccessToken = _getRefreshAccessToken;
22
+ exports.unitMocks = {
23
+ get getRefreshAccessToken() {
24
+ return _getRefreshAccessToken;
25
+ },
26
+ set getRefreshAccessToken(mockFn) {
27
+ exports.getRefreshAccessToken = mockFn;
28
+ },
29
+ };
30
+ /**
31
+ * Validates whether a given auth config is still valid (i.e., not expired).
32
+ * @param {TenantAuth} authInfo - The tenant auth configuration.
33
+ * @returns True if the token is still valid, false if expired.
34
+ */
35
+ function validateAuthInfo(authInfo) {
36
+ const now = new Date();
37
+ const expiry = new Date(authInfo.expires_at);
38
+ return now < expiry;
39
+ }
40
+ /**
41
+ * Renews the token for a given tenant using stored credentials.
42
+ * @param {TenantAuth} authInfo - Current authentication info for the tenant.
43
+ * @param {TenantInfo} tenantInfo - Public metadata about the tenant (e.g., clientId).
44
+ * @returns {Promise<void>} resolving when the token is successfully renewed.
45
+ * @throws If credentials are missing or renewal fails.
46
+ */
47
+ async function renewClientToken(authInfo, tenantInfo) {
48
+ const result = await (0, flow_1.clientCredentialsFlow)({
49
+ clientId: tenantInfo.clientId,
50
+ clientSecret: authInfo.clientSecret,
51
+ organizationId: tenantInfo.organizationId,
52
+ tenantId: tenantInfo.tenantId,
53
+ audience: tenantInfo.audience,
54
+ authority: tenantInfo.authority,
55
+ baseUrl: tenantInfo.baseUrl,
56
+ });
57
+ const tenantId = tenantInfo.tenantId;
58
+ await (0, tenant_store_1.writeTenantAuthInfo)(tenantId, {
59
+ clientSecret: authInfo.clientSecret,
60
+ access_token: result.data.access_token,
61
+ expires_in: result.data.expires_in,
62
+ expires_at: new Date(Date.now() + result.data.expires_in * 1000).toISOString(),
63
+ });
64
+ console.log(`\n Token for tenant ${tenantId} renewed.`);
65
+ }
66
+ /**
67
+ * Ensures a valid token exists, renews it if expired.
68
+ * @returns {Promise<{ tenantId: string } | null>} returns tenant context if successful, otherwise null.
69
+ * @throws If renewal fails or credentials are missing.
70
+ */
71
+ async function validateAndRenewAuthIfExpired() {
72
+ const tenantId = (0, tenant_state_1.getActiveTenant)();
73
+ if (!tenantId)
74
+ return null;
75
+ const authInfo = await (0, tenant_store_1.readTenantAuthInfo)(tenantId);
76
+ if (!authInfo)
77
+ return null;
78
+ const isValid = validateAuthInfo(authInfo);
79
+ if (isValid) {
80
+ return { tenantId };
81
+ }
82
+ const tenantInfo = await (0, tenant_store_1.readTenantInfo)(tenantId);
83
+ if (!tenantInfo)
84
+ return null;
85
+ console.log(`\n Token for tenant ${tenantId} is expired. Renewing...`);
86
+ try {
87
+ if (authInfo.clientSecret) {
88
+ await renewClientToken(authInfo, tenantInfo);
89
+ return { tenantId };
90
+ }
91
+ else if (authInfo.refresh_token) {
92
+ const refreshTokenResponse = await (0, exports.getRefreshAccessToken)({
93
+ clientId: tenantInfo.clientId,
94
+ refreshToken: authInfo.refresh_token,
95
+ tenantId: tenantId,
96
+ organizationId: tenantInfo.organizationId,
97
+ authority: tenantInfo.authority,
98
+ });
99
+ await (0, tenant_store_1.writeTenantAuthInfo)(tenantId, Object.assign({ expires_at: new Date(Date.now() + refreshTokenResponse.expires_in * 1000).toISOString() }, refreshTokenResponse));
100
+ console.log(`\n Token for tenant ${tenantId} renewed.`);
101
+ return { tenantId };
102
+ }
103
+ else {
104
+ throw new Error('\n No valid credentials found for token renewal.');
105
+ }
106
+ }
107
+ catch (err) {
108
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
109
+ console.error(`\n Failed to renew token for tenant '${tenantId}: ${errorMessage}'`);
110
+ console.log(`\n Cleaning up stale authentication data for tenant '${tenantId}'...`);
111
+ await (0, tenant_store_1.deleteTenantAuthInfo)(tenantId);
112
+ await (0, encryption_1.deleteKey)(tenantId);
113
+ (0, tenant_state_1.clearActiveTenant)();
114
+ return null;
115
+ }
116
+ }
117
+ // eslint-disable-next-line jsdoc/require-jsdoc
118
+ async function _getRefreshAccessToken({ clientId, refreshToken, tenantId, organizationId, authority = constants_1.DEFAULT_SITECORE_AUTH_DOMAIN, }) {
119
+ const params = new URLSearchParams({
120
+ client_id: clientId,
121
+ grant_type: constants_1.REFRESH_GRANT_TYPE,
122
+ refresh_token: refreshToken,
123
+ tenant_id: tenantId,
124
+ organization_id: organizationId,
125
+ });
126
+ const url = `${authority}/oauth/token`;
127
+ const data = await (0, fetcher_1.sendPostRequest)(url, params);
128
+ const { tokenTenantName } = (0, tenant_store_2.decodeJwtPayload)(data.access_token) || {};
129
+ return Object.assign(Object.assign({}, data), { tenantName: tokenTenantName });
130
+ }
@@ -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
+ }