@microsoft/teamsfx 0.3.2-rc.0 → 0.3.3-alpha.13914d2e.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.
Files changed (68) hide show
  1. package/README.md +2 -2
  2. package/dist/index.esm2017.js +1413 -0
  3. package/dist/index.esm2017.js.map +1 -0
  4. package/dist/{index.js → index.esm2017.mjs} +1467 -1506
  5. package/dist/index.esm2017.mjs.map +1 -0
  6. package/dist/index.esm5.js +1575 -0
  7. package/dist/index.esm5.js.map +1 -0
  8. package/dist/index.node.cjs.js +1653 -0
  9. package/dist/index.node.cjs.js.map +1 -0
  10. package/package.json +73 -90
  11. package/types/teamsfx.d.ts +0 -2
  12. package/dist/index.js.map +0 -1
  13. package/dist/teamsfx.js +0 -30
  14. package/dist/teamsfx.js.map +0 -1
  15. package/dist-esm/src/bot/teamsBotSsoPrompt.browser.js +0 -118
  16. package/dist-esm/src/bot/teamsBotSsoPrompt.browser.js.map +0 -1
  17. package/dist-esm/src/bot/teamsBotSsoPrompt.js +0 -349
  18. package/dist-esm/src/bot/teamsBotSsoPrompt.js.map +0 -1
  19. package/dist-esm/src/bot/teamsBotSsoPromptTokenResponse.js +0 -2
  20. package/dist-esm/src/bot/teamsBotSsoPromptTokenResponse.js.map +0 -1
  21. package/dist-esm/src/core/cache.browser.js +0 -22
  22. package/dist-esm/src/core/cache.browser.js.map +0 -1
  23. package/dist-esm/src/core/cache.js +0 -28
  24. package/dist-esm/src/core/cache.js.map +0 -1
  25. package/dist-esm/src/core/configurationProvider.js +0 -124
  26. package/dist-esm/src/core/configurationProvider.js.map +0 -1
  27. package/dist-esm/src/core/defaultTediousConnectionConfiguration.browser.js +0 -28
  28. package/dist-esm/src/core/defaultTediousConnectionConfiguration.browser.js.map +0 -1
  29. package/dist-esm/src/core/defaultTediousConnectionConfiguration.js +0 -182
  30. package/dist-esm/src/core/defaultTediousConnectionConfiguration.js.map +0 -1
  31. package/dist-esm/src/core/errors.js +0 -97
  32. package/dist-esm/src/core/errors.js.map +0 -1
  33. package/dist-esm/src/core/msGraphAuthProvider.js +0 -68
  34. package/dist-esm/src/core/msGraphAuthProvider.js.map +0 -1
  35. package/dist-esm/src/core/msGraphClientProvider.js +0 -65
  36. package/dist-esm/src/core/msGraphClientProvider.js.map +0 -1
  37. package/dist-esm/src/credential/m365TenantCredential.browser.js +0 -38
  38. package/dist-esm/src/credential/m365TenantCredential.browser.js.map +0 -1
  39. package/dist-esm/src/credential/m365TenantCredential.js +0 -126
  40. package/dist-esm/src/credential/m365TenantCredential.js.map +0 -1
  41. package/dist-esm/src/credential/onBehalfOfUserCredential.browser.js +0 -46
  42. package/dist-esm/src/credential/onBehalfOfUserCredential.browser.js.map +0 -1
  43. package/dist-esm/src/credential/onBehalfOfUserCredential.js +0 -178
  44. package/dist-esm/src/credential/onBehalfOfUserCredential.js.map +0 -1
  45. package/dist-esm/src/credential/teamsUserCredential.browser.js +0 -462
  46. package/dist-esm/src/credential/teamsUserCredential.browser.js.map +0 -1
  47. package/dist-esm/src/credential/teamsUserCredential.js +0 -56
  48. package/dist-esm/src/credential/teamsUserCredential.js.map +0 -1
  49. package/dist-esm/src/index.js +0 -14
  50. package/dist-esm/src/index.js.map +0 -1
  51. package/dist-esm/src/models/accessTokenResult.js +0 -4
  52. package/dist-esm/src/models/accessTokenResult.js.map +0 -1
  53. package/dist-esm/src/models/authCodeResult.js +0 -4
  54. package/dist-esm/src/models/authCodeResult.js.map +0 -1
  55. package/dist-esm/src/models/configuration.js +0 -20
  56. package/dist-esm/src/models/configuration.js.map +0 -1
  57. package/dist-esm/src/models/grantType.js +0 -11
  58. package/dist-esm/src/models/grantType.js.map +0 -1
  59. package/dist-esm/src/models/ssoTokenInfo.js +0 -4
  60. package/dist-esm/src/models/ssoTokenInfo.js.map +0 -1
  61. package/dist-esm/src/models/userinfo.js +0 -4
  62. package/dist-esm/src/models/userinfo.js.map +0 -1
  63. package/dist-esm/src/util/logger.js +0 -134
  64. package/dist-esm/src/util/logger.js.map +0 -1
  65. package/dist-esm/src/util/utils.js +0 -130
  66. package/dist-esm/src/util/utils.js.map +0 -1
  67. package/dist-esm/src/util/utils.node.js +0 -23
  68. package/dist-esm/src/util/utils.node.js.map +0 -1
