@sitecore-content-sdk/cli 0.2.0-beta.16 ā 0.2.0-beta.18
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/scripts/auth/index.js +2 -1
- package/dist/cjs/scripts/auth/list.js +1 -1
- package/dist/cjs/scripts/auth/login.js +81 -39
- package/dist/cjs/scripts/auth/logout.js +8 -8
- package/dist/cjs/scripts/auth/status.js +4 -4
- package/dist/cjs/scripts/auth/switch.js +57 -0
- package/dist/esm/scripts/auth/index.js +2 -1
- package/dist/esm/scripts/auth/list.js +1 -1
- package/dist/esm/scripts/auth/login.js +81 -39
- package/dist/esm/scripts/auth/logout.js +8 -8
- package/dist/esm/scripts/auth/status.js +4 -4
- package/dist/esm/scripts/auth/switch.js +53 -0
- package/package.json +3 -3
- package/types/scripts/auth/logout.d.ts +1 -1
- package/types/scripts/auth/status.d.ts +1 -1
- package/types/scripts/auth/switch.d.ts +5 -0
|
@@ -5,6 +5,7 @@ const login_1 = require("./login");
|
|
|
5
5
|
const logout_1 = require("./logout");
|
|
6
6
|
const status_1 = require("./status");
|
|
7
7
|
const list_1 = require("./list");
|
|
8
|
+
const switch_1 = require("./switch");
|
|
8
9
|
/**
|
|
9
10
|
* Registers the `auth` command group and its subcommands (`login`, `logout`, `status`, `list`) with Yargs.
|
|
10
11
|
* @param {Argv} yargs - The Yargs instance used to define CLI commands.
|
|
@@ -16,7 +17,7 @@ function builder(yargs) {
|
|
|
16
17
|
describe: 'Performs authentication for content services',
|
|
17
18
|
builder: (_yargs) => {
|
|
18
19
|
return _yargs
|
|
19
|
-
.command([login_1.login, logout_1.logout, status_1.status, list_1.list])
|
|
20
|
+
.command([login_1.login, logout_1.logout, status_1.status, list_1.list, switch_1.switchTenant])
|
|
20
21
|
.strict()
|
|
21
22
|
.demandCommand(1, 'You need to specify a command to run')
|
|
22
23
|
.help();
|
|
@@ -25,7 +25,7 @@ exports.list = {
|
|
|
25
25
|
console.log('\n No tenant information found.');
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
-
console.log('\
|
|
28
|
+
console.log('\nKnown tenants:\n');
|
|
29
29
|
tenants.forEach((tenant, index) => {
|
|
30
30
|
console.log(`Tenant ${index + 1}:`);
|
|
31
31
|
console.log(` Tenant ID : ${tenant.tenantId}`);
|
|
@@ -12,12 +12,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.login = exports.unitMock = void 0;
|
|
13
13
|
const tools_1 = require("@sitecore-content-sdk/core/tools");
|
|
14
14
|
const core_1 = require("@sitecore-content-sdk/core");
|
|
15
|
-
let { setActiveTenant, writeTenantAuthInfo, writeTenantInfo, clientCredentialsFlow } = tools_1.auth;
|
|
15
|
+
let { setActiveTenant, writeTenantAuthInfo, writeTenantInfo, clientCredentialsFlow, getRefreshAccessToken, pollForDeviceToken, startDeviceAuthFlow, } = tools_1.auth;
|
|
16
16
|
const unitMock = (formModule) => {
|
|
17
17
|
setActiveTenant = formModule.setActiveTenant;
|
|
18
18
|
writeTenantAuthInfo = formModule.writeTenantAuthInfo;
|
|
19
19
|
writeTenantInfo = formModule.writeTenantInfo;
|
|
20
20
|
clientCredentialsFlow = formModule.clientCredentialsFlow;
|
|
21
|
+
getRefreshAccessToken = formModule.getRefreshAccessToken;
|
|
22
|
+
pollForDeviceToken = formModule.pollForDeviceToken;
|
|
23
|
+
startDeviceAuthFlow = formModule.startDeviceAuthFlow;
|
|
21
24
|
};
|
|
22
25
|
exports.unitMock = unitMock;
|
|
23
26
|
exports.login = {
|
|
@@ -70,51 +73,90 @@ exports.login = {
|
|
|
70
73
|
'baseUrl',
|
|
71
74
|
], 'Login Options:'),
|
|
72
75
|
handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
-
const { clientId } = argv;
|
|
74
|
-
let authResult, tenantId, organizationId, tenantName;
|
|
75
76
|
const { DEFAULT_SITECORE_AUTH_DOMAIN, DEFAULT_SITECORE_AUTH_AUDIENCE, DEFAULT_SITECORE_AUTH_BASE_URL, } = core_1.constants;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const { clientId, clientSecret, organizationId, tenantId: inputTenantId, audience = DEFAULT_SITECORE_AUTH_AUDIENCE, authority = DEFAULT_SITECORE_AUTH_DOMAIN, baseUrl = DEFAULT_SITECORE_AUTH_BASE_URL, } = argv;
|
|
78
|
+
let authResponse;
|
|
79
|
+
let tenantId;
|
|
80
|
+
let tenantName;
|
|
81
|
+
let orgId;
|
|
82
|
+
try {
|
|
83
|
+
if (clientSecret) {
|
|
84
|
+
console.log('\n Using Client Credentials Flow...');
|
|
85
|
+
const { data, tokenTenantId, tokenOrgId, tokenTenantName } = yield clientCredentialsFlow({
|
|
79
86
|
clientId,
|
|
80
|
-
clientSecret
|
|
81
|
-
organizationId
|
|
82
|
-
tenantId:
|
|
83
|
-
audience
|
|
84
|
-
authority
|
|
85
|
-
baseUrl
|
|
87
|
+
clientSecret,
|
|
88
|
+
organizationId,
|
|
89
|
+
tenantId: inputTenantId,
|
|
90
|
+
audience,
|
|
91
|
+
authority,
|
|
92
|
+
baseUrl,
|
|
86
93
|
});
|
|
87
|
-
|
|
88
|
-
tenantId =
|
|
89
|
-
|
|
90
|
-
tenantName =
|
|
94
|
+
authResponse = data;
|
|
95
|
+
tenantId = tokenTenantId;
|
|
96
|
+
orgId = tokenOrgId;
|
|
97
|
+
tenantName = tokenTenantName;
|
|
91
98
|
}
|
|
92
|
-
|
|
93
|
-
console.
|
|
94
|
-
|
|
99
|
+
else {
|
|
100
|
+
console.log('\n Using Device Authorization Flow...');
|
|
101
|
+
if (!inputTenantId) {
|
|
102
|
+
throw new Error('\n Tenant ID is required for Device Code Flow.');
|
|
103
|
+
}
|
|
104
|
+
if (!organizationId) {
|
|
105
|
+
throw new Error('\n Organization ID is required for Device Code Flow.');
|
|
106
|
+
}
|
|
107
|
+
const deviceAuthData = yield startDeviceAuthFlow({
|
|
108
|
+
clientId,
|
|
109
|
+
audience,
|
|
110
|
+
authority,
|
|
111
|
+
baseUrl,
|
|
112
|
+
});
|
|
113
|
+
const { device_code, user_code, verification_uri, verification_uri_complete, interval, } = deviceAuthData;
|
|
114
|
+
console.log('\nš Device Authorization Started');
|
|
115
|
+
if (verification_uri_complete) {
|
|
116
|
+
console.log(`\n š Open the following URL to authenticate:\n ${verification_uri_complete}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.log(`š Visit: ${verification_uri}`);
|
|
120
|
+
console.log(`š Then enter the code: ${user_code}`);
|
|
121
|
+
}
|
|
122
|
+
const { refresh_token } = yield pollForDeviceToken({
|
|
123
|
+
clientId,
|
|
124
|
+
device_code,
|
|
125
|
+
authority,
|
|
126
|
+
interval,
|
|
127
|
+
});
|
|
128
|
+
// The initial device code flow does not support custom claims (e.g., tenantId, organizationId),
|
|
129
|
+
// which are required for proper token validation in our multi-tenant system.
|
|
130
|
+
// To include these claims, we immediately use the returned refresh_token to request a new
|
|
131
|
+
// access token with tenantId and organizationId explicitly provided.
|
|
132
|
+
authResponse = yield getRefreshAccessToken({
|
|
133
|
+
clientId,
|
|
134
|
+
refreshToken: refresh_token,
|
|
135
|
+
tenantId: inputTenantId,
|
|
136
|
+
organizationId,
|
|
137
|
+
authority,
|
|
138
|
+
});
|
|
139
|
+
tenantId = inputTenantId;
|
|
140
|
+
orgId = organizationId;
|
|
141
|
+
tenantName = authResponse.tenantName;
|
|
142
|
+
console.log('\n Device Authorization Completed');
|
|
95
143
|
}
|
|
144
|
+
yield writeTenantAuthInfo(tenantId || inputTenantId, Object.assign({ expires_at: new Date(Date.now() + authResponse.expires_in * 1000).toISOString() }, authResponse));
|
|
145
|
+
yield writeTenantInfo({
|
|
146
|
+
tenantId,
|
|
147
|
+
organizationId: orgId,
|
|
148
|
+
clientId,
|
|
149
|
+
tenantName,
|
|
150
|
+
authority,
|
|
151
|
+
audience,
|
|
152
|
+
baseUrl,
|
|
153
|
+
});
|
|
154
|
+
setActiveTenant(tenantId);
|
|
155
|
+
console.log(`\n Logged in successfully to tenant: ${tenantId}`);
|
|
96
156
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
console.log('\n Please provide client secret for authentication.');
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.error(`\n Login failed: ${error.message}`);
|
|
100
159
|
process.exit(1);
|
|
101
160
|
}
|
|
102
|
-
yield writeTenantAuthInfo(tenantId, {
|
|
103
|
-
clientSecret: argv.clientSecret,
|
|
104
|
-
access_token: authResult.access_token,
|
|
105
|
-
expires_in: authResult.expires_in,
|
|
106
|
-
expires_at: new Date(Date.now() + authResult.expires_in * 1000).toISOString(),
|
|
107
|
-
});
|
|
108
|
-
yield writeTenantInfo({
|
|
109
|
-
tenantId,
|
|
110
|
-
organizationId,
|
|
111
|
-
clientId,
|
|
112
|
-
tenantName,
|
|
113
|
-
authority: argv.authority || DEFAULT_SITECORE_AUTH_DOMAIN,
|
|
114
|
-
audience: argv.audience || DEFAULT_SITECORE_AUTH_AUDIENCE,
|
|
115
|
-
baseUrl: argv.baseUrl || DEFAULT_SITECORE_AUTH_BASE_URL,
|
|
116
|
-
});
|
|
117
|
-
setActiveTenant(tenantId);
|
|
118
|
-
console.info(`\n Logged in successfully to tenant ${tenantId}.`);
|
|
119
161
|
}),
|
|
120
162
|
};
|
|
@@ -12,11 +12,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.logout = exports.unitMock = void 0;
|
|
13
13
|
const tools_1 = require("@sitecore-content-sdk/core/tools");
|
|
14
14
|
let { deleteTenantAuthInfo, getActiveTenant, clearActiveTenant, deleteKey } = tools_1.auth;
|
|
15
|
-
const unitMock = (
|
|
16
|
-
deleteTenantAuthInfo =
|
|
17
|
-
getActiveTenant =
|
|
18
|
-
clearActiveTenant =
|
|
19
|
-
deleteKey =
|
|
15
|
+
const unitMock = (authModule) => {
|
|
16
|
+
deleteTenantAuthInfo = authModule.deleteTenantAuthInfo || deleteTenantAuthInfo;
|
|
17
|
+
getActiveTenant = authModule.getActiveTenant || getActiveTenant;
|
|
18
|
+
clearActiveTenant = authModule.clearActiveTenant || clearActiveTenant;
|
|
19
|
+
deleteKey = authModule.deleteKey || deleteKey;
|
|
20
20
|
};
|
|
21
21
|
exports.unitMock = unitMock;
|
|
22
22
|
exports.logout = {
|
|
@@ -28,9 +28,9 @@ exports.logout = {
|
|
|
28
28
|
console.error('\n No active tenant found. Please login first.');
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
deleteTenantAuthInfo(tenantId);
|
|
32
|
-
deleteKey(tenantId);
|
|
31
|
+
yield deleteTenantAuthInfo(tenantId);
|
|
32
|
+
yield deleteKey(tenantId);
|
|
33
33
|
clearActiveTenant();
|
|
34
|
-
console.
|
|
34
|
+
console.log(`\n Logged out from tenant ${tenantId}`);
|
|
35
35
|
}),
|
|
36
36
|
};
|
|
@@ -12,10 +12,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.status = exports.unitMock = void 0;
|
|
13
13
|
const tools_1 = require("@sitecore-content-sdk/core/tools");
|
|
14
14
|
let { readTenantInfo, validateAndRenewAuthIfExpired } = tools_1.auth;
|
|
15
|
-
const unitMock = (
|
|
16
|
-
readTenantInfo =
|
|
15
|
+
const unitMock = (authModule) => {
|
|
16
|
+
readTenantInfo = authModule.readTenantInfo || readTenantInfo;
|
|
17
17
|
validateAndRenewAuthIfExpired =
|
|
18
|
-
|
|
18
|
+
authModule.validateAndRenewAuthIfExpired || validateAndRenewAuthIfExpired;
|
|
19
19
|
};
|
|
20
20
|
exports.unitMock = unitMock;
|
|
21
21
|
exports.status = {
|
|
@@ -24,7 +24,7 @@ exports.status = {
|
|
|
24
24
|
handler: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
25
|
const context = yield validateAndRenewAuthIfExpired();
|
|
26
26
|
if (!context) {
|
|
27
|
-
console.log('\
|
|
27
|
+
console.log('\n No valid authentication found. Please login.');
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
const tenantInfo = yield readTenantInfo(context.tenantId);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.switchTenant = exports.unitMock = void 0;
|
|
13
|
+
const tools_1 = require("@sitecore-content-sdk/core/tools");
|
|
14
|
+
let { readTenantAuthInfo, validateAndRenewAuthIfExpired, setActiveTenant } = tools_1.auth;
|
|
15
|
+
const unitMock = (authModule) => {
|
|
16
|
+
readTenantAuthInfo = authModule.readTenantAuthInfo || readTenantAuthInfo;
|
|
17
|
+
setActiveTenant = authModule.setActiveTenant || setActiveTenant;
|
|
18
|
+
validateAndRenewAuthIfExpired =
|
|
19
|
+
authModule.validateAndRenewAuthIfExpired || validateAndRenewAuthIfExpired;
|
|
20
|
+
};
|
|
21
|
+
exports.unitMock = unitMock;
|
|
22
|
+
exports.switchTenant = {
|
|
23
|
+
command: 'switch <tenantId>',
|
|
24
|
+
describe: 'Switch into another tenant that you have logged into previously',
|
|
25
|
+
builder: (yargs) => yargs.positional('tenantId', {
|
|
26
|
+
positional: true,
|
|
27
|
+
demandOption: true,
|
|
28
|
+
type: 'string',
|
|
29
|
+
describe: 'Tenant ID to switch into.',
|
|
30
|
+
}),
|
|
31
|
+
handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
const tenantId = argv.tenantId;
|
|
33
|
+
const currentContext = yield validateAndRenewAuthIfExpired();
|
|
34
|
+
if (!currentContext) {
|
|
35
|
+
console.error('\nNo valid authentication found. Please login.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (currentContext.tenantId === tenantId) {
|
|
39
|
+
console.log(`Already in tenant: ${tenantId}`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const newTenantInfo = yield readTenantAuthInfo(tenantId);
|
|
43
|
+
if (!newTenantInfo) {
|
|
44
|
+
console.error(`Tenant info for ID '${tenantId}' not found in local storage.`);
|
|
45
|
+
console.error('Please ensure you have logged into the tenant by running the auth login command');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
setActiveTenant(tenantId);
|
|
49
|
+
const tenantContext = validateAndRenewAuthIfExpired();
|
|
50
|
+
if (!tenantContext) {
|
|
51
|
+
console.error(`Failed to switch to tenant '${tenantId}', remaining in tenant '${currentContext.tenantId}'.`);
|
|
52
|
+
setActiveTenant(currentContext.tenantId);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
console.log(`Switched to tenant: ${tenantId}`);
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
@@ -2,6 +2,7 @@ import { login } from './login';
|
|
|
2
2
|
import { logout } from './logout';
|
|
3
3
|
import { status } from './status';
|
|
4
4
|
import { list } from './list';
|
|
5
|
+
import { switchTenant } from './switch';
|
|
5
6
|
/**
|
|
6
7
|
* Registers the `auth` command group and its subcommands (`login`, `logout`, `status`, `list`) with Yargs.
|
|
7
8
|
* @param {Argv} yargs - The Yargs instance used to define CLI commands.
|
|
@@ -13,7 +14,7 @@ export function builder(yargs) {
|
|
|
13
14
|
describe: 'Performs authentication for content services',
|
|
14
15
|
builder: (_yargs) => {
|
|
15
16
|
return _yargs
|
|
16
|
-
.command([login, logout, status, list])
|
|
17
|
+
.command([login, logout, status, list, switchTenant])
|
|
17
18
|
.strict()
|
|
18
19
|
.demandCommand(1, 'You need to specify a command to run')
|
|
19
20
|
.help();
|
|
@@ -21,7 +21,7 @@ export const list = {
|
|
|
21
21
|
console.log('\n No tenant information found.');
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
console.log('\
|
|
24
|
+
console.log('\nKnown tenants:\n');
|
|
25
25
|
tenants.forEach((tenant, index) => {
|
|
26
26
|
console.log(`Tenant ${index + 1}:`);
|
|
27
27
|
console.log(` Tenant ID : ${tenant.tenantId}`);
|
|
@@ -9,12 +9,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { auth } from '@sitecore-content-sdk/core/tools';
|
|
11
11
|
import { constants } from '@sitecore-content-sdk/core';
|
|
12
|
-
let { setActiveTenant, writeTenantAuthInfo, writeTenantInfo, clientCredentialsFlow } = auth;
|
|
12
|
+
let { setActiveTenant, writeTenantAuthInfo, writeTenantInfo, clientCredentialsFlow, getRefreshAccessToken, pollForDeviceToken, startDeviceAuthFlow, } = auth;
|
|
13
13
|
export const unitMock = (formModule) => {
|
|
14
14
|
setActiveTenant = formModule.setActiveTenant;
|
|
15
15
|
writeTenantAuthInfo = formModule.writeTenantAuthInfo;
|
|
16
16
|
writeTenantInfo = formModule.writeTenantInfo;
|
|
17
17
|
clientCredentialsFlow = formModule.clientCredentialsFlow;
|
|
18
|
+
getRefreshAccessToken = formModule.getRefreshAccessToken;
|
|
19
|
+
pollForDeviceToken = formModule.pollForDeviceToken;
|
|
20
|
+
startDeviceAuthFlow = formModule.startDeviceAuthFlow;
|
|
18
21
|
};
|
|
19
22
|
export const login = {
|
|
20
23
|
command: 'login',
|
|
@@ -66,51 +69,90 @@ export const login = {
|
|
|
66
69
|
'baseUrl',
|
|
67
70
|
], 'Login Options:'),
|
|
68
71
|
handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
-
const { clientId } = argv;
|
|
70
|
-
let authResult, tenantId, organizationId, tenantName;
|
|
71
72
|
const { DEFAULT_SITECORE_AUTH_DOMAIN, DEFAULT_SITECORE_AUTH_AUDIENCE, DEFAULT_SITECORE_AUTH_BASE_URL, } = constants;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const { clientId, clientSecret, organizationId, tenantId: inputTenantId, audience = DEFAULT_SITECORE_AUTH_AUDIENCE, authority = DEFAULT_SITECORE_AUTH_DOMAIN, baseUrl = DEFAULT_SITECORE_AUTH_BASE_URL, } = argv;
|
|
74
|
+
let authResponse;
|
|
75
|
+
let tenantId;
|
|
76
|
+
let tenantName;
|
|
77
|
+
let orgId;
|
|
78
|
+
try {
|
|
79
|
+
if (clientSecret) {
|
|
80
|
+
console.log('\n Using Client Credentials Flow...');
|
|
81
|
+
const { data, tokenTenantId, tokenOrgId, tokenTenantName } = yield clientCredentialsFlow({
|
|
75
82
|
clientId,
|
|
76
|
-
clientSecret
|
|
77
|
-
organizationId
|
|
78
|
-
tenantId:
|
|
79
|
-
audience
|
|
80
|
-
authority
|
|
81
|
-
baseUrl
|
|
83
|
+
clientSecret,
|
|
84
|
+
organizationId,
|
|
85
|
+
tenantId: inputTenantId,
|
|
86
|
+
audience,
|
|
87
|
+
authority,
|
|
88
|
+
baseUrl,
|
|
82
89
|
});
|
|
83
|
-
|
|
84
|
-
tenantId =
|
|
85
|
-
|
|
86
|
-
tenantName =
|
|
90
|
+
authResponse = data;
|
|
91
|
+
tenantId = tokenTenantId;
|
|
92
|
+
orgId = tokenOrgId;
|
|
93
|
+
tenantName = tokenTenantName;
|
|
87
94
|
}
|
|
88
|
-
|
|
89
|
-
console.
|
|
90
|
-
|
|
95
|
+
else {
|
|
96
|
+
console.log('\n Using Device Authorization Flow...');
|
|
97
|
+
if (!inputTenantId) {
|
|
98
|
+
throw new Error('\n Tenant ID is required for Device Code Flow.');
|
|
99
|
+
}
|
|
100
|
+
if (!organizationId) {
|
|
101
|
+
throw new Error('\n Organization ID is required for Device Code Flow.');
|
|
102
|
+
}
|
|
103
|
+
const deviceAuthData = yield startDeviceAuthFlow({
|
|
104
|
+
clientId,
|
|
105
|
+
audience,
|
|
106
|
+
authority,
|
|
107
|
+
baseUrl,
|
|
108
|
+
});
|
|
109
|
+
const { device_code, user_code, verification_uri, verification_uri_complete, interval, } = deviceAuthData;
|
|
110
|
+
console.log('\nš Device Authorization Started');
|
|
111
|
+
if (verification_uri_complete) {
|
|
112
|
+
console.log(`\n š Open the following URL to authenticate:\n ${verification_uri_complete}`);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.log(`š Visit: ${verification_uri}`);
|
|
116
|
+
console.log(`š Then enter the code: ${user_code}`);
|
|
117
|
+
}
|
|
118
|
+
const { refresh_token } = yield pollForDeviceToken({
|
|
119
|
+
clientId,
|
|
120
|
+
device_code,
|
|
121
|
+
authority,
|
|
122
|
+
interval,
|
|
123
|
+
});
|
|
124
|
+
// The initial device code flow does not support custom claims (e.g., tenantId, organizationId),
|
|
125
|
+
// which are required for proper token validation in our multi-tenant system.
|
|
126
|
+
// To include these claims, we immediately use the returned refresh_token to request a new
|
|
127
|
+
// access token with tenantId and organizationId explicitly provided.
|
|
128
|
+
authResponse = yield getRefreshAccessToken({
|
|
129
|
+
clientId,
|
|
130
|
+
refreshToken: refresh_token,
|
|
131
|
+
tenantId: inputTenantId,
|
|
132
|
+
organizationId,
|
|
133
|
+
authority,
|
|
134
|
+
});
|
|
135
|
+
tenantId = inputTenantId;
|
|
136
|
+
orgId = organizationId;
|
|
137
|
+
tenantName = authResponse.tenantName;
|
|
138
|
+
console.log('\n Device Authorization Completed');
|
|
91
139
|
}
|
|
140
|
+
yield writeTenantAuthInfo(tenantId || inputTenantId, Object.assign({ expires_at: new Date(Date.now() + authResponse.expires_in * 1000).toISOString() }, authResponse));
|
|
141
|
+
yield writeTenantInfo({
|
|
142
|
+
tenantId,
|
|
143
|
+
organizationId: orgId,
|
|
144
|
+
clientId,
|
|
145
|
+
tenantName,
|
|
146
|
+
authority,
|
|
147
|
+
audience,
|
|
148
|
+
baseUrl,
|
|
149
|
+
});
|
|
150
|
+
setActiveTenant(tenantId);
|
|
151
|
+
console.log(`\n Logged in successfully to tenant: ${tenantId}`);
|
|
92
152
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
console.log('\n Please provide client secret for authentication.');
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.error(`\n Login failed: ${error.message}`);
|
|
96
155
|
process.exit(1);
|
|
97
156
|
}
|
|
98
|
-
yield writeTenantAuthInfo(tenantId, {
|
|
99
|
-
clientSecret: argv.clientSecret,
|
|
100
|
-
access_token: authResult.access_token,
|
|
101
|
-
expires_in: authResult.expires_in,
|
|
102
|
-
expires_at: new Date(Date.now() + authResult.expires_in * 1000).toISOString(),
|
|
103
|
-
});
|
|
104
|
-
yield writeTenantInfo({
|
|
105
|
-
tenantId,
|
|
106
|
-
organizationId,
|
|
107
|
-
clientId,
|
|
108
|
-
tenantName,
|
|
109
|
-
authority: argv.authority || DEFAULT_SITECORE_AUTH_DOMAIN,
|
|
110
|
-
audience: argv.audience || DEFAULT_SITECORE_AUTH_AUDIENCE,
|
|
111
|
-
baseUrl: argv.baseUrl || DEFAULT_SITECORE_AUTH_BASE_URL,
|
|
112
|
-
});
|
|
113
|
-
setActiveTenant(tenantId);
|
|
114
|
-
console.info(`\n Logged in successfully to tenant ${tenantId}.`);
|
|
115
157
|
}),
|
|
116
158
|
};
|
|
@@ -9,11 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { auth } from '@sitecore-content-sdk/core/tools';
|
|
11
11
|
let { deleteTenantAuthInfo, getActiveTenant, clearActiveTenant, deleteKey } = auth;
|
|
12
|
-
export const unitMock = (
|
|
13
|
-
deleteTenantAuthInfo =
|
|
14
|
-
getActiveTenant =
|
|
15
|
-
clearActiveTenant =
|
|
16
|
-
deleteKey =
|
|
12
|
+
export const unitMock = (authModule) => {
|
|
13
|
+
deleteTenantAuthInfo = authModule.deleteTenantAuthInfo || deleteTenantAuthInfo;
|
|
14
|
+
getActiveTenant = authModule.getActiveTenant || getActiveTenant;
|
|
15
|
+
clearActiveTenant = authModule.clearActiveTenant || clearActiveTenant;
|
|
16
|
+
deleteKey = authModule.deleteKey || deleteKey;
|
|
17
17
|
};
|
|
18
18
|
export const logout = {
|
|
19
19
|
command: 'logout',
|
|
@@ -24,9 +24,9 @@ export const logout = {
|
|
|
24
24
|
console.error('\n No active tenant found. Please login first.');
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
deleteTenantAuthInfo(tenantId);
|
|
28
|
-
deleteKey(tenantId);
|
|
27
|
+
yield deleteTenantAuthInfo(tenantId);
|
|
28
|
+
yield deleteKey(tenantId);
|
|
29
29
|
clearActiveTenant();
|
|
30
|
-
console.
|
|
30
|
+
console.log(`\n Logged out from tenant ${tenantId}`);
|
|
31
31
|
}),
|
|
32
32
|
};
|
|
@@ -9,10 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { auth } from '@sitecore-content-sdk/core/tools';
|
|
11
11
|
let { readTenantInfo, validateAndRenewAuthIfExpired } = auth;
|
|
12
|
-
export const unitMock = (
|
|
13
|
-
readTenantInfo =
|
|
12
|
+
export const unitMock = (authModule) => {
|
|
13
|
+
readTenantInfo = authModule.readTenantInfo || readTenantInfo;
|
|
14
14
|
validateAndRenewAuthIfExpired =
|
|
15
|
-
|
|
15
|
+
authModule.validateAndRenewAuthIfExpired || validateAndRenewAuthIfExpired;
|
|
16
16
|
};
|
|
17
17
|
export const status = {
|
|
18
18
|
command: 'status',
|
|
@@ -20,7 +20,7 @@ export const status = {
|
|
|
20
20
|
handler: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
21
|
const context = yield validateAndRenewAuthIfExpired();
|
|
22
22
|
if (!context) {
|
|
23
|
-
console.log('\
|
|
23
|
+
console.log('\n No valid authentication found. Please login.');
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
const tenantInfo = yield readTenantInfo(context.tenantId);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { auth } from '@sitecore-content-sdk/core/tools';
|
|
11
|
+
let { readTenantAuthInfo, validateAndRenewAuthIfExpired, setActiveTenant } = auth;
|
|
12
|
+
export const unitMock = (authModule) => {
|
|
13
|
+
readTenantAuthInfo = authModule.readTenantAuthInfo || readTenantAuthInfo;
|
|
14
|
+
setActiveTenant = authModule.setActiveTenant || setActiveTenant;
|
|
15
|
+
validateAndRenewAuthIfExpired =
|
|
16
|
+
authModule.validateAndRenewAuthIfExpired || validateAndRenewAuthIfExpired;
|
|
17
|
+
};
|
|
18
|
+
export const switchTenant = {
|
|
19
|
+
command: 'switch <tenantId>',
|
|
20
|
+
describe: 'Switch into another tenant that you have logged into previously',
|
|
21
|
+
builder: (yargs) => yargs.positional('tenantId', {
|
|
22
|
+
positional: true,
|
|
23
|
+
demandOption: true,
|
|
24
|
+
type: 'string',
|
|
25
|
+
describe: 'Tenant ID to switch into.',
|
|
26
|
+
}),
|
|
27
|
+
handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
const tenantId = argv.tenantId;
|
|
29
|
+
const currentContext = yield validateAndRenewAuthIfExpired();
|
|
30
|
+
if (!currentContext) {
|
|
31
|
+
console.error('\nNo valid authentication found. Please login.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (currentContext.tenantId === tenantId) {
|
|
35
|
+
console.log(`Already in tenant: ${tenantId}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const newTenantInfo = yield readTenantAuthInfo(tenantId);
|
|
39
|
+
if (!newTenantInfo) {
|
|
40
|
+
console.error(`Tenant info for ID '${tenantId}' not found in local storage.`);
|
|
41
|
+
console.error('Please ensure you have logged into the tenant by running the auth login command');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
setActiveTenant(tenantId);
|
|
45
|
+
const tenantContext = validateAndRenewAuthIfExpired();
|
|
46
|
+
if (!tenantContext) {
|
|
47
|
+
console.error(`Failed to switch to tenant '${tenantId}', remaining in tenant '${currentContext.tenantId}'.`);
|
|
48
|
+
setActiveTenant(currentContext.tenantId);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log(`Switched to tenant: ${tenantId}`);
|
|
52
|
+
}),
|
|
53
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sitecore-content-sdk/cli",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.18",
|
|
4
4
|
"description": "Sitecore Content SDK CLI",
|
|
5
5
|
"main": "dist/cjs/cli.js",
|
|
6
6
|
"module": "dist/esm/cli.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"url": "https://github.com/sitecore/content-sdk/issues"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@sitecore-content-sdk/core": "0.2.0-beta.
|
|
37
|
+
"@sitecore-content-sdk/core": "0.2.0-beta.18",
|
|
38
38
|
"dotenv": "^16.5.0",
|
|
39
39
|
"dotenv-expand": "^12.0.2",
|
|
40
40
|
"resolve": "^1.22.10",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"ts-node": "^10.9.1",
|
|
61
61
|
"typescript": "~5.8.3"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "358368ae9455c999c64af4cf63d7daef08a4fcb1",
|
|
64
64
|
"files": [
|
|
65
65
|
"dist",
|
|
66
66
|
"types"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { CommandModule } from 'yargs';
|
|
2
|
+
import { TenantInfo } from '@sitecore-content-sdk/core/tools';
|
|
3
|
+
export declare const unitMock: (authModule: any) => void;
|
|
4
|
+
export type SwitchArgs = Pick<TenantInfo, 'tenantId'>;
|
|
5
|
+
export declare const switchTenant: CommandModule<object, SwitchArgs>;
|