@microsoft/teamsfx 0.3.3-alpha.3dc53ce2.0 → 0.3.3-alpha.7e7c7c23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.esm2017.js +1413 -0
- package/dist/index.esm2017.js.map +1 -0
- package/dist/{index.js → index.esm2017.mjs} +1467 -1506
- package/dist/index.esm2017.mjs.map +1 -0
- package/dist/index.esm5.js +1575 -0
- package/dist/index.esm5.js.map +1 -0
- package/dist/index.node.cjs.js +1653 -0
- package/dist/index.node.cjs.js.map +1 -0
- package/package.json +58 -78
- package/types/teamsfx.d.ts +0 -2
- package/dist/index.js.map +0 -1
- package/dist/teamsfx.js +0 -30
- package/dist/teamsfx.js.map +0 -1
- package/dist-esm/src/bot/teamsBotSsoPrompt.browser.js +0 -118
- package/dist-esm/src/bot/teamsBotSsoPrompt.browser.js.map +0 -1
- package/dist-esm/src/bot/teamsBotSsoPrompt.js +0 -349
- package/dist-esm/src/bot/teamsBotSsoPrompt.js.map +0 -1
- package/dist-esm/src/bot/teamsBotSsoPromptTokenResponse.js +0 -2
- package/dist-esm/src/bot/teamsBotSsoPromptTokenResponse.js.map +0 -1
- package/dist-esm/src/core/cache.browser.js +0 -22
- package/dist-esm/src/core/cache.browser.js.map +0 -1
- package/dist-esm/src/core/cache.js +0 -28
- package/dist-esm/src/core/cache.js.map +0 -1
- package/dist-esm/src/core/configurationProvider.js +0 -124
- package/dist-esm/src/core/configurationProvider.js.map +0 -1
- package/dist-esm/src/core/defaultTediousConnectionConfiguration.browser.js +0 -28
- package/dist-esm/src/core/defaultTediousConnectionConfiguration.browser.js.map +0 -1
- package/dist-esm/src/core/defaultTediousConnectionConfiguration.js +0 -182
- package/dist-esm/src/core/defaultTediousConnectionConfiguration.js.map +0 -1
- package/dist-esm/src/core/errors.js +0 -97
- package/dist-esm/src/core/errors.js.map +0 -1
- package/dist-esm/src/core/msGraphAuthProvider.js +0 -68
- package/dist-esm/src/core/msGraphAuthProvider.js.map +0 -1
- package/dist-esm/src/core/msGraphClientProvider.js +0 -65
- package/dist-esm/src/core/msGraphClientProvider.js.map +0 -1
- package/dist-esm/src/credential/m365TenantCredential.browser.js +0 -38
- package/dist-esm/src/credential/m365TenantCredential.browser.js.map +0 -1
- package/dist-esm/src/credential/m365TenantCredential.js +0 -126
- package/dist-esm/src/credential/m365TenantCredential.js.map +0 -1
- package/dist-esm/src/credential/onBehalfOfUserCredential.browser.js +0 -46
- package/dist-esm/src/credential/onBehalfOfUserCredential.browser.js.map +0 -1
- package/dist-esm/src/credential/onBehalfOfUserCredential.js +0 -178
- package/dist-esm/src/credential/onBehalfOfUserCredential.js.map +0 -1
- package/dist-esm/src/credential/teamsUserCredential.browser.js +0 -462
- package/dist-esm/src/credential/teamsUserCredential.browser.js.map +0 -1
- package/dist-esm/src/credential/teamsUserCredential.js +0 -56
- package/dist-esm/src/credential/teamsUserCredential.js.map +0 -1
- package/dist-esm/src/index.js +0 -14
- package/dist-esm/src/index.js.map +0 -1
- package/dist-esm/src/models/accessTokenResult.js +0 -4
- package/dist-esm/src/models/accessTokenResult.js.map +0 -1
- package/dist-esm/src/models/authCodeResult.js +0 -4
- package/dist-esm/src/models/authCodeResult.js.map +0 -1
- package/dist-esm/src/models/configuration.js +0 -20
- package/dist-esm/src/models/configuration.js.map +0 -1
- package/dist-esm/src/models/grantType.js +0 -11
- package/dist-esm/src/models/grantType.js.map +0 -1
- package/dist-esm/src/models/ssoTokenInfo.js +0 -4
- package/dist-esm/src/models/ssoTokenInfo.js.map +0 -1
- package/dist-esm/src/models/userinfo.js +0 -4
- package/dist-esm/src/models/userinfo.js.map +0 -1
- package/dist-esm/src/util/logger.js +0 -134
- package/dist-esm/src/util/logger.js.map +0 -1
- package/dist-esm/src/util/utils.js +0 -130
- package/dist-esm/src/util/utils.js.map +0 -1
- package/dist-esm/src/util/utils.node.js +0 -23
- package/dist-esm/src/util/utils.node.js.map +0 -1
|
@@ -1,1530 +1,1491 @@
|
|
|
1
|
-
|
|
1
|
+
import jwt_decode from 'jwt-decode';
|
|
2
|
+
import { ConfidentialClientApplication } from '@azure/msal-node';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { Client } from '@microsoft/microsoft-graph-client';
|
|
5
|
+
import { ManagedIdentityCredential } from '@azure/identity';
|
|
6
|
+
import { ActivityTypes, Channels, TeamsInfo, CardFactory, ActionTypes, MessageFactory, StatusCodes, verifyStateOperationName, tokenExchangeOperationName } from 'botbuilder';
|
|
7
|
+
import { Dialog } from 'botbuilder-dialogs';
|
|
8
|
+
import { v4 } from 'uuid';
|
|
2
9
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* Constructor of ErrorWithCode.
|
|
99
|
-
*
|
|
100
|
-
* @param {string} message - error message.
|
|
101
|
-
* @param {ErrorCode} code - error code.
|
|
102
|
-
*
|
|
103
|
-
* @beta
|
|
104
|
-
*/
|
|
105
|
-
constructor(message, code) {
|
|
106
|
-
if (!code) {
|
|
107
|
-
super(message);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
super(message);
|
|
111
|
-
Object.setPrototypeOf(this, ErrorWithCode.prototype);
|
|
112
|
-
this.name = `${new.target.name}.${code}`;
|
|
113
|
-
this.code = code;
|
|
114
|
-
}
|
|
10
|
+
// Copyright (c) Microsoft Corporation.
|
|
11
|
+
// Licensed under the MIT license.
|
|
12
|
+
/**
|
|
13
|
+
* Error code to trace the error types.
|
|
14
|
+
* @beta
|
|
15
|
+
*/
|
|
16
|
+
var ErrorCode;
|
|
17
|
+
(function (ErrorCode) {
|
|
18
|
+
/**
|
|
19
|
+
* Invalid parameter error.
|
|
20
|
+
*/
|
|
21
|
+
ErrorCode["InvalidParameter"] = "InvalidParameter";
|
|
22
|
+
/**
|
|
23
|
+
* Invalid configuration error.
|
|
24
|
+
*/
|
|
25
|
+
ErrorCode["InvalidConfiguration"] = "InvalidConfiguration";
|
|
26
|
+
/**
|
|
27
|
+
* Invalid certificate error.
|
|
28
|
+
*/
|
|
29
|
+
ErrorCode["InvalidCertificate"] = "InvalidCertificate";
|
|
30
|
+
/**
|
|
31
|
+
* Internal error.
|
|
32
|
+
*/
|
|
33
|
+
ErrorCode["InternalError"] = "InternalError";
|
|
34
|
+
/**
|
|
35
|
+
* Channel is not supported error.
|
|
36
|
+
*/
|
|
37
|
+
ErrorCode["ChannelNotSupported"] = "ChannelNotSupported";
|
|
38
|
+
/**
|
|
39
|
+
* Runtime is not supported error.
|
|
40
|
+
*/
|
|
41
|
+
ErrorCode["RuntimeNotSupported"] = "RuntimeNotSupported";
|
|
42
|
+
/**
|
|
43
|
+
* User failed to finish the AAD consent flow failed.
|
|
44
|
+
*/
|
|
45
|
+
ErrorCode["ConsentFailed"] = "ConsentFailed";
|
|
46
|
+
/**
|
|
47
|
+
* The user or administrator has not consented to use the application error.
|
|
48
|
+
*/
|
|
49
|
+
ErrorCode["UiRequiredError"] = "UiRequiredError";
|
|
50
|
+
/**
|
|
51
|
+
* Token is not within its valid time range error.
|
|
52
|
+
*/
|
|
53
|
+
ErrorCode["TokenExpiredError"] = "TokenExpiredError";
|
|
54
|
+
/**
|
|
55
|
+
* Call service (AAD or simple authentication server) failed.
|
|
56
|
+
*/
|
|
57
|
+
ErrorCode["ServiceError"] = "ServiceError";
|
|
58
|
+
/**
|
|
59
|
+
* Operation failed.
|
|
60
|
+
*/
|
|
61
|
+
ErrorCode["FailedOperation"] = "FailedOperation";
|
|
62
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
63
|
+
/**
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
class ErrorMessage {
|
|
67
|
+
}
|
|
68
|
+
// InvalidConfiguration Error
|
|
69
|
+
ErrorMessage.InvalidConfiguration = "{0} in configuration is invalid: {1}.";
|
|
70
|
+
ErrorMessage.ConfigurationNotExists = "Configuration does not exist. {0}";
|
|
71
|
+
ErrorMessage.ResourceConfigurationNotExists = "{0} resource configuration does not exist.";
|
|
72
|
+
ErrorMessage.MissingResourceConfiguration = "Missing resource configuration with type: {0}, name: {1}.";
|
|
73
|
+
ErrorMessage.AuthenticationConfigurationNotExists = "Authentication configuration does not exist.";
|
|
74
|
+
// RuntimeNotSupported Error
|
|
75
|
+
ErrorMessage.BrowserRuntimeNotSupported = "{0} is not supported in browser.";
|
|
76
|
+
ErrorMessage.NodejsRuntimeNotSupported = "{0} is not supported in Node.";
|
|
77
|
+
// Internal Error
|
|
78
|
+
ErrorMessage.FailToAcquireTokenOnBehalfOfUser = "Failed to acquire access token on behalf of user: {0}";
|
|
79
|
+
// ChannelNotSupported Error
|
|
80
|
+
ErrorMessage.OnlyMSTeamsChannelSupported = "{0} is only supported in MS Teams Channel";
|
|
81
|
+
/**
|
|
82
|
+
* Error class with code and message thrown by the SDK.
|
|
83
|
+
*
|
|
84
|
+
* @beta
|
|
85
|
+
*/
|
|
86
|
+
class ErrorWithCode extends Error {
|
|
87
|
+
/**
|
|
88
|
+
* Constructor of ErrorWithCode.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} message - error message.
|
|
91
|
+
* @param {ErrorCode} code - error code.
|
|
92
|
+
*
|
|
93
|
+
* @beta
|
|
94
|
+
*/
|
|
95
|
+
constructor(message, code) {
|
|
96
|
+
if (!code) {
|
|
97
|
+
super(message);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
super(message);
|
|
101
|
+
Object.setPrototypeOf(this, ErrorWithCode.prototype);
|
|
102
|
+
this.name = `${new.target.name}.${code}`;
|
|
103
|
+
this.code = code;
|
|
104
|
+
}
|
|
115
105
|
}
|
|
116
106
|
|
|
117
|
-
// Copyright (c) Microsoft Corporation.
|
|
118
|
-
// Licensed under the MIT license.
|
|
119
|
-
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
*
|
|
128
|
-
*/
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
*
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
*/
|
|
137
|
-
LogLevel[LogLevel["Warn"] = 2] = "Warn";
|
|
138
|
-
/**
|
|
139
|
-
* Show error message.
|
|
140
|
-
*/
|
|
141
|
-
LogLevel[LogLevel["Error"] = 3] = "Error";
|
|
142
|
-
})(exports.LogLevel || (exports.LogLevel = {}));
|
|
143
|
-
/**
|
|
144
|
-
* Update log level helper.
|
|
145
|
-
*
|
|
146
|
-
* @param { LogLevel } level - log level in configuration
|
|
147
|
-
*
|
|
148
|
-
* @beta
|
|
149
|
-
*/
|
|
150
|
-
function setLogLevel(level) {
|
|
151
|
-
internalLogger.level = level;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Get log level.
|
|
155
|
-
*
|
|
156
|
-
* @returns Log level
|
|
157
|
-
*
|
|
158
|
-
* @beta
|
|
159
|
-
*/
|
|
160
|
-
function getLogLevel() {
|
|
161
|
-
return internalLogger.level;
|
|
162
|
-
}
|
|
163
|
-
class InternalLogger {
|
|
164
|
-
constructor() {
|
|
165
|
-
this.level = undefined;
|
|
166
|
-
this.defaultLogger = {
|
|
167
|
-
verbose: console.debug,
|
|
168
|
-
info: console.info,
|
|
169
|
-
warn: console.warn,
|
|
170
|
-
error: console.error,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
error(message) {
|
|
174
|
-
this.log(exports.LogLevel.Error, (x) => x.error, message);
|
|
175
|
-
}
|
|
176
|
-
warn(message) {
|
|
177
|
-
this.log(exports.LogLevel.Warn, (x) => x.warn, message);
|
|
178
|
-
}
|
|
179
|
-
info(message) {
|
|
180
|
-
this.log(exports.LogLevel.Info, (x) => x.info, message);
|
|
181
|
-
}
|
|
182
|
-
verbose(message) {
|
|
183
|
-
this.log(exports.LogLevel.Verbose, (x) => x.verbose, message);
|
|
184
|
-
}
|
|
185
|
-
log(logLevel, logFunction, message) {
|
|
186
|
-
if (message.trim() === "") {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
const timestamp = new Date().toUTCString();
|
|
190
|
-
const logHeader = `[${timestamp}] : @microsoft/teamsfx : ${exports.LogLevel[logLevel]} - `;
|
|
191
|
-
const logMessage = `${logHeader}${message}`;
|
|
192
|
-
if (this.level !== undefined && this.level <= logLevel) {
|
|
193
|
-
if (this.customLogger) {
|
|
194
|
-
logFunction(this.customLogger)(logMessage);
|
|
195
|
-
}
|
|
196
|
-
else if (this.customLogFunction) {
|
|
197
|
-
this.customLogFunction(logLevel, logMessage);
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
logFunction(this.defaultLogger)(logMessage);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Logger instance used internally
|
|
207
|
-
*
|
|
208
|
-
* @internal
|
|
209
|
-
*/
|
|
210
|
-
const internalLogger = new InternalLogger();
|
|
211
|
-
/**
|
|
212
|
-
* Set custom logger. Use the output functions if it's set. Priority is higher than setLogFunction.
|
|
213
|
-
*
|
|
214
|
-
* @param {Logger} logger - custom logger. If it's undefined, custom logger will be cleared.
|
|
215
|
-
*
|
|
216
|
-
* @example
|
|
217
|
-
* ```typescript
|
|
218
|
-
* setLogger({
|
|
219
|
-
* verbose: console.debug,
|
|
220
|
-
* info: console.info,
|
|
221
|
-
* warn: console.warn,
|
|
222
|
-
* error: console.error,
|
|
223
|
-
* });
|
|
224
|
-
* ```
|
|
225
|
-
*
|
|
226
|
-
* @beta
|
|
227
|
-
*/
|
|
228
|
-
function setLogger(logger) {
|
|
229
|
-
internalLogger.customLogger = logger;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Set custom log function. Use the function if it's set. Priority is lower than setLogger.
|
|
233
|
-
*
|
|
234
|
-
* @param {LogFunction} logFunction - custom log function. If it's undefined, custom log function will be cleared.
|
|
235
|
-
*
|
|
236
|
-
* @example
|
|
237
|
-
* ```typescript
|
|
238
|
-
* setLogFunction((level: LogLevel, message: string) => {
|
|
239
|
-
* if (level === LogLevel.Error) {
|
|
240
|
-
* console.log(message);
|
|
241
|
-
* }
|
|
242
|
-
* });
|
|
243
|
-
* ```
|
|
244
|
-
*
|
|
245
|
-
* @beta
|
|
246
|
-
*/
|
|
247
|
-
function setLogFunction(logFunction) {
|
|
248
|
-
internalLogger.customLogFunction = logFunction;
|
|
249
|
-
}
|
|
107
|
+
// Copyright (c) Microsoft Corporation.
|
|
108
|
+
// Licensed under the MIT license.
|
|
109
|
+
/**
|
|
110
|
+
* Available resource type.
|
|
111
|
+
* @beta
|
|
112
|
+
*/
|
|
113
|
+
var ResourceType;
|
|
114
|
+
(function (ResourceType) {
|
|
115
|
+
/**
|
|
116
|
+
* SQL database.
|
|
117
|
+
*
|
|
118
|
+
*/
|
|
119
|
+
ResourceType[ResourceType["SQL"] = 0] = "SQL";
|
|
120
|
+
/**
|
|
121
|
+
* Rest API.
|
|
122
|
+
*
|
|
123
|
+
*/
|
|
124
|
+
ResourceType[ResourceType["API"] = 1] = "API";
|
|
125
|
+
})(ResourceType || (ResourceType = {}));
|
|
250
126
|
|
|
251
|
-
// Copyright (c) Microsoft Corporation.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
*
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
*
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
127
|
+
// Copyright (c) Microsoft Corporation.
|
|
128
|
+
// Licensed under the MIT license.
|
|
129
|
+
/**
|
|
130
|
+
* Log level.
|
|
131
|
+
*
|
|
132
|
+
* @beta
|
|
133
|
+
*/
|
|
134
|
+
var LogLevel;
|
|
135
|
+
(function (LogLevel) {
|
|
136
|
+
/**
|
|
137
|
+
* Show verbose, information, warning and error message.
|
|
138
|
+
*/
|
|
139
|
+
LogLevel[LogLevel["Verbose"] = 0] = "Verbose";
|
|
140
|
+
/**
|
|
141
|
+
* Show information, warning and error message.
|
|
142
|
+
*/
|
|
143
|
+
LogLevel[LogLevel["Info"] = 1] = "Info";
|
|
144
|
+
/**
|
|
145
|
+
* Show warning and error message.
|
|
146
|
+
*/
|
|
147
|
+
LogLevel[LogLevel["Warn"] = 2] = "Warn";
|
|
148
|
+
/**
|
|
149
|
+
* Show error message.
|
|
150
|
+
*/
|
|
151
|
+
LogLevel[LogLevel["Error"] = 3] = "Error";
|
|
152
|
+
})(LogLevel || (LogLevel = {}));
|
|
153
|
+
/**
|
|
154
|
+
* Update log level helper.
|
|
155
|
+
*
|
|
156
|
+
* @param { LogLevel } level - log level in configuration
|
|
157
|
+
*
|
|
158
|
+
* @beta
|
|
159
|
+
*/
|
|
160
|
+
function setLogLevel(level) {
|
|
161
|
+
internalLogger.level = level;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get log level.
|
|
165
|
+
*
|
|
166
|
+
* @returns Log level
|
|
167
|
+
*
|
|
168
|
+
* @beta
|
|
169
|
+
*/
|
|
170
|
+
function getLogLevel() {
|
|
171
|
+
return internalLogger.level;
|
|
172
|
+
}
|
|
173
|
+
class InternalLogger {
|
|
174
|
+
constructor() {
|
|
175
|
+
this.level = undefined;
|
|
176
|
+
this.defaultLogger = {
|
|
177
|
+
verbose: console.debug,
|
|
178
|
+
info: console.info,
|
|
179
|
+
warn: console.warn,
|
|
180
|
+
error: console.error,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
error(message) {
|
|
184
|
+
this.log(LogLevel.Error, (x) => x.error, message);
|
|
185
|
+
}
|
|
186
|
+
warn(message) {
|
|
187
|
+
this.log(LogLevel.Warn, (x) => x.warn, message);
|
|
188
|
+
}
|
|
189
|
+
info(message) {
|
|
190
|
+
this.log(LogLevel.Info, (x) => x.info, message);
|
|
191
|
+
}
|
|
192
|
+
verbose(message) {
|
|
193
|
+
this.log(LogLevel.Verbose, (x) => x.verbose, message);
|
|
194
|
+
}
|
|
195
|
+
log(logLevel, logFunction, message) {
|
|
196
|
+
if (message.trim() === "") {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const timestamp = new Date().toUTCString();
|
|
200
|
+
const logHeader = `[${timestamp}] : @microsoft/teamsfx : ${LogLevel[logLevel]} - `;
|
|
201
|
+
const logMessage = `${logHeader}${message}`;
|
|
202
|
+
if (this.level !== undefined && this.level <= logLevel) {
|
|
203
|
+
if (this.customLogger) {
|
|
204
|
+
logFunction(this.customLogger)(logMessage);
|
|
205
|
+
}
|
|
206
|
+
else if (this.customLogFunction) {
|
|
207
|
+
this.customLogFunction(logLevel, logMessage);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
logFunction(this.defaultLogger)(logMessage);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Logger instance used internally
|
|
217
|
+
*
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
const internalLogger = new InternalLogger();
|
|
221
|
+
/**
|
|
222
|
+
* Set custom logger. Use the output functions if it's set. Priority is higher than setLogFunction.
|
|
223
|
+
*
|
|
224
|
+
* @param {Logger} logger - custom logger. If it's undefined, custom logger will be cleared.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* setLogger({
|
|
229
|
+
* verbose: console.debug,
|
|
230
|
+
* info: console.info,
|
|
231
|
+
* warn: console.warn,
|
|
232
|
+
* error: console.error,
|
|
233
|
+
* });
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* @beta
|
|
237
|
+
*/
|
|
238
|
+
function setLogger(logger) {
|
|
239
|
+
internalLogger.customLogger = logger;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Set custom log function. Use the function if it's set. Priority is lower than setLogger.
|
|
243
|
+
*
|
|
244
|
+
* @param {LogFunction} logFunction - custom log function. If it's undefined, custom log function will be cleared.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* setLogFunction((level: LogLevel, message: string) => {
|
|
249
|
+
* if (level === LogLevel.Error) {
|
|
250
|
+
* console.log(message);
|
|
251
|
+
* }
|
|
252
|
+
* });
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* @beta
|
|
256
|
+
*/
|
|
257
|
+
function setLogFunction(logFunction) {
|
|
258
|
+
internalLogger.customLogFunction = logFunction;
|
|
374
259
|
}
|
|
375
260
|
|
|
376
|
-
// Copyright (c) Microsoft Corporation.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
*
|
|
380
|
-
* @
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
261
|
+
// Copyright (c) Microsoft Corporation.
|
|
262
|
+
/**
|
|
263
|
+
* Parse jwt token payload
|
|
264
|
+
*
|
|
265
|
+
* @param token
|
|
266
|
+
*
|
|
267
|
+
* @returns Payload object
|
|
268
|
+
*
|
|
269
|
+
* @internal
|
|
270
|
+
*/
|
|
271
|
+
function parseJwt(token) {
|
|
272
|
+
try {
|
|
273
|
+
const tokenObj = jwt_decode(token);
|
|
274
|
+
if (!tokenObj || !tokenObj.exp) {
|
|
275
|
+
throw new ErrorWithCode("Decoded token is null or exp claim does not exists.", ErrorCode.InternalError);
|
|
276
|
+
}
|
|
277
|
+
return tokenObj;
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
const errorMsg = "Parse jwt token failed in node env with error: " + err.message;
|
|
281
|
+
internalLogger.error(errorMsg);
|
|
282
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* @internal
|
|
287
|
+
*/
|
|
288
|
+
function getUserInfoFromSsoToken(ssoToken) {
|
|
289
|
+
if (!ssoToken) {
|
|
290
|
+
const errorMsg = "SSO token is undefined.";
|
|
291
|
+
internalLogger.error(errorMsg);
|
|
292
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
|
|
293
|
+
}
|
|
294
|
+
const tokenObject = parseJwt(ssoToken);
|
|
295
|
+
const userInfo = {
|
|
296
|
+
displayName: tokenObject.name,
|
|
297
|
+
objectId: tokenObject.oid,
|
|
298
|
+
preferredUserName: "",
|
|
299
|
+
};
|
|
300
|
+
if (tokenObject.ver === "2.0") {
|
|
301
|
+
userInfo.preferredUserName = tokenObject.preferred_username;
|
|
302
|
+
}
|
|
303
|
+
else if (tokenObject.ver === "1.0") {
|
|
304
|
+
userInfo.preferredUserName = tokenObject.upn;
|
|
305
|
+
}
|
|
306
|
+
return userInfo;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Format string template with replacements
|
|
310
|
+
*
|
|
311
|
+
* ```typescript
|
|
312
|
+
* const template = "{0} and {1} are fruit. {0} is my favorite one."
|
|
313
|
+
* const formattedStr = formatString(template, "apple", "pear"); // formattedStr: "apple and pear are fruit. apple is my favorite one."
|
|
314
|
+
* ```
|
|
315
|
+
*
|
|
316
|
+
* @param str string template
|
|
317
|
+
* @param replacements replacement string array
|
|
318
|
+
* @returns Formatted string
|
|
319
|
+
*
|
|
320
|
+
* @internal
|
|
321
|
+
*/
|
|
322
|
+
function formatString(str, ...replacements) {
|
|
323
|
+
const args = replacements;
|
|
324
|
+
return str.replace(/{(\d+)}/g, function (match, number) {
|
|
325
|
+
return typeof args[number] != "undefined" ? args[number] : match;
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* @internal
|
|
330
|
+
*/
|
|
331
|
+
function validateScopesType(value) {
|
|
332
|
+
// string
|
|
333
|
+
if (typeof value === "string" || value instanceof String) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// empty array
|
|
337
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
// string array
|
|
341
|
+
if (Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string")) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const errorMsg = "The type of scopes is not valid, it must be string or string array";
|
|
345
|
+
internalLogger.error(errorMsg);
|
|
346
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* @internal
|
|
350
|
+
*/
|
|
351
|
+
function getScopesArray(scopes) {
|
|
352
|
+
const scopesArray = typeof scopes === "string" ? scopes.split(" ") : scopes;
|
|
353
|
+
return scopesArray.filter((x) => x !== null && x !== "");
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* @internal
|
|
357
|
+
*/
|
|
358
|
+
function getAuthority(authorityHost, tenantId) {
|
|
359
|
+
const normalizedAuthorityHost = authorityHost.replace(/\/+$/g, "");
|
|
360
|
+
return normalizedAuthorityHost + "/" + tenantId;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* @internal
|
|
364
|
+
*/
|
|
365
|
+
const isNode = typeof process !== "undefined" &&
|
|
366
|
+
!!process.version &&
|
|
367
|
+
!!process.versions &&
|
|
368
|
+
!!process.versions.node;
|
|
395
369
|
|
|
396
|
-
// Copyright (c) Microsoft Corporation.
|
|
397
|
-
/**
|
|
398
|
-
* Global configuration instance
|
|
399
|
-
*
|
|
400
|
-
*/
|
|
401
|
-
let config;
|
|
402
|
-
/**
|
|
403
|
-
* Initialize configuration from environment variables or configuration object and set the global instance
|
|
404
|
-
*
|
|
405
|
-
* @param {Configuration} configuration - Optional configuration that overrides the default configuration values. The override depth is 1.
|
|
406
|
-
*
|
|
407
|
-
* @throws {@link ErrorCode|InvalidParameter} when configuration is not passed in browser environment
|
|
408
|
-
*
|
|
409
|
-
* @beta
|
|
410
|
-
*/
|
|
411
|
-
function loadConfiguration(configuration) {
|
|
412
|
-
internalLogger.info("load configuration");
|
|
413
|
-
// browser environment
|
|
414
|
-
if (!
|
|
415
|
-
if (!configuration) {
|
|
416
|
-
const errorMsg = "You are running the code in browser. Configuration must be passed in.";
|
|
417
|
-
internalLogger.error(errorMsg);
|
|
418
|
-
throw new ErrorWithCode(errorMsg,
|
|
419
|
-
}
|
|
420
|
-
config = configuration;
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
// node environment
|
|
424
|
-
let newAuthentication;
|
|
425
|
-
let newResources = [];
|
|
426
|
-
const defaultResourceName = "default";
|
|
427
|
-
if (configuration === null || configuration === void 0 ? void 0 : configuration.authentication) {
|
|
428
|
-
newAuthentication = configuration.authentication;
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
newAuthentication = {
|
|
432
|
-
authorityHost: process.env.M365_AUTHORITY_HOST,
|
|
433
|
-
tenantId: process.env.M365_TENANT_ID,
|
|
434
|
-
clientId: process.env.M365_CLIENT_ID,
|
|
435
|
-
clientSecret: process.env.M365_CLIENT_SECRET,
|
|
436
|
-
simpleAuthEndpoint: process.env.SIMPLE_AUTH_ENDPOINT,
|
|
437
|
-
initiateLoginEndpoint: process.env.INITIATE_LOGIN_ENDPOINT,
|
|
438
|
-
applicationIdUri: process.env.M365_APPLICATION_ID_URI,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
if (configuration === null || configuration === void 0 ? void 0 : configuration.resources) {
|
|
442
|
-
newResources = configuration.resources;
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
newResources = [
|
|
446
|
-
{
|
|
447
|
-
// SQL resource
|
|
448
|
-
type:
|
|
449
|
-
name: defaultResourceName,
|
|
450
|
-
properties: {
|
|
451
|
-
sqlServerEndpoint: process.env.SQL_ENDPOINT,
|
|
452
|
-
sqlUsername: process.env.SQL_USER_NAME,
|
|
453
|
-
sqlPassword: process.env.SQL_PASSWORD,
|
|
454
|
-
sqlDatabaseName: process.env.SQL_DATABASE_NAME,
|
|
455
|
-
sqlIdentityId: process.env.IDENTITY_ID,
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
{
|
|
459
|
-
// API resource
|
|
460
|
-
type:
|
|
461
|
-
name: defaultResourceName,
|
|
462
|
-
properties: {
|
|
463
|
-
endpoint: process.env.API_ENDPOINT,
|
|
464
|
-
},
|
|
465
|
-
},
|
|
466
|
-
];
|
|
467
|
-
}
|
|
468
|
-
config = {
|
|
469
|
-
authentication: newAuthentication,
|
|
470
|
-
resources: newResources,
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Get configuration for a specific resource.
|
|
475
|
-
* @param {ResourceType} resourceType - The type of resource
|
|
476
|
-
* @param {string} resourceName - The name of resource, default value is "default".
|
|
477
|
-
*
|
|
478
|
-
* @returns Resource configuration for target resource from global configuration instance.
|
|
479
|
-
*
|
|
480
|
-
* @throws {@link ErrorCode|InvalidConfiguration} when resource configuration with the specific type and name is not found
|
|
481
|
-
*
|
|
482
|
-
* @beta
|
|
483
|
-
*/
|
|
484
|
-
function getResourceConfiguration(resourceType, resourceName = "default") {
|
|
485
|
-
var _a;
|
|
486
|
-
internalLogger.info(`Get resource configuration of ${
|
|
487
|
-
const result = (_a = config.resources) === null || _a === void 0 ? void 0 : _a.find((item) => item.type === resourceType && item.name === resourceName);
|
|
488
|
-
if (result) {
|
|
489
|
-
return result.properties;
|
|
490
|
-
}
|
|
491
|
-
const errorMsg = formatString(ErrorMessage.MissingResourceConfiguration,
|
|
492
|
-
internalLogger.error(errorMsg);
|
|
493
|
-
throw new ErrorWithCode(errorMsg,
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Get configuration for authentication.
|
|
497
|
-
*
|
|
498
|
-
* @returns Authentication configuration from global configuration instance, the value may be undefined if no authentication config exists in current environment.
|
|
499
|
-
*
|
|
500
|
-
* @throws {@link ErrorCode|InvalidConfiguration} when global configuration does not exist
|
|
501
|
-
*
|
|
502
|
-
* @beta
|
|
503
|
-
*/
|
|
504
|
-
function getAuthenticationConfiguration() {
|
|
505
|
-
internalLogger.info("Get authentication configuration");
|
|
506
|
-
if (config) {
|
|
507
|
-
return config.authentication;
|
|
508
|
-
}
|
|
509
|
-
const errorMsg = "Please call loadConfiguration() first before calling getAuthenticationConfiguration().";
|
|
510
|
-
internalLogger.error(errorMsg);
|
|
511
|
-
throw new ErrorWithCode(formatString(ErrorMessage.ConfigurationNotExists, errorMsg),
|
|
370
|
+
// Copyright (c) Microsoft Corporation.
|
|
371
|
+
/**
|
|
372
|
+
* Global configuration instance
|
|
373
|
+
*
|
|
374
|
+
*/
|
|
375
|
+
let config;
|
|
376
|
+
/**
|
|
377
|
+
* Initialize configuration from environment variables or configuration object and set the global instance
|
|
378
|
+
*
|
|
379
|
+
* @param {Configuration} configuration - Optional configuration that overrides the default configuration values. The override depth is 1.
|
|
380
|
+
*
|
|
381
|
+
* @throws {@link ErrorCode|InvalidParameter} when configuration is not passed in browser environment
|
|
382
|
+
*
|
|
383
|
+
* @beta
|
|
384
|
+
*/
|
|
385
|
+
function loadConfiguration(configuration) {
|
|
386
|
+
internalLogger.info("load configuration");
|
|
387
|
+
// browser environment
|
|
388
|
+
if (!isNode) {
|
|
389
|
+
if (!configuration) {
|
|
390
|
+
const errorMsg = "You are running the code in browser. Configuration must be passed in.";
|
|
391
|
+
internalLogger.error(errorMsg);
|
|
392
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
|
|
393
|
+
}
|
|
394
|
+
config = configuration;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
// node environment
|
|
398
|
+
let newAuthentication;
|
|
399
|
+
let newResources = [];
|
|
400
|
+
const defaultResourceName = "default";
|
|
401
|
+
if (configuration === null || configuration === void 0 ? void 0 : configuration.authentication) {
|
|
402
|
+
newAuthentication = configuration.authentication;
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
newAuthentication = {
|
|
406
|
+
authorityHost: process.env.M365_AUTHORITY_HOST,
|
|
407
|
+
tenantId: process.env.M365_TENANT_ID,
|
|
408
|
+
clientId: process.env.M365_CLIENT_ID,
|
|
409
|
+
clientSecret: process.env.M365_CLIENT_SECRET,
|
|
410
|
+
simpleAuthEndpoint: process.env.SIMPLE_AUTH_ENDPOINT,
|
|
411
|
+
initiateLoginEndpoint: process.env.INITIATE_LOGIN_ENDPOINT,
|
|
412
|
+
applicationIdUri: process.env.M365_APPLICATION_ID_URI,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (configuration === null || configuration === void 0 ? void 0 : configuration.resources) {
|
|
416
|
+
newResources = configuration.resources;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
newResources = [
|
|
420
|
+
{
|
|
421
|
+
// SQL resource
|
|
422
|
+
type: ResourceType.SQL,
|
|
423
|
+
name: defaultResourceName,
|
|
424
|
+
properties: {
|
|
425
|
+
sqlServerEndpoint: process.env.SQL_ENDPOINT,
|
|
426
|
+
sqlUsername: process.env.SQL_USER_NAME,
|
|
427
|
+
sqlPassword: process.env.SQL_PASSWORD,
|
|
428
|
+
sqlDatabaseName: process.env.SQL_DATABASE_NAME,
|
|
429
|
+
sqlIdentityId: process.env.IDENTITY_ID,
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
// API resource
|
|
434
|
+
type: ResourceType.API,
|
|
435
|
+
name: defaultResourceName,
|
|
436
|
+
properties: {
|
|
437
|
+
endpoint: process.env.API_ENDPOINT,
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
];
|
|
441
|
+
}
|
|
442
|
+
config = {
|
|
443
|
+
authentication: newAuthentication,
|
|
444
|
+
resources: newResources,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Get configuration for a specific resource.
|
|
449
|
+
* @param {ResourceType} resourceType - The type of resource
|
|
450
|
+
* @param {string} resourceName - The name of resource, default value is "default".
|
|
451
|
+
*
|
|
452
|
+
* @returns Resource configuration for target resource from global configuration instance.
|
|
453
|
+
*
|
|
454
|
+
* @throws {@link ErrorCode|InvalidConfiguration} when resource configuration with the specific type and name is not found
|
|
455
|
+
*
|
|
456
|
+
* @beta
|
|
457
|
+
*/
|
|
458
|
+
function getResourceConfiguration(resourceType, resourceName = "default") {
|
|
459
|
+
var _a;
|
|
460
|
+
internalLogger.info(`Get resource configuration of ${ResourceType[resourceType]} from ${resourceName}`);
|
|
461
|
+
const result = (_a = config.resources) === null || _a === void 0 ? void 0 : _a.find((item) => item.type === resourceType && item.name === resourceName);
|
|
462
|
+
if (result) {
|
|
463
|
+
return result.properties;
|
|
464
|
+
}
|
|
465
|
+
const errorMsg = formatString(ErrorMessage.MissingResourceConfiguration, ResourceType[resourceType], resourceName);
|
|
466
|
+
internalLogger.error(errorMsg);
|
|
467
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Get configuration for authentication.
|
|
471
|
+
*
|
|
472
|
+
* @returns Authentication configuration from global configuration instance, the value may be undefined if no authentication config exists in current environment.
|
|
473
|
+
*
|
|
474
|
+
* @throws {@link ErrorCode|InvalidConfiguration} when global configuration does not exist
|
|
475
|
+
*
|
|
476
|
+
* @beta
|
|
477
|
+
*/
|
|
478
|
+
function getAuthenticationConfiguration() {
|
|
479
|
+
internalLogger.info("Get authentication configuration");
|
|
480
|
+
if (config) {
|
|
481
|
+
return config.authentication;
|
|
482
|
+
}
|
|
483
|
+
const errorMsg = "Please call loadConfiguration() first before calling getAuthenticationConfiguration().";
|
|
484
|
+
internalLogger.error(errorMsg);
|
|
485
|
+
throw new ErrorWithCode(formatString(ErrorMessage.ConfigurationNotExists, errorMsg), ErrorCode.InvalidConfiguration);
|
|
512
486
|
}
|
|
513
487
|
|
|
514
|
-
/**
|
|
515
|
-
* @internal
|
|
516
|
-
*/
|
|
517
|
-
function createConfidentialClientApplication(authentication) {
|
|
518
|
-
const authority = getAuthority(authentication.authorityHost, authentication.tenantId);
|
|
519
|
-
const clientCertificate = parseCertificate(authentication.certificateContent);
|
|
520
|
-
const auth = {
|
|
521
|
-
clientId: authentication.clientId,
|
|
522
|
-
authority: authority,
|
|
523
|
-
};
|
|
524
|
-
if (clientCertificate) {
|
|
525
|
-
auth.clientCertificate = clientCertificate;
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
auth.clientSecret = authentication.clientSecret;
|
|
529
|
-
}
|
|
530
|
-
return new
|
|
531
|
-
auth,
|
|
532
|
-
});
|
|
488
|
+
/**
|
|
489
|
+
* @internal
|
|
490
|
+
*/
|
|
491
|
+
function createConfidentialClientApplication(authentication) {
|
|
492
|
+
const authority = getAuthority(authentication.authorityHost, authentication.tenantId);
|
|
493
|
+
const clientCertificate = parseCertificate(authentication.certificateContent);
|
|
494
|
+
const auth = {
|
|
495
|
+
clientId: authentication.clientId,
|
|
496
|
+
authority: authority,
|
|
497
|
+
};
|
|
498
|
+
if (clientCertificate) {
|
|
499
|
+
auth.clientCertificate = clientCertificate;
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
auth.clientSecret = authentication.clientSecret;
|
|
503
|
+
}
|
|
504
|
+
return new ConfidentialClientApplication({
|
|
505
|
+
auth,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* @internal
|
|
510
|
+
*/
|
|
511
|
+
function parseCertificate(certificateContent) {
|
|
512
|
+
if (!certificateContent) {
|
|
513
|
+
return undefined;
|
|
514
|
+
}
|
|
515
|
+
const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/;
|
|
516
|
+
const match = certificatePattern.exec(certificateContent);
|
|
517
|
+
if (!match) {
|
|
518
|
+
const errorMsg = "The certificate content does not contain a PEM-encoded certificate.";
|
|
519
|
+
internalLogger.error(errorMsg);
|
|
520
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidCertificate);
|
|
521
|
+
}
|
|
522
|
+
const thumbprint = createHash("sha1")
|
|
523
|
+
.update(Buffer.from(match[3], "base64"))
|
|
524
|
+
.digest("hex")
|
|
525
|
+
.toUpperCase();
|
|
526
|
+
return {
|
|
527
|
+
thumbprint: thumbprint,
|
|
528
|
+
privateKey: certificateContent,
|
|
529
|
+
};
|
|
533
530
|
}
|
|
534
531
|
|
|
535
|
-
// Copyright (c) Microsoft Corporation.
|
|
536
|
-
/**
|
|
537
|
-
* Represent Microsoft 365 tenant identity, and it is usually used when user is not involved like time-triggered automation job.
|
|
538
|
-
*
|
|
539
|
-
* @example
|
|
540
|
-
* ```typescript
|
|
541
|
-
* loadConfiguration(); // load configuration from environment variables
|
|
542
|
-
* const credential = new M365TenantCredential();
|
|
543
|
-
* ```
|
|
544
|
-
*
|
|
545
|
-
* @remarks
|
|
546
|
-
* Only works in in server side.
|
|
547
|
-
*
|
|
548
|
-
* @beta
|
|
549
|
-
*/
|
|
550
|
-
class M365TenantCredential {
|
|
551
|
-
/**
|
|
552
|
-
* Constructor of M365TenantCredential.
|
|
553
|
-
*
|
|
554
|
-
* @remarks
|
|
555
|
-
* Only works in in server side.
|
|
556
|
-
*
|
|
557
|
-
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.
|
|
558
|
-
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
559
|
-
*
|
|
560
|
-
* @beta
|
|
561
|
-
*/
|
|
562
|
-
constructor() {
|
|
563
|
-
internalLogger.info("Create M365 tenant credential");
|
|
564
|
-
const config = this.loadAndValidateConfig();
|
|
565
|
-
this.msalClient = createConfidentialClientApplication(config);
|
|
566
|
-
}
|
|
567
|
-
/**
|
|
568
|
-
* Get access token for credential.
|
|
569
|
-
*
|
|
570
|
-
* @example
|
|
571
|
-
* ```typescript
|
|
572
|
-
* await credential.getToken(["User.Read.All"]) // Get Graph access token for single scope using string array
|
|
573
|
-
* await credential.getToken("User.Read.All") // Get Graph access token for single scope using string
|
|
574
|
-
* await credential.getToken(["User.Read.All", "Calendars.Read"]) // Get Graph access token for multiple scopes using string array
|
|
575
|
-
* await credential.getToken("User.Read.All Calendars.Read") // Get Graph access token for multiple scopes using space-separated string
|
|
576
|
-
* await credential.getToken("https://graph.microsoft.com/User.Read.All") // Get Graph access token with full resource URI
|
|
577
|
-
* await credential.getToken(["https://outlook.office.com/Mail.Read"]) // Get Outlook access token
|
|
578
|
-
* ```
|
|
579
|
-
*
|
|
580
|
-
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
581
|
-
* @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.
|
|
582
|
-
*
|
|
583
|
-
* @throws {@link ErrorCode|ServiceError} when get access token with authentication error.
|
|
584
|
-
* @throws {@link ErrorCode|InternalError} when get access token with unknown error.
|
|
585
|
-
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
586
|
-
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
587
|
-
*
|
|
588
|
-
* @returns Access token with expected scopes.
|
|
589
|
-
* Throw error if get access token failed.
|
|
590
|
-
*
|
|
591
|
-
* @beta
|
|
592
|
-
*/
|
|
593
|
-
getToken(scopes, options) {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
accessToken
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
|
|
651
|
-
}
|
|
532
|
+
// Copyright (c) Microsoft Corporation.
|
|
533
|
+
/**
|
|
534
|
+
* Represent Microsoft 365 tenant identity, and it is usually used when user is not involved like time-triggered automation job.
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* ```typescript
|
|
538
|
+
* loadConfiguration(); // load configuration from environment variables
|
|
539
|
+
* const credential = new M365TenantCredential();
|
|
540
|
+
* ```
|
|
541
|
+
*
|
|
542
|
+
* @remarks
|
|
543
|
+
* Only works in in server side.
|
|
544
|
+
*
|
|
545
|
+
* @beta
|
|
546
|
+
*/
|
|
547
|
+
class M365TenantCredential {
|
|
548
|
+
/**
|
|
549
|
+
* Constructor of M365TenantCredential.
|
|
550
|
+
*
|
|
551
|
+
* @remarks
|
|
552
|
+
* Only works in in server side.
|
|
553
|
+
*
|
|
554
|
+
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.
|
|
555
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
556
|
+
*
|
|
557
|
+
* @beta
|
|
558
|
+
*/
|
|
559
|
+
constructor() {
|
|
560
|
+
internalLogger.info("Create M365 tenant credential");
|
|
561
|
+
const config = this.loadAndValidateConfig();
|
|
562
|
+
this.msalClient = createConfidentialClientApplication(config);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Get access token for credential.
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* ```typescript
|
|
569
|
+
* await credential.getToken(["User.Read.All"]) // Get Graph access token for single scope using string array
|
|
570
|
+
* await credential.getToken("User.Read.All") // Get Graph access token for single scope using string
|
|
571
|
+
* await credential.getToken(["User.Read.All", "Calendars.Read"]) // Get Graph access token for multiple scopes using string array
|
|
572
|
+
* await credential.getToken("User.Read.All Calendars.Read") // Get Graph access token for multiple scopes using space-separated string
|
|
573
|
+
* await credential.getToken("https://graph.microsoft.com/User.Read.All") // Get Graph access token with full resource URI
|
|
574
|
+
* await credential.getToken(["https://outlook.office.com/Mail.Read"]) // Get Outlook access token
|
|
575
|
+
* ```
|
|
576
|
+
*
|
|
577
|
+
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
578
|
+
* @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.
|
|
579
|
+
*
|
|
580
|
+
* @throws {@link ErrorCode|ServiceError} when get access token with authentication error.
|
|
581
|
+
* @throws {@link ErrorCode|InternalError} when get access token with unknown error.
|
|
582
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
583
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
584
|
+
*
|
|
585
|
+
* @returns Access token with expected scopes.
|
|
586
|
+
* Throw error if get access token failed.
|
|
587
|
+
*
|
|
588
|
+
* @beta
|
|
589
|
+
*/
|
|
590
|
+
async getToken(scopes, options) {
|
|
591
|
+
let accessToken;
|
|
592
|
+
validateScopesType(scopes);
|
|
593
|
+
const scopesStr = typeof scopes === "string" ? scopes : scopes.join(" ");
|
|
594
|
+
internalLogger.info("Get access token with scopes: " + scopesStr);
|
|
595
|
+
try {
|
|
596
|
+
const scopesArray = getScopesArray(scopes);
|
|
597
|
+
const authenticationResult = await this.msalClient.acquireTokenByClientCredential({
|
|
598
|
+
scopes: scopesArray,
|
|
599
|
+
});
|
|
600
|
+
if (authenticationResult) {
|
|
601
|
+
accessToken = {
|
|
602
|
+
token: authenticationResult.accessToken,
|
|
603
|
+
expiresOnTimestamp: authenticationResult.expiresOn.getTime(),
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
catch (err) {
|
|
608
|
+
const errorMsg = "Get M365 tenant credential failed with error: " + err.message;
|
|
609
|
+
internalLogger.error(errorMsg);
|
|
610
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.ServiceError);
|
|
611
|
+
}
|
|
612
|
+
if (!accessToken) {
|
|
613
|
+
const errorMsg = "Get M365 tenant credential access token failed with empty access token";
|
|
614
|
+
internalLogger.error(errorMsg);
|
|
615
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);
|
|
616
|
+
}
|
|
617
|
+
return accessToken;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Load and validate authentication configuration
|
|
621
|
+
* @returns Authentication configuration
|
|
622
|
+
*/
|
|
623
|
+
loadAndValidateConfig() {
|
|
624
|
+
internalLogger.verbose("Validate authentication configuration");
|
|
625
|
+
const config = getAuthenticationConfiguration();
|
|
626
|
+
if (!config) {
|
|
627
|
+
internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);
|
|
628
|
+
throw new ErrorWithCode(ErrorMessage.AuthenticationConfigurationNotExists, ErrorCode.InvalidConfiguration);
|
|
629
|
+
}
|
|
630
|
+
if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {
|
|
631
|
+
return config;
|
|
632
|
+
}
|
|
633
|
+
const missingValues = [];
|
|
634
|
+
if (!config.clientId) {
|
|
635
|
+
missingValues.push("clientId");
|
|
636
|
+
}
|
|
637
|
+
if (!config.clientSecret && !config.certificateContent) {
|
|
638
|
+
missingValues.push("clientSecret or certificateContent");
|
|
639
|
+
}
|
|
640
|
+
if (!config.tenantId) {
|
|
641
|
+
missingValues.push("tenantId");
|
|
642
|
+
}
|
|
643
|
+
const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingValues.join(", "), "undefined");
|
|
644
|
+
internalLogger.error(errorMsg);
|
|
645
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
|
|
646
|
+
}
|
|
652
647
|
}
|
|
653
648
|
|
|
654
|
-
// Copyright (c) Microsoft Corporation.
|
|
655
|
-
/**
|
|
656
|
-
* Represent on-behalf-of flow to get user identity, and it is designed to be used in server side.
|
|
657
|
-
*
|
|
658
|
-
* @example
|
|
659
|
-
* ```typescript
|
|
660
|
-
* loadConfiguration(); // load configuration from environment variables
|
|
661
|
-
* const credential = new OnBehalfOfUserCredential(ssoToken);
|
|
662
|
-
* ```
|
|
663
|
-
*
|
|
664
|
-
* @remarks
|
|
665
|
-
* Can only be used in server side.
|
|
666
|
-
*
|
|
667
|
-
* @beta
|
|
668
|
-
*/
|
|
669
|
-
class OnBehalfOfUserCredential {
|
|
670
|
-
/**
|
|
671
|
-
* Constructor of OnBehalfOfUserCredential
|
|
672
|
-
*
|
|
673
|
-
* @remarks
|
|
674
|
-
* Only works in in server side.
|
|
675
|
-
*
|
|
676
|
-
* @param {string} ssoToken - User token provided by Teams SSO feature.
|
|
677
|
-
*
|
|
678
|
-
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
|
|
679
|
-
* @throws {@link ErrorCode|InternalError} when SSO token is not valid.
|
|
680
|
-
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
681
|
-
*
|
|
682
|
-
* @beta
|
|
683
|
-
*/
|
|
684
|
-
constructor(ssoToken) {
|
|
685
|
-
var _a, _b, _c, _d, _e;
|
|
686
|
-
internalLogger.info("Get on behalf of user credential");
|
|
687
|
-
const missingConfigurations = [];
|
|
688
|
-
if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.clientId)) {
|
|
689
|
-
missingConfigurations.push("clientId");
|
|
690
|
-
}
|
|
691
|
-
if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.authorityHost)) {
|
|
692
|
-
missingConfigurations.push("authorityHost");
|
|
693
|
-
}
|
|
694
|
-
if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.clientSecret) && !((_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.certificateContent)) {
|
|
695
|
-
missingConfigurations.push("clientSecret or certificateContent");
|
|
696
|
-
}
|
|
697
|
-
if (!((_e = config === null || config === void 0 ? void 0 : config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId)) {
|
|
698
|
-
missingConfigurations.push("tenantId");
|
|
699
|
-
}
|
|
700
|
-
if (missingConfigurations.length != 0) {
|
|
701
|
-
const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
|
|
702
|
-
internalLogger.error(errorMsg);
|
|
703
|
-
throw new ErrorWithCode(errorMsg,
|
|
704
|
-
}
|
|
705
|
-
this.msalClient = createConfidentialClientApplication(config.authentication);
|
|
706
|
-
const decodedSsoToken = parseJwt(ssoToken);
|
|
707
|
-
this.ssoToken = {
|
|
708
|
-
token: ssoToken,
|
|
709
|
-
expiresOnTimestamp: decodedSsoToken.exp,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
/**
|
|
713
|
-
* Get access token from credential.
|
|
714
|
-
*
|
|
715
|
-
* @example
|
|
716
|
-
* ```typescript
|
|
717
|
-
* await credential.getToken([]) // Get SSO token using empty string array
|
|
718
|
-
* await credential.getToken("") // Get SSO token using empty string
|
|
719
|
-
* await credential.getToken([".default"]) // Get Graph access token with default scope using string array
|
|
720
|
-
* await credential.getToken(".default") // Get Graph access token with default scope using string
|
|
721
|
-
* await credential.getToken(["User.Read"]) // Get Graph access token for single scope using string array
|
|
722
|
-
* await credential.getToken("User.Read") // Get Graph access token for single scope using string
|
|
723
|
-
* await credential.getToken(["User.Read", "Application.Read.All"]) // Get Graph access token for multiple scopes using string array
|
|
724
|
-
* await credential.getToken("User.Read Application.Read.All") // Get Graph access token for multiple scopes using space-separated string
|
|
725
|
-
* await credential.getToken("https://graph.microsoft.com/User.Read") // Get Graph access token with full resource URI
|
|
726
|
-
* await credential.getToken(["https://outlook.office.com/Mail.Read"]) // Get Outlook access token
|
|
727
|
-
* ```
|
|
728
|
-
*
|
|
729
|
-
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
730
|
-
* @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.
|
|
731
|
-
*
|
|
732
|
-
* @throws {@link ErrorCode|InternalError} when failed to acquire access token on behalf of user with unknown error.
|
|
733
|
-
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
734
|
-
* @throws {@link ErrorCode|UiRequiredError} when need user consent to get access token.
|
|
735
|
-
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
|
|
736
|
-
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
737
|
-
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
738
|
-
*
|
|
739
|
-
* @returns Access token with expected scopes.
|
|
740
|
-
*
|
|
741
|
-
* @remarks
|
|
742
|
-
* If scopes is empty string or array, it returns SSO token.
|
|
743
|
-
* If scopes is non-empty, it returns access token for target scope.
|
|
744
|
-
*
|
|
745
|
-
* @beta
|
|
746
|
-
*/
|
|
747
|
-
getToken(scopes, options) {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
*
|
|
790
|
-
* ```
|
|
791
|
-
*
|
|
792
|
-
*
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
* @
|
|
796
|
-
*
|
|
797
|
-
* @
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
822
|
-
}
|
|
649
|
+
// Copyright (c) Microsoft Corporation.
|
|
650
|
+
/**
|
|
651
|
+
* Represent on-behalf-of flow to get user identity, and it is designed to be used in server side.
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* ```typescript
|
|
655
|
+
* loadConfiguration(); // load configuration from environment variables
|
|
656
|
+
* const credential = new OnBehalfOfUserCredential(ssoToken);
|
|
657
|
+
* ```
|
|
658
|
+
*
|
|
659
|
+
* @remarks
|
|
660
|
+
* Can only be used in server side.
|
|
661
|
+
*
|
|
662
|
+
* @beta
|
|
663
|
+
*/
|
|
664
|
+
class OnBehalfOfUserCredential {
|
|
665
|
+
/**
|
|
666
|
+
* Constructor of OnBehalfOfUserCredential
|
|
667
|
+
*
|
|
668
|
+
* @remarks
|
|
669
|
+
* Only works in in server side.
|
|
670
|
+
*
|
|
671
|
+
* @param {string} ssoToken - User token provided by Teams SSO feature.
|
|
672
|
+
*
|
|
673
|
+
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
|
|
674
|
+
* @throws {@link ErrorCode|InternalError} when SSO token is not valid.
|
|
675
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
676
|
+
*
|
|
677
|
+
* @beta
|
|
678
|
+
*/
|
|
679
|
+
constructor(ssoToken) {
|
|
680
|
+
var _a, _b, _c, _d, _e;
|
|
681
|
+
internalLogger.info("Get on behalf of user credential");
|
|
682
|
+
const missingConfigurations = [];
|
|
683
|
+
if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.clientId)) {
|
|
684
|
+
missingConfigurations.push("clientId");
|
|
685
|
+
}
|
|
686
|
+
if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.authorityHost)) {
|
|
687
|
+
missingConfigurations.push("authorityHost");
|
|
688
|
+
}
|
|
689
|
+
if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.clientSecret) && !((_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.certificateContent)) {
|
|
690
|
+
missingConfigurations.push("clientSecret or certificateContent");
|
|
691
|
+
}
|
|
692
|
+
if (!((_e = config === null || config === void 0 ? void 0 : config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId)) {
|
|
693
|
+
missingConfigurations.push("tenantId");
|
|
694
|
+
}
|
|
695
|
+
if (missingConfigurations.length != 0) {
|
|
696
|
+
const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
|
|
697
|
+
internalLogger.error(errorMsg);
|
|
698
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
|
|
699
|
+
}
|
|
700
|
+
this.msalClient = createConfidentialClientApplication(config.authentication);
|
|
701
|
+
const decodedSsoToken = parseJwt(ssoToken);
|
|
702
|
+
this.ssoToken = {
|
|
703
|
+
token: ssoToken,
|
|
704
|
+
expiresOnTimestamp: decodedSsoToken.exp,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Get access token from credential.
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* ```typescript
|
|
712
|
+
* await credential.getToken([]) // Get SSO token using empty string array
|
|
713
|
+
* await credential.getToken("") // Get SSO token using empty string
|
|
714
|
+
* await credential.getToken([".default"]) // Get Graph access token with default scope using string array
|
|
715
|
+
* await credential.getToken(".default") // Get Graph access token with default scope using string
|
|
716
|
+
* await credential.getToken(["User.Read"]) // Get Graph access token for single scope using string array
|
|
717
|
+
* await credential.getToken("User.Read") // Get Graph access token for single scope using string
|
|
718
|
+
* await credential.getToken(["User.Read", "Application.Read.All"]) // Get Graph access token for multiple scopes using string array
|
|
719
|
+
* await credential.getToken("User.Read Application.Read.All") // Get Graph access token for multiple scopes using space-separated string
|
|
720
|
+
* await credential.getToken("https://graph.microsoft.com/User.Read") // Get Graph access token with full resource URI
|
|
721
|
+
* await credential.getToken(["https://outlook.office.com/Mail.Read"]) // Get Outlook access token
|
|
722
|
+
* ```
|
|
723
|
+
*
|
|
724
|
+
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
725
|
+
* @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.
|
|
726
|
+
*
|
|
727
|
+
* @throws {@link ErrorCode|InternalError} when failed to acquire access token on behalf of user with unknown error.
|
|
728
|
+
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
729
|
+
* @throws {@link ErrorCode|UiRequiredError} when need user consent to get access token.
|
|
730
|
+
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
|
|
731
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
732
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
733
|
+
*
|
|
734
|
+
* @returns Access token with expected scopes.
|
|
735
|
+
*
|
|
736
|
+
* @remarks
|
|
737
|
+
* If scopes is empty string or array, it returns SSO token.
|
|
738
|
+
* If scopes is non-empty, it returns access token for target scope.
|
|
739
|
+
*
|
|
740
|
+
* @beta
|
|
741
|
+
*/
|
|
742
|
+
async getToken(scopes, options) {
|
|
743
|
+
validateScopesType(scopes);
|
|
744
|
+
const scopesArray = getScopesArray(scopes);
|
|
745
|
+
let result;
|
|
746
|
+
if (!scopesArray.length) {
|
|
747
|
+
internalLogger.info("Get SSO token.");
|
|
748
|
+
if (Math.floor(Date.now() / 1000) > this.ssoToken.expiresOnTimestamp) {
|
|
749
|
+
const errorMsg = "Sso token has already expired.";
|
|
750
|
+
internalLogger.error(errorMsg);
|
|
751
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.TokenExpiredError);
|
|
752
|
+
}
|
|
753
|
+
result = this.ssoToken;
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
internalLogger.info("Get access token with scopes: " + scopesArray.join(" "));
|
|
757
|
+
let authenticationResult;
|
|
758
|
+
try {
|
|
759
|
+
authenticationResult = await this.msalClient.acquireTokenOnBehalfOf({
|
|
760
|
+
oboAssertion: this.ssoToken.token,
|
|
761
|
+
scopes: scopesArray,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
catch (error) {
|
|
765
|
+
throw this.generateAuthServerError(error);
|
|
766
|
+
}
|
|
767
|
+
if (!authenticationResult) {
|
|
768
|
+
const errorMsg = "Access token is null";
|
|
769
|
+
internalLogger.error(errorMsg);
|
|
770
|
+
throw new ErrorWithCode(formatString(ErrorMessage.FailToAcquireTokenOnBehalfOfUser, errorMsg), ErrorCode.InternalError);
|
|
771
|
+
}
|
|
772
|
+
result = {
|
|
773
|
+
token: authenticationResult.accessToken,
|
|
774
|
+
expiresOnTimestamp: authenticationResult.expiresOn.getTime(),
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Get basic user info from SSO token.
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```typescript
|
|
784
|
+
* const currentUser = getUserInfo();
|
|
785
|
+
* ```
|
|
786
|
+
*
|
|
787
|
+
* @throws {@link ErrorCode|InternalError} when SSO token is not valid.
|
|
788
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
789
|
+
*
|
|
790
|
+
* @returns Basic user info with user displayName, objectId and preferredUserName.
|
|
791
|
+
*
|
|
792
|
+
* @beta
|
|
793
|
+
*/
|
|
794
|
+
getUserInfo() {
|
|
795
|
+
internalLogger.info("Get basic user info from SSO token");
|
|
796
|
+
return getUserInfoFromSsoToken(this.ssoToken.token);
|
|
797
|
+
}
|
|
798
|
+
generateAuthServerError(err) {
|
|
799
|
+
const errorMessage = err.errorMessage;
|
|
800
|
+
if (err.name === "InteractionRequiredAuthError") {
|
|
801
|
+
const fullErrorMsg = "Failed to get access token from AAD server, interaction required: " + errorMessage;
|
|
802
|
+
internalLogger.warn(fullErrorMsg);
|
|
803
|
+
return new ErrorWithCode(fullErrorMsg, ErrorCode.UiRequiredError);
|
|
804
|
+
}
|
|
805
|
+
else if (errorMessage && errorMessage.indexOf("AADSTS500133") >= 0) {
|
|
806
|
+
const fullErrorMsg = "Failed to get access token from AAD server, sso token expired: " + errorMessage;
|
|
807
|
+
internalLogger.error(fullErrorMsg);
|
|
808
|
+
return new ErrorWithCode(fullErrorMsg, ErrorCode.TokenExpiredError);
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
const fullErrorMsg = formatString(ErrorMessage.FailToAcquireTokenOnBehalfOfUser, errorMessage);
|
|
812
|
+
internalLogger.error(fullErrorMsg);
|
|
813
|
+
return new ErrorWithCode(fullErrorMsg, ErrorCode.ServiceError);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
823
816
|
}
|
|
824
817
|
|
|
825
|
-
// Copyright (c) Microsoft Corporation.
|
|
826
|
-
/**
|
|
827
|
-
* Represent Teams current user's identity, and it is used within Teams client applications.
|
|
828
|
-
*
|
|
829
|
-
* @remarks
|
|
830
|
-
* Can only be used within Teams.
|
|
831
|
-
*
|
|
832
|
-
* @beta
|
|
833
|
-
*/
|
|
834
|
-
class TeamsUserCredential {
|
|
835
|
-
/**
|
|
836
|
-
* Constructor of TeamsUserCredential.
|
|
837
|
-
* @remarks
|
|
838
|
-
* Can only be used within Teams.
|
|
839
|
-
* @beta
|
|
840
|
-
*/
|
|
841
|
-
constructor() {
|
|
842
|
-
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"),
|
|
843
|
-
}
|
|
844
|
-
/**
|
|
845
|
-
* Popup login page to get user's access token with specific scopes.
|
|
846
|
-
* @remarks
|
|
847
|
-
* Can only be used within Teams.
|
|
848
|
-
* @beta
|
|
849
|
-
*/
|
|
850
|
-
login(scopes) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
*
|
|
857
|
-
* @
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
*/
|
|
872
|
-
getUserInfo() {
|
|
873
|
-
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), exports.ErrorCode.RuntimeNotSupported);
|
|
874
|
-
}
|
|
818
|
+
// Copyright (c) Microsoft Corporation.
|
|
819
|
+
/**
|
|
820
|
+
* Represent Teams current user's identity, and it is used within Teams client applications.
|
|
821
|
+
*
|
|
822
|
+
* @remarks
|
|
823
|
+
* Can only be used within Teams.
|
|
824
|
+
*
|
|
825
|
+
* @beta
|
|
826
|
+
*/
|
|
827
|
+
class TeamsUserCredential {
|
|
828
|
+
/**
|
|
829
|
+
* Constructor of TeamsUserCredential.
|
|
830
|
+
* @remarks
|
|
831
|
+
* Can only be used within Teams.
|
|
832
|
+
* @beta
|
|
833
|
+
*/
|
|
834
|
+
constructor() {
|
|
835
|
+
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Popup login page to get user's access token with specific scopes.
|
|
839
|
+
* @remarks
|
|
840
|
+
* Can only be used within Teams.
|
|
841
|
+
* @beta
|
|
842
|
+
*/
|
|
843
|
+
async login(scopes) {
|
|
844
|
+
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Get access token from credential.
|
|
848
|
+
* @remarks
|
|
849
|
+
* Can only be used within Teams.
|
|
850
|
+
* @beta
|
|
851
|
+
*/
|
|
852
|
+
async getToken(scopes, options) {
|
|
853
|
+
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Get basic user info from SSO token
|
|
857
|
+
* @remarks
|
|
858
|
+
* Can only be used within Teams.
|
|
859
|
+
* @beta
|
|
860
|
+
*/
|
|
861
|
+
getUserInfo() {
|
|
862
|
+
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
863
|
+
}
|
|
875
864
|
}
|
|
876
865
|
|
|
877
|
-
// Copyright (c) Microsoft Corporation.
|
|
878
|
-
const defaultScope = "https://graph.microsoft.com/.default";
|
|
879
|
-
/**
|
|
880
|
-
* Microsoft Graph auth provider for Teams Framework
|
|
881
|
-
*
|
|
882
|
-
* @beta
|
|
883
|
-
*/
|
|
884
|
-
class MsGraphAuthProvider {
|
|
885
|
-
/**
|
|
886
|
-
* Constructor of MsGraphAuthProvider.
|
|
887
|
-
*
|
|
888
|
-
* @param {TokenCredential} credential - Credential used to invoke Microsoft Graph APIs.
|
|
889
|
-
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
890
|
-
*
|
|
891
|
-
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
892
|
-
*
|
|
893
|
-
* @returns An instance of MsGraphAuthProvider.
|
|
894
|
-
*
|
|
895
|
-
* @beta
|
|
896
|
-
*/
|
|
897
|
-
constructor(credential, scopes) {
|
|
898
|
-
this.credential = credential;
|
|
899
|
-
let scopesStr = defaultScope;
|
|
900
|
-
if (scopes) {
|
|
901
|
-
validateScopesType(scopes);
|
|
902
|
-
scopesStr = typeof scopes === "string" ? scopes : scopes.join(" ");
|
|
903
|
-
if (scopesStr === "") {
|
|
904
|
-
scopesStr = defaultScope;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
internalLogger.info(`Create Microsoft Graph Authentication Provider with scopes: '${scopesStr}'`);
|
|
908
|
-
this.scopes = scopesStr;
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Get access token for Microsoft Graph API requests.
|
|
912
|
-
*
|
|
913
|
-
* @throws {@link ErrorCode|InternalError} when get access token failed due to empty token or unknown other problems.
|
|
914
|
-
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
915
|
-
* @throws {@link ErrorCode|UiRequiredError} when need user consent to get access token.
|
|
916
|
-
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth or AAD server.
|
|
917
|
-
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
918
|
-
*
|
|
919
|
-
* @returns Access token from the credential.
|
|
920
|
-
*
|
|
921
|
-
*/
|
|
922
|
-
getAccessToken() {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
});
|
|
937
|
-
}
|
|
866
|
+
// Copyright (c) Microsoft Corporation.
|
|
867
|
+
const defaultScope = "https://graph.microsoft.com/.default";
|
|
868
|
+
/**
|
|
869
|
+
* Microsoft Graph auth provider for Teams Framework
|
|
870
|
+
*
|
|
871
|
+
* @beta
|
|
872
|
+
*/
|
|
873
|
+
class MsGraphAuthProvider {
|
|
874
|
+
/**
|
|
875
|
+
* Constructor of MsGraphAuthProvider.
|
|
876
|
+
*
|
|
877
|
+
* @param {TokenCredential} credential - Credential used to invoke Microsoft Graph APIs.
|
|
878
|
+
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
879
|
+
*
|
|
880
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
881
|
+
*
|
|
882
|
+
* @returns An instance of MsGraphAuthProvider.
|
|
883
|
+
*
|
|
884
|
+
* @beta
|
|
885
|
+
*/
|
|
886
|
+
constructor(credential, scopes) {
|
|
887
|
+
this.credential = credential;
|
|
888
|
+
let scopesStr = defaultScope;
|
|
889
|
+
if (scopes) {
|
|
890
|
+
validateScopesType(scopes);
|
|
891
|
+
scopesStr = typeof scopes === "string" ? scopes : scopes.join(" ");
|
|
892
|
+
if (scopesStr === "") {
|
|
893
|
+
scopesStr = defaultScope;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
internalLogger.info(`Create Microsoft Graph Authentication Provider with scopes: '${scopesStr}'`);
|
|
897
|
+
this.scopes = scopesStr;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Get access token for Microsoft Graph API requests.
|
|
901
|
+
*
|
|
902
|
+
* @throws {@link ErrorCode|InternalError} when get access token failed due to empty token or unknown other problems.
|
|
903
|
+
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
904
|
+
* @throws {@link ErrorCode|UiRequiredError} when need user consent to get access token.
|
|
905
|
+
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth or AAD server.
|
|
906
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
907
|
+
*
|
|
908
|
+
* @returns Access token from the credential.
|
|
909
|
+
*
|
|
910
|
+
*/
|
|
911
|
+
async getAccessToken() {
|
|
912
|
+
internalLogger.info(`Get Graph Access token with scopes: '${this.scopes}'`);
|
|
913
|
+
const accessToken = await this.credential.getToken(this.scopes);
|
|
914
|
+
return new Promise((resolve, reject) => {
|
|
915
|
+
if (accessToken) {
|
|
916
|
+
resolve(accessToken.token);
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
const errorMsg = "Graph access token is undefined or empty";
|
|
920
|
+
internalLogger.error(errorMsg);
|
|
921
|
+
reject(new ErrorWithCode(errorMsg, ErrorCode.InternalError));
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
}
|
|
938
925
|
}
|
|
939
926
|
|
|
940
|
-
// Copyright (c) Microsoft Corporation.
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
*
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
*
|
|
962
|
-
*
|
|
963
|
-
*
|
|
964
|
-
*
|
|
965
|
-
*
|
|
966
|
-
*
|
|
967
|
-
*
|
|
968
|
-
*
|
|
969
|
-
*
|
|
970
|
-
*
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
*
|
|
984
|
-
*
|
|
985
|
-
*
|
|
986
|
-
*
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
* ]));
|
|
1000
|
-
* ```
|
|
1001
|
-
*
|
|
1002
|
-
* @beta
|
|
1003
|
-
*/
|
|
1004
|
-
class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
1005
|
-
/**
|
|
1006
|
-
* Constructor of TeamsBotSsoPrompt.
|
|
1007
|
-
*
|
|
1008
|
-
* @param dialogId Unique ID of the dialog within its parent `DialogSet` or `ComponentDialog`.
|
|
1009
|
-
* @param settings Settings used to configure the prompt.
|
|
1010
|
-
*
|
|
1011
|
-
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
1012
|
-
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1013
|
-
*
|
|
1014
|
-
* @beta
|
|
1015
|
-
*/
|
|
1016
|
-
constructor(dialogId, settings) {
|
|
1017
|
-
super(dialogId);
|
|
1018
|
-
this.settings = settings;
|
|
1019
|
-
validateScopesType(settings.scopes);
|
|
1020
|
-
internalLogger.info("Create a new Teams Bot SSO Prompt");
|
|
1021
|
-
}
|
|
1022
|
-
/**
|
|
1023
|
-
* Called when a prompt dialog is pushed onto the dialog stack and is being activated.
|
|
1024
|
-
* @remarks
|
|
1025
|
-
* If the task is successful, the result indicates whether the prompt is still
|
|
1026
|
-
* active after the turn has been processed by the prompt.
|
|
1027
|
-
*
|
|
1028
|
-
* @param dc The DialogContext for the current turn of the conversation.
|
|
1029
|
-
*
|
|
1030
|
-
* @throws {@link ErrorCode|InvalidParameter} when timeout property in teams bot sso prompt settings is not number or is not positive.
|
|
1031
|
-
* @throws {@link ErrorCode|ChannelNotSupported} when bot channel is not MS Teams.
|
|
1032
|
-
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1033
|
-
*
|
|
1034
|
-
* @returns A `Promise` representing the asynchronous operation.
|
|
1035
|
-
*
|
|
1036
|
-
* @beta
|
|
1037
|
-
*/
|
|
1038
|
-
beginDialog(dc) {
|
|
1039
|
-
var _a;
|
|
1040
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1041
|
-
internalLogger.info("Begin Teams Bot SSO Prompt");
|
|
1042
|
-
this.ensureMsTeamsChannel(dc);
|
|
1043
|
-
// Initialize prompt state
|
|
1044
|
-
const default_timeout = 900000;
|
|
1045
|
-
let timeout = default_timeout;
|
|
1046
|
-
if (this.settings.timeout) {
|
|
1047
|
-
if (typeof this.settings.timeout != "number") {
|
|
1048
|
-
const errorMsg = "type of timeout property in teamsBotSsoPromptSettings should be number.";
|
|
1049
|
-
internalLogger.error(errorMsg);
|
|
1050
|
-
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidParameter);
|
|
1051
|
-
}
|
|
1052
|
-
if (this.settings.timeout <= 0) {
|
|
1053
|
-
const errorMsg = "value of timeout property in teamsBotSsoPromptSettings should be positive.";
|
|
1054
|
-
internalLogger.error(errorMsg);
|
|
1055
|
-
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidParameter);
|
|
1056
|
-
}
|
|
1057
|
-
timeout = this.settings.timeout;
|
|
1058
|
-
}
|
|
1059
|
-
if (this.settings.endOnInvalidMessage === undefined) {
|
|
1060
|
-
this.settings.endOnInvalidMessage = true;
|
|
1061
|
-
}
|
|
1062
|
-
const state = (_a = dc.activeDialog) === null || _a === void 0 ? void 0 : _a.state;
|
|
1063
|
-
state.state = {};
|
|
1064
|
-
state.options = {};
|
|
1065
|
-
state.expires = new Date().getTime() + timeout;
|
|
1066
|
-
// Send OAuth card to get SSO token
|
|
1067
|
-
yield this.sendOAuthCardAsync(dc.context);
|
|
1068
|
-
return botbuilderDialogs.Dialog.EndOfTurn;
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
/**
|
|
1072
|
-
* Called when a prompt dialog is the active dialog and the user replied with a new activity.
|
|
1073
|
-
*
|
|
1074
|
-
* @remarks
|
|
1075
|
-
* If the task is successful, the result indicates whether the dialog is still
|
|
1076
|
-
* active after the turn has been processed by the dialog.
|
|
1077
|
-
* The prompt generally continues to receive the user's replies until it accepts the
|
|
1078
|
-
* user's reply as valid input for the prompt.
|
|
1079
|
-
*
|
|
1080
|
-
* @param dc The DialogContext for the current turn of the conversation.
|
|
1081
|
-
*
|
|
1082
|
-
* @returns A `Promise` representing the asynchronous operation.
|
|
1083
|
-
*
|
|
1084
|
-
* @throws {@link ErrorCode|ChannelNotSupported} when bot channel is not MS Teams.
|
|
1085
|
-
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1086
|
-
*
|
|
1087
|
-
* @beta
|
|
1088
|
-
*/
|
|
1089
|
-
continueDialog(dc) {
|
|
1090
|
-
var _a;
|
|
1091
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1092
|
-
internalLogger.info("Continue Teams Bot SSO Prompt");
|
|
1093
|
-
this.ensureMsTeamsChannel(dc);
|
|
1094
|
-
// Check for timeout
|
|
1095
|
-
const state = (_a = dc.activeDialog) === null || _a === void 0 ? void 0 : _a.state;
|
|
1096
|
-
const isMessage = dc.context.activity.type === botbuilder.ActivityTypes.Message;
|
|
1097
|
-
const isTimeoutActivityType = isMessage ||
|
|
1098
|
-
this.isTeamsVerificationInvoke(dc.context) ||
|
|
1099
|
-
this.isTokenExchangeRequestInvoke(dc.context);
|
|
1100
|
-
// If the incoming Activity is a message, or an Activity Type normally handled by TeamsBotSsoPrompt,
|
|
1101
|
-
// check to see if this TeamsBotSsoPrompt Expiration has elapsed, and end the dialog if so.
|
|
1102
|
-
const hasTimedOut = isTimeoutActivityType && new Date().getTime() > state.expires;
|
|
1103
|
-
if (hasTimedOut) {
|
|
1104
|
-
internalLogger.warn("End Teams Bot SSO Prompt due to timeout");
|
|
1105
|
-
return yield dc.endDialog(undefined);
|
|
1106
|
-
}
|
|
1107
|
-
else {
|
|
1108
|
-
if (this.isTeamsVerificationInvoke(dc.context) ||
|
|
1109
|
-
this.isTokenExchangeRequestInvoke(dc.context)) {
|
|
1110
|
-
// Recognize token
|
|
1111
|
-
const recognized = yield this.recognizeToken(dc);
|
|
1112
|
-
if (recognized.succeeded) {
|
|
1113
|
-
return yield dc.endDialog(recognized.value);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
else if (isMessage && this.settings.endOnInvalidMessage) {
|
|
1117
|
-
internalLogger.warn("End Teams Bot SSO Prompt due to invalid message");
|
|
1118
|
-
return yield dc.endDialog(undefined);
|
|
1119
|
-
}
|
|
1120
|
-
return botbuilderDialogs.Dialog.EndOfTurn;
|
|
1121
|
-
}
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
/**
|
|
1125
|
-
* Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
|
|
1126
|
-
* @param dc dialog context
|
|
1127
|
-
* @throws {@link ErrorCode|ChannelNotSupported} if bot channel is not MS Teams
|
|
1128
|
-
* @internal
|
|
1129
|
-
*/
|
|
1130
|
-
ensureMsTeamsChannel(dc) {
|
|
1131
|
-
if (dc.context.activity.channelId != botbuilder.Channels.Msteams) {
|
|
1132
|
-
const errorMsg = formatString(ErrorMessage.OnlyMSTeamsChannelSupported, "Teams Bot SSO Prompt");
|
|
1133
|
-
internalLogger.error(errorMsg);
|
|
1134
|
-
throw new ErrorWithCode(errorMsg, exports.ErrorCode.ChannelNotSupported);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
/**
|
|
1138
|
-
* Send OAuthCard that tells Teams to obtain an authentication token for the bot application.
|
|
1139
|
-
* For details see https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/auth-aad-sso-bots.
|
|
1140
|
-
*
|
|
1141
|
-
* @internal
|
|
1142
|
-
*/
|
|
1143
|
-
sendOAuthCardAsync(context) {
|
|
1144
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1145
|
-
internalLogger.verbose("Send OAuth card to get SSO token");
|
|
1146
|
-
const account = yield botbuilder.TeamsInfo.getMember(context, context.activity.from.id);
|
|
1147
|
-
internalLogger.verbose("Get Teams member account user principal name: " + account.userPrincipalName);
|
|
1148
|
-
const loginHint = account.userPrincipalName ? account.userPrincipalName : "";
|
|
1149
|
-
const signInResource = this.getSignInResource(loginHint);
|
|
1150
|
-
const card = botbuilder.CardFactory.oauthCard("", "Teams SSO Sign In", "Sign In", signInResource.signInLink, signInResource.tokenExchangeResource);
|
|
1151
|
-
card.content.buttons[0].type = botbuilder.ActionTypes.Signin;
|
|
1152
|
-
const msg = botbuilder.MessageFactory.attachment(card);
|
|
1153
|
-
// Send prompt
|
|
1154
|
-
yield context.sendActivity(msg);
|
|
1155
|
-
});
|
|
1156
|
-
}
|
|
1157
|
-
/**
|
|
1158
|
-
* Get sign in resource.
|
|
1159
|
-
*
|
|
1160
|
-
* @throws {@link ErrorCode|InvalidConfiguration} if client id, tenant id or initiate login endpoint is not found in config.
|
|
1161
|
-
*
|
|
1162
|
-
* @internal
|
|
1163
|
-
*/
|
|
1164
|
-
getSignInResource(loginHint) {
|
|
1165
|
-
var _a, _b, _c, _d, _e;
|
|
1166
|
-
internalLogger.verbose("Get sign in authentication configuration");
|
|
1167
|
-
const missingConfigurations = [];
|
|
1168
|
-
if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.initiateLoginEndpoint)) {
|
|
1169
|
-
missingConfigurations.push("initiateLoginEndpoint");
|
|
1170
|
-
}
|
|
1171
|
-
if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.clientId)) {
|
|
1172
|
-
missingConfigurations.push("clientId");
|
|
1173
|
-
}
|
|
1174
|
-
if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.tenantId)) {
|
|
1175
|
-
missingConfigurations.push("tenantId");
|
|
1176
|
-
}
|
|
1177
|
-
if (!((_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.applicationIdUri)) {
|
|
1178
|
-
missingConfigurations.push("applicationIdUri");
|
|
1179
|
-
}
|
|
1180
|
-
if (missingConfigurations.length != 0) {
|
|
1181
|
-
const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
|
|
1182
|
-
internalLogger.error(errorMsg);
|
|
1183
|
-
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
|
|
1184
|
-
}
|
|
1185
|
-
const signInLink = `${config.authentication.initiateLoginEndpoint}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${config.authentication.clientId}&tenantId=${config.authentication.tenantId}&loginHint=${loginHint}`;
|
|
1186
|
-
internalLogger.verbose("Sign in link: " + signInLink);
|
|
1187
|
-
const tokenExchangeResource = {
|
|
1188
|
-
id: uuid.v4(),
|
|
1189
|
-
uri: ((_e = config.authentication) === null || _e === void 0 ? void 0 : _e.applicationIdUri.replace(/\/$/, "")) + "/access_as_user",
|
|
1190
|
-
};
|
|
1191
|
-
internalLogger.verbose("Token exchange resource uri: " + tokenExchangeResource.uri);
|
|
1192
|
-
return {
|
|
1193
|
-
signInLink: signInLink,
|
|
1194
|
-
tokenExchangeResource: tokenExchangeResource,
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
/**
|
|
1198
|
-
* @internal
|
|
1199
|
-
*/
|
|
1200
|
-
recognizeToken(dc) {
|
|
1201
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1202
|
-
const context = dc.context;
|
|
1203
|
-
let tokenResponse;
|
|
1204
|
-
if (this.isTokenExchangeRequestInvoke(context)) {
|
|
1205
|
-
internalLogger.verbose("Receive token exchange request");
|
|
1206
|
-
// Received activity is not a token exchange request
|
|
1207
|
-
if (!(context.activity.value && this.isTokenExchangeRequest(context.activity.value))) {
|
|
1208
|
-
const warningMsg = "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.";
|
|
1209
|
-
internalLogger.warn(warningMsg);
|
|
1210
|
-
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder.StatusCodes.BAD_REQUEST, warningMsg));
|
|
1211
|
-
}
|
|
1212
|
-
else {
|
|
1213
|
-
const ssoToken = context.activity.value.token;
|
|
1214
|
-
const credential = new OnBehalfOfUserCredential(ssoToken);
|
|
1215
|
-
let exchangedToken;
|
|
1216
|
-
try {
|
|
1217
|
-
exchangedToken = yield credential.getToken(this.settings.scopes);
|
|
1218
|
-
if (exchangedToken) {
|
|
1219
|
-
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder.StatusCodes.OK, "", context.activity.value.id));
|
|
1220
|
-
const ssoTokenExpiration = parseJwt(ssoToken).exp;
|
|
1221
|
-
tokenResponse = {
|
|
1222
|
-
ssoToken: ssoToken,
|
|
1223
|
-
ssoTokenExpiration: new Date(ssoTokenExpiration * 1000).toISOString(),
|
|
1224
|
-
connectionName: "",
|
|
1225
|
-
token: exchangedToken.token,
|
|
1226
|
-
expiration: exchangedToken.expiresOnTimestamp.toString(),
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
catch (error) {
|
|
1231
|
-
const warningMsg = "The bot is unable to exchange token. Ask for user consent.";
|
|
1232
|
-
internalLogger.info(warningMsg);
|
|
1233
|
-
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder.StatusCodes.PRECONDITION_FAILED, warningMsg, context.activity.value.id));
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
else if (this.isTeamsVerificationInvoke(context)) {
|
|
1238
|
-
internalLogger.verbose("Receive Teams state verification request");
|
|
1239
|
-
yield this.sendOAuthCardAsync(dc.context);
|
|
1240
|
-
yield context.sendActivity({ type: invokeResponseType, value: { status: botbuilder.StatusCodes.OK } });
|
|
1241
|
-
}
|
|
1242
|
-
return tokenResponse !== undefined
|
|
1243
|
-
? { succeeded: true, value: tokenResponse }
|
|
1244
|
-
: { succeeded: false };
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
/**
|
|
1248
|
-
* @internal
|
|
1249
|
-
*/
|
|
1250
|
-
getTokenExchangeInvokeResponse(status, failureDetail, id) {
|
|
1251
|
-
const invokeResponse = {
|
|
1252
|
-
type: invokeResponseType,
|
|
1253
|
-
value: { status, body: new TokenExchangeInvokeResponse(id, failureDetail) },
|
|
1254
|
-
};
|
|
1255
|
-
return invokeResponse;
|
|
1256
|
-
}
|
|
1257
|
-
/**
|
|
1258
|
-
* @internal
|
|
1259
|
-
*/
|
|
1260
|
-
isTeamsVerificationInvoke(context) {
|
|
1261
|
-
const activity = context.activity;
|
|
1262
|
-
return activity.type === botbuilder.ActivityTypes.Invoke && activity.name === botbuilder.verifyStateOperationName;
|
|
1263
|
-
}
|
|
1264
|
-
/**
|
|
1265
|
-
* @internal
|
|
1266
|
-
*/
|
|
1267
|
-
isTokenExchangeRequestInvoke(context) {
|
|
1268
|
-
const activity = context.activity;
|
|
1269
|
-
return activity.type === botbuilder.ActivityTypes.Invoke && activity.name === botbuilder.tokenExchangeOperationName;
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* @internal
|
|
1273
|
-
*/
|
|
1274
|
-
isTokenExchangeRequest(obj) {
|
|
1275
|
-
return obj.hasOwnProperty("token");
|
|
1276
|
-
}
|
|
927
|
+
// Copyright (c) Microsoft Corporation.
|
|
928
|
+
/**
|
|
929
|
+
* Get Microsoft graph client.
|
|
930
|
+
*
|
|
931
|
+
* @example
|
|
932
|
+
* Get Microsoft graph client by TokenCredential
|
|
933
|
+
* ```typescript
|
|
934
|
+
* // Sso token example (Azure Function)
|
|
935
|
+
* const ssoToken = "YOUR_TOKEN_STRING";
|
|
936
|
+
* const options = {"AAD_APP_ID", "AAD_APP_SECRET"};
|
|
937
|
+
* const credential = new OnBehalfOfAADUserCredential(ssoToken, options);
|
|
938
|
+
* const graphClient = await createMicrosoftGraphClient(credential);
|
|
939
|
+
* const profile = await graphClient.api("/me").get();
|
|
940
|
+
*
|
|
941
|
+
* // TeamsBotSsoPrompt example (Bot Application)
|
|
942
|
+
* const requiredScopes = ["User.Read"];
|
|
943
|
+
* const config: Configuration = {
|
|
944
|
+
* loginUrl: loginUrl,
|
|
945
|
+
* clientId: clientId,
|
|
946
|
+
* clientSecret: clientSecret,
|
|
947
|
+
* tenantId: tenantId
|
|
948
|
+
* };
|
|
949
|
+
* const prompt = new TeamsBotSsoPrompt(dialogId, {
|
|
950
|
+
* config: config
|
|
951
|
+
* scopes: '["User.Read"],
|
|
952
|
+
* });
|
|
953
|
+
* this.addDialog(prompt);
|
|
954
|
+
*
|
|
955
|
+
* const oboCredential = new OnBehalfOfAADUserCredential(
|
|
956
|
+
* getUserId(dialogContext),
|
|
957
|
+
* {
|
|
958
|
+
* clientId: "AAD_APP_ID",
|
|
959
|
+
* clientSecret: "AAD_APP_SECRET"
|
|
960
|
+
* });
|
|
961
|
+
* try {
|
|
962
|
+
* const graphClient = await createMicrosoftGraphClient(credential);
|
|
963
|
+
* const profile = await graphClient.api("/me").get();
|
|
964
|
+
* } catch (e) {
|
|
965
|
+
* dialogContext.beginDialog(dialogId);
|
|
966
|
+
* return Dialog.endOfTurn();
|
|
967
|
+
* }
|
|
968
|
+
* ```
|
|
969
|
+
*
|
|
970
|
+
* @param {TokenCredential} credential - token credential instance.
|
|
971
|
+
* @param scopes - The array of Microsoft Token scope of access. Default value is `[.default]`.
|
|
972
|
+
*
|
|
973
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
974
|
+
*
|
|
975
|
+
* @returns Graph client with specified scopes.
|
|
976
|
+
*
|
|
977
|
+
* @beta
|
|
978
|
+
*/
|
|
979
|
+
function createMicrosoftGraphClient(credential, scopes) {
|
|
980
|
+
internalLogger.info("Create Microsoft Graph Client");
|
|
981
|
+
const authProvider = new MsGraphAuthProvider(credential, scopes);
|
|
982
|
+
const graphClient = Client.initWithMiddleware({
|
|
983
|
+
authProvider,
|
|
984
|
+
});
|
|
985
|
+
return graphClient;
|
|
1277
986
|
}
|
|
1278
987
|
|
|
1279
|
-
// Copyright (c) Microsoft Corporation.
|
|
1280
|
-
/**
|
|
1281
|
-
*
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
988
|
+
// Copyright (c) Microsoft Corporation.
|
|
989
|
+
/**
|
|
990
|
+
* SQL connection configuration instance.
|
|
991
|
+
* @remarks
|
|
992
|
+
* Only works in in server side.
|
|
993
|
+
*
|
|
994
|
+
* @beta
|
|
995
|
+
*
|
|
996
|
+
*/
|
|
997
|
+
class DefaultTediousConnectionConfiguration {
|
|
998
|
+
constructor() {
|
|
999
|
+
/**
|
|
1000
|
+
* MSSQL default scope
|
|
1001
|
+
* https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
|
|
1002
|
+
*/
|
|
1003
|
+
this.defaultSQLScope = "https://database.windows.net/";
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Generate connection configuration consumed by tedious.
|
|
1007
|
+
*
|
|
1008
|
+
* @returns Connection configuration of tedious for the SQL.
|
|
1009
|
+
*
|
|
1010
|
+
* @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
|
|
1011
|
+
* @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
|
|
1012
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1013
|
+
*
|
|
1014
|
+
* @beta
|
|
1015
|
+
*/
|
|
1016
|
+
async getConfig() {
|
|
1017
|
+
internalLogger.info("Get SQL configuration");
|
|
1018
|
+
const configuration = getResourceConfiguration(ResourceType.SQL);
|
|
1019
|
+
if (!configuration) {
|
|
1020
|
+
const errMsg = "SQL resource configuration not exist";
|
|
1021
|
+
internalLogger.error(errMsg);
|
|
1022
|
+
throw new ErrorWithCode(errMsg, ErrorCode.InvalidConfiguration);
|
|
1023
|
+
}
|
|
1024
|
+
try {
|
|
1025
|
+
this.isSQLConfigurationValid(configuration);
|
|
1026
|
+
}
|
|
1027
|
+
catch (err) {
|
|
1028
|
+
throw err;
|
|
1029
|
+
}
|
|
1030
|
+
if (!this.isMsiAuthentication()) {
|
|
1031
|
+
const configWithUPS = this.generateDefaultConfig(configuration);
|
|
1032
|
+
internalLogger.verbose("SQL configuration with username and password generated");
|
|
1033
|
+
return configWithUPS;
|
|
1034
|
+
}
|
|
1035
|
+
try {
|
|
1036
|
+
const configWithToken = await this.generateTokenConfig(configuration);
|
|
1037
|
+
internalLogger.verbose("SQL configuration with MSI token generated");
|
|
1038
|
+
return configWithToken;
|
|
1039
|
+
}
|
|
1040
|
+
catch (error) {
|
|
1041
|
+
throw error;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Check SQL use MSI identity or username and password.
|
|
1046
|
+
*
|
|
1047
|
+
* @returns false - login with SQL MSI identity, true - login with username and password.
|
|
1048
|
+
* @internal
|
|
1049
|
+
*/
|
|
1050
|
+
isMsiAuthentication() {
|
|
1051
|
+
internalLogger.verbose("Check connection config using MSI access token or username and password");
|
|
1052
|
+
const configuration = getResourceConfiguration(ResourceType.SQL);
|
|
1053
|
+
if ((configuration === null || configuration === void 0 ? void 0 : configuration.sqlUsername) != null && (configuration === null || configuration === void 0 ? void 0 : configuration.sqlPassword) != null) {
|
|
1054
|
+
internalLogger.verbose("Login with username and password");
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
internalLogger.verbose("Login with MSI identity");
|
|
1058
|
+
return true;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* check configuration is an available configurations.
|
|
1062
|
+
* @param { SqlConfiguration } sqlConfig
|
|
1063
|
+
*
|
|
1064
|
+
* @returns true - SQL configuration has a valid SQL endpoints, SQL username with password or identity ID.
|
|
1065
|
+
* false - configuration is not valid.
|
|
1066
|
+
* @internal
|
|
1067
|
+
*/
|
|
1068
|
+
isSQLConfigurationValid(sqlConfig) {
|
|
1069
|
+
internalLogger.verbose("Check SQL configuration if valid");
|
|
1070
|
+
if (!sqlConfig.sqlServerEndpoint) {
|
|
1071
|
+
internalLogger.error("SQL configuration is not valid without SQL server endpoint exist");
|
|
1072
|
+
throw new ErrorWithCode("SQL configuration error without SQL server endpoint exist", ErrorCode.InvalidConfiguration);
|
|
1073
|
+
}
|
|
1074
|
+
if (!(sqlConfig.sqlUsername && sqlConfig.sqlPassword) && !sqlConfig.sqlIdentityId) {
|
|
1075
|
+
const errMsg = `SQL configuration is not valid without ${sqlConfig.sqlIdentityId ? "" : "identity id "} ${sqlConfig.sqlUsername ? "" : "SQL username "} ${sqlConfig.sqlPassword ? "" : "SQL password"} exist`;
|
|
1076
|
+
internalLogger.error(errMsg);
|
|
1077
|
+
throw new ErrorWithCode(errMsg, ErrorCode.InvalidConfiguration);
|
|
1078
|
+
}
|
|
1079
|
+
internalLogger.verbose("SQL configuration is valid");
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Generate tedious connection configuration with default authentication type.
|
|
1083
|
+
*
|
|
1084
|
+
* @param { SqlConfiguration } SQL configuration with username and password.
|
|
1085
|
+
*
|
|
1086
|
+
* @returns Tedious connection configuration with username and password.
|
|
1087
|
+
* @internal
|
|
1088
|
+
*/
|
|
1089
|
+
generateDefaultConfig(sqlConfig) {
|
|
1090
|
+
internalLogger.verbose(`SQL server ${sqlConfig.sqlServerEndpoint}, user name ${sqlConfig.sqlUsername}, database name ${sqlConfig.sqlDatabaseName}`);
|
|
1091
|
+
const config = {
|
|
1092
|
+
server: sqlConfig.sqlServerEndpoint,
|
|
1093
|
+
authentication: {
|
|
1094
|
+
type: TediousAuthenticationType.default,
|
|
1095
|
+
options: {
|
|
1096
|
+
userName: sqlConfig.sqlUsername,
|
|
1097
|
+
password: sqlConfig.sqlPassword,
|
|
1098
|
+
},
|
|
1099
|
+
},
|
|
1100
|
+
options: {
|
|
1101
|
+
database: sqlConfig.sqlDatabaseName,
|
|
1102
|
+
encrypt: true,
|
|
1103
|
+
},
|
|
1104
|
+
};
|
|
1105
|
+
return config;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Generate tedious connection configuration with azure-active-directory-access-token authentication type.
|
|
1109
|
+
*
|
|
1110
|
+
* @param { SqlConfiguration } SQL configuration with AAD access token.
|
|
1111
|
+
*
|
|
1112
|
+
* @returns Tedious connection configuration with access token.
|
|
1113
|
+
* @internal
|
|
1114
|
+
*/
|
|
1115
|
+
async generateTokenConfig(sqlConfig) {
|
|
1116
|
+
internalLogger.verbose("Generate tedious config with MSI token");
|
|
1117
|
+
let token;
|
|
1118
|
+
try {
|
|
1119
|
+
const credential = new ManagedIdentityCredential(sqlConfig.sqlIdentityId);
|
|
1120
|
+
token = await credential.getToken(this.defaultSQLScope);
|
|
1121
|
+
}
|
|
1122
|
+
catch (error) {
|
|
1123
|
+
const errMsg = "Get user MSI token failed";
|
|
1124
|
+
internalLogger.error(errMsg);
|
|
1125
|
+
throw new ErrorWithCode(errMsg, ErrorCode.InternalError);
|
|
1126
|
+
}
|
|
1127
|
+
if (token) {
|
|
1128
|
+
const config = {
|
|
1129
|
+
server: sqlConfig.sqlServerEndpoint,
|
|
1130
|
+
authentication: {
|
|
1131
|
+
type: TediousAuthenticationType.MSI,
|
|
1132
|
+
options: {
|
|
1133
|
+
token: token.token,
|
|
1134
|
+
},
|
|
1135
|
+
},
|
|
1136
|
+
options: {
|
|
1137
|
+
database: sqlConfig.sqlDatabaseName,
|
|
1138
|
+
encrypt: true,
|
|
1139
|
+
},
|
|
1140
|
+
};
|
|
1141
|
+
internalLogger.verbose(`Generate token configuration success, server endpoint is ${sqlConfig.sqlServerEndpoint}, database name is ${sqlConfig.sqlDatabaseName}`);
|
|
1142
|
+
return config;
|
|
1143
|
+
}
|
|
1144
|
+
internalLogger.error(`Generate token configuration, server endpoint is ${sqlConfig.sqlServerEndpoint}, MSI token is not valid`);
|
|
1145
|
+
throw new ErrorWithCode("MSI token is not valid", ErrorCode.InternalError);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* tedious connection config authentication type.
|
|
1150
|
+
* https://tediousjs.github.io/tedious/api-connection.html
|
|
1151
|
+
* @internal
|
|
1152
|
+
*/
|
|
1153
|
+
var TediousAuthenticationType;
|
|
1154
|
+
(function (TediousAuthenticationType) {
|
|
1155
|
+
TediousAuthenticationType["default"] = "default";
|
|
1156
|
+
TediousAuthenticationType["MSI"] = "azure-active-directory-access-token";
|
|
1157
|
+
})(TediousAuthenticationType || (TediousAuthenticationType = {}));
|
|
1339
1158
|
|
|
1340
|
-
// Copyright (c) Microsoft Corporation.
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
*
|
|
1344
|
-
*
|
|
1345
|
-
*
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
*
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1159
|
+
// Copyright (c) Microsoft Corporation.
|
|
1160
|
+
const invokeResponseType = "invokeResponse";
|
|
1161
|
+
/**
|
|
1162
|
+
* Response body returned for a token exchange invoke activity.
|
|
1163
|
+
*
|
|
1164
|
+
* @beta
|
|
1165
|
+
*/
|
|
1166
|
+
class TokenExchangeInvokeResponse {
|
|
1167
|
+
constructor(id, failureDetail) {
|
|
1168
|
+
this.id = id;
|
|
1169
|
+
this.failureDetail = failureDetail;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Creates a new prompt that leverage Teams Single Sign On (SSO) support for bot to automatically sign in user and
|
|
1174
|
+
* help receive oauth token, asks the user to consent if needed.
|
|
1175
|
+
*
|
|
1176
|
+
* @remarks
|
|
1177
|
+
* The prompt will attempt to retrieve the users current token of the desired scopes and store it in
|
|
1178
|
+
* the token store.
|
|
1179
|
+
*
|
|
1180
|
+
* User will be automatically signed in leveraging Teams support of Bot Single Sign On(SSO):
|
|
1181
|
+
* https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/auth-aad-sso-bots
|
|
1182
|
+
*
|
|
1183
|
+
* @example
|
|
1184
|
+
* When used with your bots `DialogSet` you can simply add a new instance of the prompt as a named
|
|
1185
|
+
* dialog using `DialogSet.add()`. You can then start the prompt from a waterfall step using either
|
|
1186
|
+
* `DialogContext.beginDialog()` or `DialogContext.prompt()`. The user will be prompted to sign in as
|
|
1187
|
+
* needed and their access token will be passed as an argument to the callers next waterfall step:
|
|
1188
|
+
*
|
|
1189
|
+
* ```JavaScript
|
|
1190
|
+
* const { ConversationState, MemoryStorage } = require('botbuilder');
|
|
1191
|
+
* const { DialogSet, WaterfallDialog } = require('botbuilder-dialogs');
|
|
1192
|
+
* const { TeamsBotSsoPrompt } = require('@microsoft/teamsfx');
|
|
1193
|
+
*
|
|
1194
|
+
* const convoState = new ConversationState(new MemoryStorage());
|
|
1195
|
+
* const dialogState = convoState.createProperty('dialogState');
|
|
1196
|
+
* const dialogs = new DialogSet(dialogState);
|
|
1197
|
+
*
|
|
1198
|
+
* loadConfiguration();
|
|
1199
|
+
* dialogs.add(new TeamsBotSsoPrompt('TeamsBotSsoPrompt', {
|
|
1200
|
+
* scopes: ["User.Read"],
|
|
1201
|
+
* }));
|
|
1202
|
+
*
|
|
1203
|
+
* dialogs.add(new WaterfallDialog('taskNeedingLogin', [
|
|
1204
|
+
* async (step) => {
|
|
1205
|
+
* return await step.beginDialog('TeamsBotSsoPrompt');
|
|
1206
|
+
* },
|
|
1207
|
+
* async (step) => {
|
|
1208
|
+
* const token = step.result;
|
|
1209
|
+
* if (token) {
|
|
1210
|
+
*
|
|
1211
|
+
* // ... continue with task needing access token ...
|
|
1212
|
+
*
|
|
1213
|
+
* } else {
|
|
1214
|
+
* await step.context.sendActivity(`Sorry... We couldn't log you in. Try again later.`);
|
|
1215
|
+
* return await step.endDialog();
|
|
1216
|
+
* }
|
|
1217
|
+
* }
|
|
1218
|
+
* ]));
|
|
1219
|
+
* ```
|
|
1220
|
+
*
|
|
1221
|
+
* @beta
|
|
1222
|
+
*/
|
|
1223
|
+
class TeamsBotSsoPrompt extends Dialog {
|
|
1224
|
+
/**
|
|
1225
|
+
* Constructor of TeamsBotSsoPrompt.
|
|
1226
|
+
*
|
|
1227
|
+
* @param dialogId Unique ID of the dialog within its parent `DialogSet` or `ComponentDialog`.
|
|
1228
|
+
* @param settings Settings used to configure the prompt.
|
|
1229
|
+
*
|
|
1230
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
1231
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1232
|
+
*
|
|
1233
|
+
* @beta
|
|
1234
|
+
*/
|
|
1235
|
+
constructor(dialogId, settings) {
|
|
1236
|
+
super(dialogId);
|
|
1237
|
+
this.settings = settings;
|
|
1238
|
+
validateScopesType(settings.scopes);
|
|
1239
|
+
internalLogger.info("Create a new Teams Bot SSO Prompt");
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Called when a prompt dialog is pushed onto the dialog stack and is being activated.
|
|
1243
|
+
* @remarks
|
|
1244
|
+
* If the task is successful, the result indicates whether the prompt is still
|
|
1245
|
+
* active after the turn has been processed by the prompt.
|
|
1246
|
+
*
|
|
1247
|
+
* @param dc The DialogContext for the current turn of the conversation.
|
|
1248
|
+
*
|
|
1249
|
+
* @throws {@link ErrorCode|InvalidParameter} when timeout property in teams bot sso prompt settings is not number or is not positive.
|
|
1250
|
+
* @throws {@link ErrorCode|ChannelNotSupported} when bot channel is not MS Teams.
|
|
1251
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1252
|
+
*
|
|
1253
|
+
* @returns A `Promise` representing the asynchronous operation.
|
|
1254
|
+
*
|
|
1255
|
+
* @beta
|
|
1256
|
+
*/
|
|
1257
|
+
async beginDialog(dc) {
|
|
1258
|
+
var _a;
|
|
1259
|
+
internalLogger.info("Begin Teams Bot SSO Prompt");
|
|
1260
|
+
this.ensureMsTeamsChannel(dc);
|
|
1261
|
+
// Initialize prompt state
|
|
1262
|
+
const default_timeout = 900000;
|
|
1263
|
+
let timeout = default_timeout;
|
|
1264
|
+
if (this.settings.timeout) {
|
|
1265
|
+
if (typeof this.settings.timeout != "number") {
|
|
1266
|
+
const errorMsg = "type of timeout property in teamsBotSsoPromptSettings should be number.";
|
|
1267
|
+
internalLogger.error(errorMsg);
|
|
1268
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
|
|
1269
|
+
}
|
|
1270
|
+
if (this.settings.timeout <= 0) {
|
|
1271
|
+
const errorMsg = "value of timeout property in teamsBotSsoPromptSettings should be positive.";
|
|
1272
|
+
internalLogger.error(errorMsg);
|
|
1273
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
|
|
1274
|
+
}
|
|
1275
|
+
timeout = this.settings.timeout;
|
|
1276
|
+
}
|
|
1277
|
+
if (this.settings.endOnInvalidMessage === undefined) {
|
|
1278
|
+
this.settings.endOnInvalidMessage = true;
|
|
1279
|
+
}
|
|
1280
|
+
const state = (_a = dc.activeDialog) === null || _a === void 0 ? void 0 : _a.state;
|
|
1281
|
+
state.state = {};
|
|
1282
|
+
state.options = {};
|
|
1283
|
+
state.expires = new Date().getTime() + timeout;
|
|
1284
|
+
// Send OAuth card to get SSO token
|
|
1285
|
+
await this.sendOAuthCardAsync(dc.context);
|
|
1286
|
+
return Dialog.EndOfTurn;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Called when a prompt dialog is the active dialog and the user replied with a new activity.
|
|
1290
|
+
*
|
|
1291
|
+
* @remarks
|
|
1292
|
+
* If the task is successful, the result indicates whether the dialog is still
|
|
1293
|
+
* active after the turn has been processed by the dialog.
|
|
1294
|
+
* The prompt generally continues to receive the user's replies until it accepts the
|
|
1295
|
+
* user's reply as valid input for the prompt.
|
|
1296
|
+
*
|
|
1297
|
+
* @param dc The DialogContext for the current turn of the conversation.
|
|
1298
|
+
*
|
|
1299
|
+
* @returns A `Promise` representing the asynchronous operation.
|
|
1300
|
+
*
|
|
1301
|
+
* @throws {@link ErrorCode|ChannelNotSupported} when bot channel is not MS Teams.
|
|
1302
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1303
|
+
*
|
|
1304
|
+
* @beta
|
|
1305
|
+
*/
|
|
1306
|
+
async continueDialog(dc) {
|
|
1307
|
+
var _a;
|
|
1308
|
+
internalLogger.info("Continue Teams Bot SSO Prompt");
|
|
1309
|
+
this.ensureMsTeamsChannel(dc);
|
|
1310
|
+
// Check for timeout
|
|
1311
|
+
const state = (_a = dc.activeDialog) === null || _a === void 0 ? void 0 : _a.state;
|
|
1312
|
+
const isMessage = dc.context.activity.type === ActivityTypes.Message;
|
|
1313
|
+
const isTimeoutActivityType = isMessage ||
|
|
1314
|
+
this.isTeamsVerificationInvoke(dc.context) ||
|
|
1315
|
+
this.isTokenExchangeRequestInvoke(dc.context);
|
|
1316
|
+
// If the incoming Activity is a message, or an Activity Type normally handled by TeamsBotSsoPrompt,
|
|
1317
|
+
// check to see if this TeamsBotSsoPrompt Expiration has elapsed, and end the dialog if so.
|
|
1318
|
+
const hasTimedOut = isTimeoutActivityType && new Date().getTime() > state.expires;
|
|
1319
|
+
if (hasTimedOut) {
|
|
1320
|
+
internalLogger.warn("End Teams Bot SSO Prompt due to timeout");
|
|
1321
|
+
return await dc.endDialog(undefined);
|
|
1322
|
+
}
|
|
1323
|
+
else {
|
|
1324
|
+
if (this.isTeamsVerificationInvoke(dc.context) ||
|
|
1325
|
+
this.isTokenExchangeRequestInvoke(dc.context)) {
|
|
1326
|
+
// Recognize token
|
|
1327
|
+
const recognized = await this.recognizeToken(dc);
|
|
1328
|
+
if (recognized.succeeded) {
|
|
1329
|
+
return await dc.endDialog(recognized.value);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
else if (isMessage && this.settings.endOnInvalidMessage) {
|
|
1333
|
+
internalLogger.warn("End Teams Bot SSO Prompt due to invalid message");
|
|
1334
|
+
return await dc.endDialog(undefined);
|
|
1335
|
+
}
|
|
1336
|
+
return Dialog.EndOfTurn;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
|
|
1341
|
+
* @param dc dialog context
|
|
1342
|
+
* @throws {@link ErrorCode|ChannelNotSupported} if bot channel is not MS Teams
|
|
1343
|
+
* @internal
|
|
1344
|
+
*/
|
|
1345
|
+
ensureMsTeamsChannel(dc) {
|
|
1346
|
+
if (dc.context.activity.channelId != Channels.Msteams) {
|
|
1347
|
+
const errorMsg = formatString(ErrorMessage.OnlyMSTeamsChannelSupported, "Teams Bot SSO Prompt");
|
|
1348
|
+
internalLogger.error(errorMsg);
|
|
1349
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.ChannelNotSupported);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Send OAuthCard that tells Teams to obtain an authentication token for the bot application.
|
|
1354
|
+
* For details see https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/auth-aad-sso-bots.
|
|
1355
|
+
*
|
|
1356
|
+
* @internal
|
|
1357
|
+
*/
|
|
1358
|
+
async sendOAuthCardAsync(context) {
|
|
1359
|
+
internalLogger.verbose("Send OAuth card to get SSO token");
|
|
1360
|
+
const account = await TeamsInfo.getMember(context, context.activity.from.id);
|
|
1361
|
+
internalLogger.verbose("Get Teams member account user principal name: " + account.userPrincipalName);
|
|
1362
|
+
const loginHint = account.userPrincipalName ? account.userPrincipalName : "";
|
|
1363
|
+
const signInResource = this.getSignInResource(loginHint);
|
|
1364
|
+
const card = CardFactory.oauthCard("", "Teams SSO Sign In", "Sign In", signInResource.signInLink, signInResource.tokenExchangeResource);
|
|
1365
|
+
card.content.buttons[0].type = ActionTypes.Signin;
|
|
1366
|
+
const msg = MessageFactory.attachment(card);
|
|
1367
|
+
// Send prompt
|
|
1368
|
+
await context.sendActivity(msg);
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Get sign in resource.
|
|
1372
|
+
*
|
|
1373
|
+
* @throws {@link ErrorCode|InvalidConfiguration} if client id, tenant id or initiate login endpoint is not found in config.
|
|
1374
|
+
*
|
|
1375
|
+
* @internal
|
|
1376
|
+
*/
|
|
1377
|
+
getSignInResource(loginHint) {
|
|
1378
|
+
var _a, _b, _c, _d, _e;
|
|
1379
|
+
internalLogger.verbose("Get sign in authentication configuration");
|
|
1380
|
+
const missingConfigurations = [];
|
|
1381
|
+
if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.initiateLoginEndpoint)) {
|
|
1382
|
+
missingConfigurations.push("initiateLoginEndpoint");
|
|
1383
|
+
}
|
|
1384
|
+
if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.clientId)) {
|
|
1385
|
+
missingConfigurations.push("clientId");
|
|
1386
|
+
}
|
|
1387
|
+
if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.tenantId)) {
|
|
1388
|
+
missingConfigurations.push("tenantId");
|
|
1389
|
+
}
|
|
1390
|
+
if (!((_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.applicationIdUri)) {
|
|
1391
|
+
missingConfigurations.push("applicationIdUri");
|
|
1392
|
+
}
|
|
1393
|
+
if (missingConfigurations.length != 0) {
|
|
1394
|
+
const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
|
|
1395
|
+
internalLogger.error(errorMsg);
|
|
1396
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
|
|
1397
|
+
}
|
|
1398
|
+
const signInLink = `${config.authentication.initiateLoginEndpoint}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${config.authentication.clientId}&tenantId=${config.authentication.tenantId}&loginHint=${loginHint}`;
|
|
1399
|
+
internalLogger.verbose("Sign in link: " + signInLink);
|
|
1400
|
+
const tokenExchangeResource = {
|
|
1401
|
+
id: v4(),
|
|
1402
|
+
uri: ((_e = config.authentication) === null || _e === void 0 ? void 0 : _e.applicationIdUri.replace(/\/$/, "")) + "/access_as_user",
|
|
1403
|
+
};
|
|
1404
|
+
internalLogger.verbose("Token exchange resource uri: " + tokenExchangeResource.uri);
|
|
1405
|
+
return {
|
|
1406
|
+
signInLink: signInLink,
|
|
1407
|
+
tokenExchangeResource: tokenExchangeResource,
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* @internal
|
|
1412
|
+
*/
|
|
1413
|
+
async recognizeToken(dc) {
|
|
1414
|
+
const context = dc.context;
|
|
1415
|
+
let tokenResponse;
|
|
1416
|
+
if (this.isTokenExchangeRequestInvoke(context)) {
|
|
1417
|
+
internalLogger.verbose("Receive token exchange request");
|
|
1418
|
+
// Received activity is not a token exchange request
|
|
1419
|
+
if (!(context.activity.value && this.isTokenExchangeRequest(context.activity.value))) {
|
|
1420
|
+
const warningMsg = "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.";
|
|
1421
|
+
internalLogger.warn(warningMsg);
|
|
1422
|
+
await context.sendActivity(this.getTokenExchangeInvokeResponse(StatusCodes.BAD_REQUEST, warningMsg));
|
|
1423
|
+
}
|
|
1424
|
+
else {
|
|
1425
|
+
const ssoToken = context.activity.value.token;
|
|
1426
|
+
const credential = new OnBehalfOfUserCredential(ssoToken);
|
|
1427
|
+
let exchangedToken;
|
|
1428
|
+
try {
|
|
1429
|
+
exchangedToken = await credential.getToken(this.settings.scopes);
|
|
1430
|
+
if (exchangedToken) {
|
|
1431
|
+
await context.sendActivity(this.getTokenExchangeInvokeResponse(StatusCodes.OK, "", context.activity.value.id));
|
|
1432
|
+
const ssoTokenExpiration = parseJwt(ssoToken).exp;
|
|
1433
|
+
tokenResponse = {
|
|
1434
|
+
ssoToken: ssoToken,
|
|
1435
|
+
ssoTokenExpiration: new Date(ssoTokenExpiration * 1000).toISOString(),
|
|
1436
|
+
connectionName: "",
|
|
1437
|
+
token: exchangedToken.token,
|
|
1438
|
+
expiration: exchangedToken.expiresOnTimestamp.toString(),
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
catch (error) {
|
|
1443
|
+
const warningMsg = "The bot is unable to exchange token. Ask for user consent.";
|
|
1444
|
+
internalLogger.info(warningMsg);
|
|
1445
|
+
await context.sendActivity(this.getTokenExchangeInvokeResponse(StatusCodes.PRECONDITION_FAILED, warningMsg, context.activity.value.id));
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
else if (this.isTeamsVerificationInvoke(context)) {
|
|
1450
|
+
internalLogger.verbose("Receive Teams state verification request");
|
|
1451
|
+
await this.sendOAuthCardAsync(dc.context);
|
|
1452
|
+
await context.sendActivity({ type: invokeResponseType, value: { status: StatusCodes.OK } });
|
|
1453
|
+
}
|
|
1454
|
+
return tokenResponse !== undefined
|
|
1455
|
+
? { succeeded: true, value: tokenResponse }
|
|
1456
|
+
: { succeeded: false };
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* @internal
|
|
1460
|
+
*/
|
|
1461
|
+
getTokenExchangeInvokeResponse(status, failureDetail, id) {
|
|
1462
|
+
const invokeResponse = {
|
|
1463
|
+
type: invokeResponseType,
|
|
1464
|
+
value: { status, body: new TokenExchangeInvokeResponse(id, failureDetail) },
|
|
1465
|
+
};
|
|
1466
|
+
return invokeResponse;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* @internal
|
|
1470
|
+
*/
|
|
1471
|
+
isTeamsVerificationInvoke(context) {
|
|
1472
|
+
const activity = context.activity;
|
|
1473
|
+
return activity.type === ActivityTypes.Invoke && activity.name === verifyStateOperationName;
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* @internal
|
|
1477
|
+
*/
|
|
1478
|
+
isTokenExchangeRequestInvoke(context) {
|
|
1479
|
+
const activity = context.activity;
|
|
1480
|
+
return activity.type === ActivityTypes.Invoke && activity.name === tokenExchangeOperationName;
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* @internal
|
|
1484
|
+
*/
|
|
1485
|
+
isTokenExchangeRequest(obj) {
|
|
1486
|
+
return obj.hasOwnProperty("token");
|
|
1487
|
+
}
|
|
1503
1488
|
}
|
|
1504
|
-
/**
|
|
1505
|
-
* tedious connection config authentication type.
|
|
1506
|
-
* https://tediousjs.github.io/tedious/api-connection.html
|
|
1507
|
-
* @internal
|
|
1508
|
-
*/
|
|
1509
|
-
var TediousAuthenticationType;
|
|
1510
|
-
(function (TediousAuthenticationType) {
|
|
1511
|
-
TediousAuthenticationType["default"] = "default";
|
|
1512
|
-
TediousAuthenticationType["MSI"] = "azure-active-directory-access-token";
|
|
1513
|
-
})(TediousAuthenticationType || (TediousAuthenticationType = {}));
|
|
1514
1489
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
exports.M365TenantCredential = M365TenantCredential;
|
|
1518
|
-
exports.MsGraphAuthProvider = MsGraphAuthProvider;
|
|
1519
|
-
exports.OnBehalfOfUserCredential = OnBehalfOfUserCredential;
|
|
1520
|
-
exports.TeamsBotSsoPrompt = TeamsBotSsoPrompt;
|
|
1521
|
-
exports.TeamsUserCredential = TeamsUserCredential;
|
|
1522
|
-
exports.createMicrosoftGraphClient = createMicrosoftGraphClient;
|
|
1523
|
-
exports.getAuthenticationConfiguration = getAuthenticationConfiguration;
|
|
1524
|
-
exports.getLogLevel = getLogLevel;
|
|
1525
|
-
exports.getResourceConfiguration = getResourceConfiguration;
|
|
1526
|
-
exports.loadConfiguration = loadConfiguration;
|
|
1527
|
-
exports.setLogFunction = setLogFunction;
|
|
1528
|
-
exports.setLogLevel = setLogLevel;
|
|
1529
|
-
exports.setLogger = setLogger;
|
|
1530
|
-
//# sourceMappingURL=index.js.map
|
|
1490
|
+
export { DefaultTediousConnectionConfiguration, ErrorCode, ErrorWithCode, LogLevel, M365TenantCredential, MsGraphAuthProvider, OnBehalfOfUserCredential, ResourceType, TeamsBotSsoPrompt, TeamsUserCredential, createMicrosoftGraphClient, getAuthenticationConfiguration, getLogLevel, getResourceConfiguration, loadConfiguration, setLogFunction, setLogLevel, setLogger };
|
|
1491
|
+
//# sourceMappingURL=index.esm2017.mjs.map
|