@@ -1,1530 +1,1491 @@
1
- 'use strict';
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
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var tslib = require('tslib');
6
- var jwt_decode = require('jwt-decode');
7
- var crypto = require('crypto');
8
- var coreHttp = require('@azure/core-http');
9
- var msalNode = require('@azure/msal-node');
10
- var botbuilder = require('botbuilder');
11
- var botbuilderDialogs = require('botbuilder-dialogs');
12
- var uuid = require('uuid');
13
- var microsoftGraphClient = require('@microsoft/microsoft-graph-client');
14
- var identity = require('@azure/identity');
15
-
16
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
17
-
18
- var jwt_decode__default = /*#__PURE__*/_interopDefaultLegacy(jwt_decode);
19
-
20
- // Copyright (c) Microsoft Corporation.
21
- // Licensed under the MIT license.
22
- /**
23
- * Error code to trace the error types.
24
- * @beta
25
- */
26
- exports.ErrorCode = void 0;
27
- (function (ErrorCode) {
28
- /**
29
- * Invalid parameter error.
30
- */
31
- ErrorCode["InvalidParameter"] = "InvalidParameter";
32
- /**
33
- * Invalid configuration error.
34
- */
35
- ErrorCode["InvalidConfiguration"] = "InvalidConfiguration";
36
- /**
37
- * Invalid certificate error.
38
- */
39
- ErrorCode["InvalidCertificate"] = "InvalidCertificate";
40
- /**
41
- * Internal error.
42
- */
43
- ErrorCode["InternalError"] = "InternalError";
44
- /**
45
- * Channel is not supported error.
46
- */
47
- ErrorCode["ChannelNotSupported"] = "ChannelNotSupported";
48
- /**
49
- * Runtime is not supported error.
50
- */
51
- ErrorCode["RuntimeNotSupported"] = "RuntimeNotSupported";
52
- /**
53
- * User failed to finish the AAD consent flow failed.
54
- */
55
- ErrorCode["ConsentFailed"] = "ConsentFailed";
56
- /**
57
- * The user or administrator has not consented to use the application error.
58
- */
59
- ErrorCode["UiRequiredError"] = "UiRequiredError";
60
- /**
61
- * Token is not within its valid time range error.
62
- */
63
- ErrorCode["TokenExpiredError"] = "TokenExpiredError";
64
- /**
65
- * Call service (AAD or simple authentication server) failed.
66
- */
67
- ErrorCode["ServiceError"] = "ServiceError";
68
- /**
69
- * Operation failed.
70
- */
71
- ErrorCode["FailedOperation"] = "FailedOperation";
72
- })(exports.ErrorCode || (exports.ErrorCode = {}));
73
- /**
74
- * @internal
75
- */
76
- class ErrorMessage {
77
- }
78
- // InvalidConfiguration Error
79
- ErrorMessage.InvalidConfiguration = "{0} in configuration is invalid: {1}.";
80
- ErrorMessage.ConfigurationNotExists = "Configuration does not exist. {0}";
81
- ErrorMessage.ResourceConfigurationNotExists = "{0} resource configuration does not exist.";
82
- ErrorMessage.MissingResourceConfiguration = "Missing resource configuration with type: {0}, name: {1}.";
83
- ErrorMessage.AuthenticationConfigurationNotExists = "Authentication configuration does not exist.";
84
- // RuntimeNotSupported Error
85
- ErrorMessage.BrowserRuntimeNotSupported = "{0} is not supported in browser.";
86
- ErrorMessage.NodejsRuntimeNotSupported = "{0} is not supported in Node.";
87
- // Internal Error
88
- ErrorMessage.FailToAcquireTokenOnBehalfOfUser = "Failed to acquire access token on behalf of user: {0}";
89
- // ChannelNotSupported Error
90
- ErrorMessage.OnlyMSTeamsChannelSupported = "{0} is only supported in MS Teams Channel";
91
- /**
92
- * Error class with code and message thrown by the SDK.
93
- *
94
- * @beta
95
- */
96
- class ErrorWithCode extends Error {
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
- * Log level.
121
- *
122
- * @beta
123
- */
124
- exports.LogLevel = void 0;
125
- (function (LogLevel) {
126
- /**
127
- * Show verbose, information, warning and error message.
128
- */
129
- LogLevel[LogLevel["Verbose"] = 0] = "Verbose";
130
- /**
131
- * Show information, warning and error message.
132
- */
133
- LogLevel[LogLevel["Info"] = 1] = "Info";
134
- /**
135
- * Show warning and error message.
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
- * Parse jwt token payload
254
- *
255
- * @param token
256
- *
257
- * @returns Payload object
258
- *
259
- * @internal
260
- */
261
- function parseJwt(token) {
262
- try {
263
- const tokenObj = jwt_decode__default['default'](token);
264
- if (!tokenObj || !tokenObj.exp) {
265
- throw new ErrorWithCode("Decoded token is null or exp claim does not exists.", exports.ErrorCode.InternalError);
266
- }
267
- return tokenObj;
268
- }
269
- catch (err) {
270
- const errorMsg = "Parse jwt token failed in node env with error: " + err.message;
271
- internalLogger.error(errorMsg);
272
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InternalError);
273
- }
274
- }
275
- /**
276
- * @internal
277
- */
278
- function getUserInfoFromSsoToken(ssoToken) {
279
- if (!ssoToken) {
280
- const errorMsg = "SSO token is undefined.";
281
- internalLogger.error(errorMsg);
282
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidParameter);
283
- }
284
- const tokenObject = parseJwt(ssoToken);
285
- const userInfo = {
286
- displayName: tokenObject.name,
287
- objectId: tokenObject.oid,
288
- preferredUserName: "",
289
- };
290
- if (tokenObject.ver === "2.0") {
291
- userInfo.preferredUserName = tokenObject.preferred_username;
292
- }
293
- else if (tokenObject.ver === "1.0") {
294
- userInfo.preferredUserName = tokenObject.upn;
295
- }
296
- return userInfo;
297
- }
298
- /**
299
- * Format string template with replacements
300
- *
301
- * ```typescript
302
- * const template = "{0} and {1} are fruit. {0} is my favorite one."
303
- * const formattedStr = formatString(template, "apple", "pear"); // formattedStr: "apple and pear are fruit. apple is my favorite one."
304
- * ```
305
- *
306
- * @param str string template
307
- * @param replacements replacement string array
308
- * @returns Formatted string
309
- *
310
- * @internal
311
- */
312
- function formatString(str, ...replacements) {
313
- const args = replacements;
314
- return str.replace(/{(\d+)}/g, function (match, number) {
315
- return typeof args[number] != "undefined" ? args[number] : match;
316
- });
317
- }
318
- /**
319
- * @internal
320
- */
321
- function validateScopesType(value) {
322
- // string
323
- if (typeof value === "string" || value instanceof String) {
324
- return;
325
- }
326
- // empty array
327
- if (Array.isArray(value) && value.length === 0) {
328
- return;
329
- }
330
- // string array
331
- if (Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string")) {
332
- return;
333
- }
334
- const errorMsg = "The type of scopes is not valid, it must be string or string array";
335
- internalLogger.error(errorMsg);
336
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidParameter);
337
- }
338
- /**
339
- * @internal
340
- */
341
- function getScopesArray(scopes) {
342
- const scopesArray = typeof scopes === "string" ? scopes.split(" ") : scopes;
343
- return scopesArray.filter((x) => x !== null && x !== "");
344
- }
345
- /**
346
- * @internal
347
- */
348
- function getAuthority(authorityHost, tenantId) {
349
- const normalizedAuthorityHost = authorityHost.replace(/\/+$/g, "");
350
- return normalizedAuthorityHost + "/" + tenantId;
351
- }
352
- /**
353
- * @internal
354
- */
355
- function parseCertificate(certificateContent) {
356
- if (!certificateContent) {
357
- return undefined;
358
- }
359
- const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/;
360
- const match = certificatePattern.exec(certificateContent);
361
- if (!match) {
362
- const errorMsg = "The certificate content does not contain a PEM-encoded certificate.";
363
- internalLogger.error(errorMsg);
364
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidCertificate);
365
- }
366
- const thumbprint = crypto.createHash("sha1")
367
- .update(Buffer.from(match[3], "base64"))
368
- .digest("hex")
369
- .toUpperCase();
370
- return {
371
- thumbprint: thumbprint,
372
- privateKey: certificateContent,
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
- // Licensed under the MIT license.
378
- /**
379
- * Available resource type.
380
- * @beta
381
- */
382
- exports.ResourceType = void 0;
383
- (function (ResourceType) {
384
- /**
385
- * SQL database.
386
- *
387
- */
388
- ResourceType[ResourceType["SQL"] = 0] = "SQL";
389
- /**
390
- * Rest API.
391
- *
392
- */
393
- ResourceType[ResourceType["API"] = 1] = "API";
394
- })(exports.ResourceType || (exports.ResourceType = {}));
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 (!coreHttp.isNode) {
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, exports.ErrorCode.InvalidParameter);
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: exports.ResourceType.SQL,
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: exports.ResourceType.API,
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 ${exports.ResourceType[resourceType]} from ${resourceName}`);
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, exports.ResourceType[resourceType], resourceName);
492
- internalLogger.error(errorMsg);
493
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
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), exports.ErrorCode.InvalidConfiguration);
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 msalNode.ConfidentialClientApplication({
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
- return tslib.__awaiter(this, void 0, void 0, function* () {
595
- let accessToken;
596
- validateScopesType(scopes);
597
- const scopesStr = typeof scopes === "string" ? scopes : scopes.join(" ");
598
- internalLogger.info("Get access token with scopes: " + scopesStr);
599
- try {
600
- const scopesArray = getScopesArray(scopes);
601
- const authenticationResult = yield this.msalClient.acquireTokenByClientCredential({
602
- scopes: scopesArray,
603
- });
604
- if (authenticationResult) {
605
- accessToken = {
606
- token: authenticationResult.accessToken,
607
- expiresOnTimestamp: authenticationResult.expiresOn.getTime(),
608
- };
609
- }
610
- }
611
- catch (err) {
612
- const errorMsg = "Get M365 tenant credential failed with error: " + err.message;
613
- internalLogger.error(errorMsg);
614
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.ServiceError);
615
- }
616
- if (!accessToken) {
617
- const errorMsg = "Get M365 tenant credential access token failed with empty access token";
618
- internalLogger.error(errorMsg);
619
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InternalError);
620
- }
621
- return accessToken;
622
- });
623
- }
624
- /**
625
- * Load and validate authentication configuration
626
- * @returns Authentication configuration
627
- */
628
- loadAndValidateConfig() {
629
- internalLogger.verbose("Validate authentication configuration");
630
- const config = getAuthenticationConfiguration();
631
- if (!config) {
632
- internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);
633
- throw new ErrorWithCode(ErrorMessage.AuthenticationConfigurationNotExists, exports.ErrorCode.InvalidConfiguration);
634
- }
635
- if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {
636
- return config;
637
- }
638
- const missingValues = [];
639
- if (!config.clientId) {
640
- missingValues.push("clientId");
641
- }
642
- if (!config.clientSecret && !config.certificateContent) {
643
- missingValues.push("clientSecret or certificateContent");
644
- }
645
- if (!config.tenantId) {
646
- missingValues.push("tenantId");
647
- }
648
- const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingValues.join(", "), "undefined");
649
- internalLogger.error(errorMsg);
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, exports.ErrorCode.InvalidConfiguration);
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
- return tslib.__awaiter(this, void 0, void 0, function* () {
749
- validateScopesType(scopes);
750
- const scopesArray = getScopesArray(scopes);
751
- let result;
752
- if (!scopesArray.length) {
753
- internalLogger.info("Get SSO token.");
754
- if (Math.floor(Date.now() / 1000) > this.ssoToken.expiresOnTimestamp) {
755
- const errorMsg = "Sso token has already expired.";
756
- internalLogger.error(errorMsg);
757
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.TokenExpiredError);
758
- }
759
- result = this.ssoToken;
760
- }
761
- else {
762
- internalLogger.info("Get access token with scopes: " + scopesArray.join(" "));
763
- let authenticationResult;
764
- try {
765
- authenticationResult = yield this.msalClient.acquireTokenOnBehalfOf({
766
- oboAssertion: this.ssoToken.token,
767
- scopes: scopesArray,
768
- });
769
- }
770
- catch (error) {
771
- throw this.generateAuthServerError(error);
772
- }
773
- if (!authenticationResult) {
774
- const errorMsg = "Access token is null";
775
- internalLogger.error(errorMsg);
776
- throw new ErrorWithCode(formatString(ErrorMessage.FailToAcquireTokenOnBehalfOfUser, errorMsg), exports.ErrorCode.InternalError);
777
- }
778
- result = {
779
- token: authenticationResult.accessToken,
780
- expiresOnTimestamp: authenticationResult.expiresOn.getTime(),
781
- };
782
- }
783
- return result;
784
- });
785
- }
786
- /**
787
- * Get basic user info from SSO token.
788
- *
789
- * @example
790
- * ```typescript
791
- * const currentUser = getUserInfo();
792
- * ```
793
- *
794
- * @throws {@link ErrorCode|InternalError} when SSO token is not valid.
795
- * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
796
- *
797
- * @returns Basic user info with user displayName, objectId and preferredUserName.
798
- *
799
- * @beta
800
- */
801
- getUserInfo() {
802
- internalLogger.info("Get basic user info from SSO token");
803
- return getUserInfoFromSsoToken(this.ssoToken.token);
804
- }
805
- generateAuthServerError(err) {
806
- const errorMessage = err.errorMessage;
807
- if (err.name === "InteractionRequiredAuthError") {
808
- const fullErrorMsg = "Failed to get access token from AAD server, interaction required: " + errorMessage;
809
- internalLogger.warn(fullErrorMsg);
810
- return new ErrorWithCode(fullErrorMsg, exports.ErrorCode.UiRequiredError);
811
- }
812
- else if (errorMessage && errorMessage.indexOf("AADSTS500133") >= 0) {
813
- const fullErrorMsg = "Failed to get access token from AAD server, sso token expired: " + errorMessage;
814
- internalLogger.error(fullErrorMsg);
815
- return new ErrorWithCode(fullErrorMsg, exports.ErrorCode.TokenExpiredError);
816
- }
817
- else {
818
- const fullErrorMsg = formatString(ErrorMessage.FailToAcquireTokenOnBehalfOfUser, errorMessage);
819
- internalLogger.error(fullErrorMsg);
820
- return new ErrorWithCode(fullErrorMsg, exports.ErrorCode.ServiceError);
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"), exports.ErrorCode.RuntimeNotSupported);
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
- return tslib.__awaiter(this, void 0, void 0, function* () {
852
- throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), exports.ErrorCode.RuntimeNotSupported);
853
- });
854
- }
855
- /**
856
- * Get access token from credential.
857
- * @remarks
858
- * Can only be used within Teams.
859
- * @beta
860
- */
861
- getToken(scopes, options) {
862
- return tslib.__awaiter(this, void 0, void 0, function* () {
863
- throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), exports.ErrorCode.RuntimeNotSupported);
864
- });
865
- }
866
- /**
867
- * Get basic user info from SSO token
868
- * @remarks
869
- * Can only be used within Teams.
870
- * @beta
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
- return tslib.__awaiter(this, void 0, void 0, function* () {
924
- internalLogger.info(`Get Graph Access token with scopes: '${this.scopes}'`);
925
- const accessToken = yield this.credential.getToken(this.scopes);
926
- return new Promise((resolve, reject) => {
927
- if (accessToken) {
928
- resolve(accessToken.token);
929
- }
930
- else {
931
- const errorMsg = "Graph access token is undefined or empty";
932
- internalLogger.error(errorMsg);
933
- reject(new ErrorWithCode(errorMsg, exports.ErrorCode.InternalError));
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
- const invokeResponseType = "invokeResponse";
942
- /**
943
- * Response body returned for a token exchange invoke activity.
944
- *
945
- * @beta
946
- */
947
- class TokenExchangeInvokeResponse {
948
- constructor(id, failureDetail) {
949
- this.id = id;
950
- this.failureDetail = failureDetail;
951
- }
952
- }
953
- /**
954
- * Creates a new prompt that leverage Teams Single Sign On (SSO) support for bot to automatically sign in user and
955
- * help receive oauth token, asks the user to consent if needed.
956
- *
957
- * @remarks
958
- * The prompt will attempt to retrieve the users current token of the desired scopes and store it in
959
- * the token store.
960
- *
961
- * User will be automatically signed in leveraging Teams support of Bot Single Sign On(SSO):
962
- * https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/auth-aad-sso-bots
963
- *
964
- * @example
965
- * When used with your bots `DialogSet` you can simply add a new instance of the prompt as a named
966
- * dialog using `DialogSet.add()`. You can then start the prompt from a waterfall step using either
967
- * `DialogContext.beginDialog()` or `DialogContext.prompt()`. The user will be prompted to sign in as
968
- * needed and their access token will be passed as an argument to the callers next waterfall step:
969
- *
970
- * ```JavaScript
971
- * const { ConversationState, MemoryStorage } = require('botbuilder');
972
- * const { DialogSet, WaterfallDialog } = require('botbuilder-dialogs');
973
- * const { TeamsBotSsoPrompt } = require('@microsoft/teamsfx');
974
- *
975
- * const convoState = new ConversationState(new MemoryStorage());
976
- * const dialogState = convoState.createProperty('dialogState');
977
- * const dialogs = new DialogSet(dialogState);
978
- *
979
- * loadConfiguration();
980
- * dialogs.add(new TeamsBotSsoPrompt('TeamsBotSsoPrompt', {
981
- * scopes: ["User.Read"],
982
- * }));
983
- *
984
- * dialogs.add(new WaterfallDialog('taskNeedingLogin', [
985
- * async (step) => {
986
- * return await step.beginDialog('TeamsBotSsoPrompt');
987
- * },
988
- * async (step) => {
989
- * const token = step.result;
990
- * if (token) {
991
- *
992
- * // ... continue with task needing access token ...
993
- *
994
- * } else {
995
- * await step.context.sendActivity(`Sorry... We couldn't log you in. Try again later.`);
996
- * return await step.endDialog();
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
- * Get Microsoft graph client.
1282
- *
1283
- * @example
1284
- * Get Microsoft graph client by TokenCredential
1285
- * ```typescript
1286
- * // Sso token example (Azure Function)
1287
- * const ssoToken = "YOUR_TOKEN_STRING";
1288
- * const options = {"AAD_APP_ID", "AAD_APP_SECRET"};
1289
- * const credential = new OnBehalfOfAADUserCredential(ssoToken, options);
1290
- * const graphClient = await createMicrosoftGraphClient(credential);
1291
- * const profile = await graphClient.api("/me").get();
1292
- *
1293
- * // TeamsBotSsoPrompt example (Bot Application)
1294
- * const requiredScopes = ["User.Read"];
1295
- * const config: Configuration = {
1296
- * loginUrl: loginUrl,
1297
- * clientId: clientId,
1298
- * clientSecret: clientSecret,
1299
- * tenantId: tenantId
1300
- * };
1301
- * const prompt = new TeamsBotSsoPrompt(dialogId, {
1302
- * config: config
1303
- * scopes: '["User.Read"],
1304
- * });
1305
- * this.addDialog(prompt);
1306
- *
1307
- * const oboCredential = new OnBehalfOfAADUserCredential(
1308
- * getUserId(dialogContext),
1309
- * {
1310
- * clientId: "AAD_APP_ID",
1311
- * clientSecret: "AAD_APP_SECRET"
1312
- * });
1313
- * try {
1314
- * const graphClient = await createMicrosoftGraphClient(credential);
1315
- * const profile = await graphClient.api("/me").get();
1316
- * } catch (e) {
1317
- * dialogContext.beginDialog(dialogId);
1318
- * return Dialog.endOfTurn();
1319
- * }
1320
- * ```
1321
- *
1322
- * @param {TokenCredential} credential - token credential instance.
1323
- * @param scopes - The array of Microsoft Token scope of access. Default value is `[.default]`.
1324
- *
1325
- * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
1326
- *
1327
- * @returns Graph client with specified scopes.
1328
- *
1329
- * @beta
1330
- */
1331
- function createMicrosoftGraphClient(credential, scopes) {
1332
- internalLogger.info("Create Microsoft Graph Client");
1333
- const authProvider = new MsGraphAuthProvider(credential, scopes);
1334
- const graphClient = microsoftGraphClient.Client.initWithMiddleware({
1335
- authProvider,
1336
- });
1337
- return graphClient;
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
- * SQL connection configuration instance.
1343
- * @remarks
1344
- * Only works in in server side.
1345
- *
1346
- * @beta
1347
- *
1348
- */
1349
- class DefaultTediousConnectionConfiguration {
1350
- constructor() {
1351
- /**
1352
- * MSSQL default scope
1353
- * https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
1354
- */
1355
- this.defaultSQLScope = "https://database.windows.net/";
1356
- }
1357
- /**
1358
- * Generate connection configuration consumed by tedious.
1359
- *
1360
- * @returns Connection configuration of tedious for the SQL.
1361
- *
1362
- * @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
1363
- * @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
1364
- * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1365
- *
1366
- * @beta
1367
- */
1368
- getConfig() {
1369
- return tslib.__awaiter(this, void 0, void 0, function* () {
1370
- internalLogger.info("Get SQL configuration");
1371
- const configuration = getResourceConfiguration(exports.ResourceType.SQL);
1372
- if (!configuration) {
1373
- const errMsg = "SQL resource configuration not exist";
1374
- internalLogger.error(errMsg);
1375
- throw new ErrorWithCode(errMsg, exports.ErrorCode.InvalidConfiguration);
1376
- }
1377
- try {
1378
- this.isSQLConfigurationValid(configuration);
1379
- }
1380
- catch (err) {
1381
- throw err;
1382
- }
1383
- if (!this.isMsiAuthentication()) {
1384
- const configWithUPS = this.generateDefaultConfig(configuration);
1385
- internalLogger.verbose("SQL configuration with username and password generated");
1386
- return configWithUPS;
1387
- }
1388
- try {
1389
- const configWithToken = yield this.generateTokenConfig(configuration);
1390
- internalLogger.verbose("SQL configuration with MSI token generated");
1391
- return configWithToken;
1392
- }
1393
- catch (error) {
1394
- throw error;
1395
- }
1396
- });
1397
- }
1398
- /**
1399
- * Check SQL use MSI identity or username and password.
1400
- *
1401
- * @returns false - login with SQL MSI identity, true - login with username and password.
1402
- * @internal
1403
- */
1404
- isMsiAuthentication() {
1405
- internalLogger.verbose("Check connection config using MSI access token or username and password");
1406
- const configuration = getResourceConfiguration(exports.ResourceType.SQL);
1407
- if ((configuration === null || configuration === void 0 ? void 0 : configuration.sqlUsername) != null && (configuration === null || configuration === void 0 ? void 0 : configuration.sqlPassword) != null) {
1408
- internalLogger.verbose("Login with username and password");
1409
- return false;
1410
- }
1411
- internalLogger.verbose("Login with MSI identity");
1412
- return true;
1413
- }
1414
- /**
1415
- * check configuration is an available configurations.
1416
- * @param { SqlConfiguration } sqlConfig
1417
- *
1418
- * @returns true - SQL configuration has a valid SQL endpoints, SQL username with password or identity ID.
1419
- * false - configuration is not valid.
1420
- * @internal
1421
- */
1422
- isSQLConfigurationValid(sqlConfig) {
1423
- internalLogger.verbose("Check SQL configuration if valid");
1424
- if (!sqlConfig.sqlServerEndpoint) {
1425
- internalLogger.error("SQL configuration is not valid without SQL server endpoint exist");
1426
- throw new ErrorWithCode("SQL configuration error without SQL server endpoint exist", exports.ErrorCode.InvalidConfiguration);
1427
- }
1428
- if (!(sqlConfig.sqlUsername && sqlConfig.sqlPassword) && !sqlConfig.sqlIdentityId) {
1429
- const errMsg = `SQL configuration is not valid without ${sqlConfig.sqlIdentityId ? "" : "identity id "} ${sqlConfig.sqlUsername ? "" : "SQL username "} ${sqlConfig.sqlPassword ? "" : "SQL password"} exist`;
1430
- internalLogger.error(errMsg);
1431
- throw new ErrorWithCode(errMsg, exports.ErrorCode.InvalidConfiguration);
1432
- }
1433
- internalLogger.verbose("SQL configuration is valid");
1434
- }
1435
- /**
1436
- * Generate tedious connection configuration with default authentication type.
1437
- *
1438
- * @param { SqlConfiguration } SQL configuration with username and password.
1439
- *
1440
- * @returns Tedious connection configuration with username and password.
1441
- * @internal
1442
- */
1443
- generateDefaultConfig(sqlConfig) {
1444
- internalLogger.verbose(`SQL server ${sqlConfig.sqlServerEndpoint}, user name ${sqlConfig.sqlUsername}, database name ${sqlConfig.sqlDatabaseName}`);
1445
- const config = {
1446
- server: sqlConfig.sqlServerEndpoint,
1447
- authentication: {
1448
- type: TediousAuthenticationType.default,
1449
- options: {
1450
- userName: sqlConfig.sqlUsername,
1451
- password: sqlConfig.sqlPassword,
1452
- },
1453
- },
1454
- options: {
1455
- database: sqlConfig.sqlDatabaseName,
1456
- encrypt: true,
1457
- },
1458
- };
1459
- return config;
1460
- }
1461
- /**
1462
- * Generate tedious connection configuration with azure-active-directory-access-token authentication type.
1463
- *
1464
- * @param { SqlConfiguration } SQL configuration with AAD access token.
1465
- *
1466
- * @returns Tedious connection configuration with access token.
1467
- * @internal
1468
- */
1469
- generateTokenConfig(sqlConfig) {
1470
- return tslib.__awaiter(this, void 0, void 0, function* () {
1471
- internalLogger.verbose("Generate tedious config with MSI token");
1472
- let token;
1473
- try {
1474
- const credential = new identity.ManagedIdentityCredential(sqlConfig.sqlIdentityId);
1475
- token = yield credential.getToken(this.defaultSQLScope);
1476
- }
1477
- catch (error) {
1478
- const errMsg = "Get user MSI token failed";
1479
- internalLogger.error(errMsg);
1480
- throw new ErrorWithCode(errMsg, exports.ErrorCode.InternalError);
1481
- }
1482
- if (token) {
1483
- const config = {
1484
- server: sqlConfig.sqlServerEndpoint,
1485
- authentication: {
1486
- type: TediousAuthenticationType.MSI,
1487
- options: {
1488
- token: token.token,
1489
- },
1490
- },
1491
- options: {
1492
- database: sqlConfig.sqlDatabaseName,
1493
- encrypt: true,
1494
- },
1495
- };
1496
- internalLogger.verbose(`Generate token configuration success, server endpoint is ${sqlConfig.sqlServerEndpoint}, database name is ${sqlConfig.sqlDatabaseName}`);
1497
- return config;
1498
- }
1499
- internalLogger.error(`Generate token configuration, server endpoint is ${sqlConfig.sqlServerEndpoint}, MSI token is not valid`);
1500
- throw new ErrorWithCode("MSI token is not valid", exports.ErrorCode.InternalError);
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
- exports.DefaultTediousConnectionConfiguration = DefaultTediousConnectionConfiguration;
1516
- exports.ErrorWithCode = ErrorWithCode;
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