@microsoft/teamsfx 0.3.0-alpha.8101fb46.0 → 0.3.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -17
- package/dist/index.js +127 -53
- package/dist/index.js.map +1 -1
- package/dist/teamsfx.js +30 -0
- package/dist/teamsfx.js.map +1 -0
- package/dist-esm/src/bot/teamsBotSsoPrompt.js +7 -4
- package/dist-esm/src/bot/teamsBotSsoPrompt.js.map +1 -1
- package/dist-esm/src/core/errors.js +4 -0
- package/dist-esm/src/core/errors.js.map +1 -1
- package/dist-esm/src/credential/m365TenantCredential.js +19 -21
- package/dist-esm/src/credential/m365TenantCredential.js.map +1 -1
- package/dist-esm/src/credential/onBehalfOfUserCredential.js +8 -17
- package/dist-esm/src/credential/onBehalfOfUserCredential.js.map +1 -1
- package/dist-esm/src/credential/teamsUserCredential.browser.js.map +1 -1
- package/dist-esm/src/models/configuration.js.map +1 -1
- package/dist-esm/src/util/logger.js +22 -3
- package/dist-esm/src/util/logger.js.map +1 -1
- package/dist-esm/src/util/utils.js +38 -0
- package/dist-esm/src/util/utils.js.map +1 -1
- package/dist-esm/src/util/utils.node.js +23 -0
- package/dist-esm/src/util/utils.node.js.map +1 -0
- package/package.json +24 -19
- package/types/teamsfx.d.ts +900 -871
package/README.md
CHANGED
|
@@ -112,16 +112,32 @@ TeamsFx SDK provides helper functions to ease the configuration for third-party
|
|
|
112
112
|
|
|
113
113
|
API will throw `ErrorWithCode` if error happens. Each `ErrorWithCode` contains error code and error message.
|
|
114
114
|
|
|
115
|
-
For example, to filter out
|
|
115
|
+
For example, to filter out specific error, you could use the following check:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
try {
|
|
119
|
+
const credential = new TeamsUserCredential();
|
|
120
|
+
await credential.login("User.Read");
|
|
121
|
+
} catch (err: unknown) {
|
|
122
|
+
if (err instanceof ErrorWithCode && err.code !== ErrorCode.ConsentFailed) {
|
|
123
|
+
throw err;
|
|
124
|
+
} else {
|
|
125
|
+
// Silently fail because user cancels the consent dialog
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
And if credential instance is used in other library like Microsoft Graph, it's possible that error is catched and transformed.
|
|
116
132
|
|
|
117
133
|
```ts
|
|
118
134
|
try {
|
|
119
135
|
const credential = new TeamsUserCredential();
|
|
120
136
|
const graphClient = createMicrosoftGraphClient(credential, ["User.Read"]); // Initializes MS Graph SDK using our MsGraphAuthProvider
|
|
121
137
|
const profile = await graphClient.api("/me").get();
|
|
122
|
-
} catch (err) {
|
|
123
|
-
//
|
|
124
|
-
if (err instanceof
|
|
138
|
+
} catch (err: unknown) {
|
|
139
|
+
// ErrorWithCode is handled by Graph client
|
|
140
|
+
if (err instanceof GraphError && err.code?.includes(ErrorCode.UiRequiredError)) {
|
|
125
141
|
this.setState({
|
|
126
142
|
showLoginBtn: true,
|
|
127
143
|
});
|
|
@@ -237,8 +253,12 @@ dialogs.add(
|
|
|
237
253
|
|
|
238
254
|
### Configure log
|
|
239
255
|
|
|
240
|
-
You can set custome log level and
|
|
241
|
-
|
|
256
|
+
You can set custome log level and redirect outputs when using this library.
|
|
257
|
+
Logging is turned off by default, you can turn it on by setting log level.
|
|
258
|
+
|
|
259
|
+
#### Enable log by setting log level
|
|
260
|
+
|
|
261
|
+
When log level is set, logging is enabled. It prints log information to console by default.
|
|
242
262
|
|
|
243
263
|
Set log level using the snippet below:
|
|
244
264
|
|
|
@@ -247,28 +267,33 @@ Set log level using the snippet below:
|
|
|
247
267
|
setLogLevel(LogLevel.Warn);
|
|
248
268
|
```
|
|
249
269
|
|
|
250
|
-
|
|
270
|
+
You can redirect log output by setting custom logger or log function.
|
|
271
|
+
|
|
272
|
+
##### Redirect by setting custom logger
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
setLogLevel(LogLevel.Info);
|
|
276
|
+
// Set another logger if you want to redirect to Application Insights in Azure Function
|
|
277
|
+
setLogger(context.log);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
##### Redirect by setting custom log function
|
|
281
|
+
|
|
282
|
+
Please note that log function will not take effect if you set a custom logger.
|
|
251
283
|
|
|
252
284
|
```ts
|
|
285
|
+
setLogLevel(LogLevel.Info);
|
|
253
286
|
// Only log error message to Application Insights in bot application.
|
|
254
|
-
setLogFunction((level: LogLevel,
|
|
287
|
+
setLogFunction((level: LogLevel, message: string) => {
|
|
255
288
|
if (level === LogLevel.Error) {
|
|
256
|
-
const { format, ...rest } = args;
|
|
257
289
|
this.telemetryClient.trackTrace({
|
|
258
|
-
message:
|
|
290
|
+
message: message,
|
|
259
291
|
severityLevel: Severity.Error,
|
|
260
292
|
});
|
|
261
293
|
}
|
|
262
294
|
});
|
|
263
295
|
```
|
|
264
296
|
|
|
265
|
-
Set a custome logger instance:
|
|
266
|
-
|
|
267
|
-
```ts
|
|
268
|
-
// context.log send messages to Application Insights in Azure Function
|
|
269
|
-
setLogger(context.log);
|
|
270
|
-
```
|
|
271
|
-
|
|
272
297
|
## Next steps
|
|
273
298
|
|
|
274
299
|
Please take a look at the [Samples](https://github.com/OfficeDev/TeamsFx-Samples) project for detailed examples on how to use this library.
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var tslib = require('tslib');
|
|
6
|
-
var identity = require('@azure/identity');
|
|
7
6
|
var jwt_decode = require('jwt-decode');
|
|
7
|
+
var crypto = require('crypto');
|
|
8
8
|
var coreHttp = require('@azure/core-http');
|
|
9
9
|
var msalNode = require('@azure/msal-node');
|
|
10
|
-
var
|
|
10
|
+
var botbuilder = require('botbuilder');
|
|
11
11
|
var botbuilderDialogs = require('botbuilder-dialogs');
|
|
12
12
|
var uuid = require('uuid');
|
|
13
13
|
var microsoftGraphClient = require('@microsoft/microsoft-graph-client');
|
|
14
|
+
var identity = require('@azure/identity');
|
|
14
15
|
|
|
15
16
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
16
17
|
|
|
@@ -32,6 +33,10 @@ exports.ErrorCode = void 0;
|
|
|
32
33
|
* Invalid configuration error.
|
|
33
34
|
*/
|
|
34
35
|
ErrorCode["InvalidConfiguration"] = "InvalidConfiguration";
|
|
36
|
+
/**
|
|
37
|
+
* Invalid certificate error.
|
|
38
|
+
*/
|
|
39
|
+
ErrorCode["InvalidCertificate"] = "InvalidCertificate";
|
|
35
40
|
/**
|
|
36
41
|
* Internal error.
|
|
37
42
|
*/
|
|
@@ -157,7 +162,7 @@ function getLogLevel() {
|
|
|
157
162
|
}
|
|
158
163
|
class InternalLogger {
|
|
159
164
|
constructor() {
|
|
160
|
-
this.level =
|
|
165
|
+
this.level = undefined;
|
|
161
166
|
this.defaultLogger = {
|
|
162
167
|
verbose: console.debug,
|
|
163
168
|
info: console.info,
|
|
@@ -184,7 +189,7 @@ class InternalLogger {
|
|
|
184
189
|
const timestamp = new Date().toUTCString();
|
|
185
190
|
const logHeader = `[${timestamp}] : @microsoft/teamsfx : ${exports.LogLevel[logLevel]} - `;
|
|
186
191
|
const logMessage = `${logHeader}${message}`;
|
|
187
|
-
if (this.level <= logLevel) {
|
|
192
|
+
if (this.level !== undefined && this.level <= logLevel) {
|
|
188
193
|
if (this.customLogger) {
|
|
189
194
|
logFunction(this.customLogger)(logMessage);
|
|
190
195
|
}
|
|
@@ -204,10 +209,20 @@ class InternalLogger {
|
|
|
204
209
|
*/
|
|
205
210
|
const internalLogger = new InternalLogger();
|
|
206
211
|
/**
|
|
207
|
-
* Set custom logger. Use the output
|
|
212
|
+
* Set custom logger. Use the output functions if it's set. Priority is higher than setLogFunction.
|
|
208
213
|
*
|
|
209
214
|
* @param {Logger} logger - custom logger. If it's undefined, custom logger will be cleared.
|
|
210
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
|
+
*
|
|
211
226
|
* @beta
|
|
212
227
|
*/
|
|
213
228
|
function setLogger(logger) {
|
|
@@ -218,6 +233,15 @@ function setLogger(logger) {
|
|
|
218
233
|
*
|
|
219
234
|
* @param {LogFunction} logFunction - custom log function. If it's undefined, custom log function will be cleared.
|
|
220
235
|
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```typescript
|
|
238
|
+
* setLogFunction((level: LogLevel, message: string) => {
|
|
239
|
+
* if (level === LogLevel.Error) {
|
|
240
|
+
* console.log(message);
|
|
241
|
+
* }
|
|
242
|
+
* });
|
|
243
|
+
* ```
|
|
244
|
+
*
|
|
221
245
|
* @beta
|
|
222
246
|
*/
|
|
223
247
|
function setLogFunction(logFunction) {
|
|
@@ -311,6 +335,43 @@ function validateScopesType(value) {
|
|
|
311
335
|
internalLogger.error(errorMsg);
|
|
312
336
|
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidParameter);
|
|
313
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
|
+
};
|
|
374
|
+
}
|
|
314
375
|
|
|
315
376
|
// Copyright (c) Microsoft Corporation.
|
|
316
377
|
// Licensed under the MIT license.
|
|
@@ -450,6 +511,27 @@ function getAuthenticationConfiguration() {
|
|
|
450
511
|
throw new ErrorWithCode(formatString(ErrorMessage.ConfigurationNotExists, errorMsg), exports.ErrorCode.InvalidConfiguration);
|
|
451
512
|
}
|
|
452
513
|
|
|
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
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
453
535
|
// Copyright (c) Microsoft Corporation.
|
|
454
536
|
/**
|
|
455
537
|
* Represent Microsoft 365 tenant identity, and it is usually used when user is not involved like time-triggered automation job.
|
|
@@ -480,10 +562,7 @@ class M365TenantCredential {
|
|
|
480
562
|
constructor() {
|
|
481
563
|
internalLogger.info("Create M365 tenant credential");
|
|
482
564
|
const config = this.loadAndValidateConfig();
|
|
483
|
-
|
|
484
|
-
authorityHost: config.authorityHost,
|
|
485
|
-
};
|
|
486
|
-
this.clientSecretCredential = new identity.ClientSecretCredential(config.tenantId, config.clientId, config.clientSecret, tokenCredentialOptions);
|
|
565
|
+
this.msalClient = createConfidentialClientApplication(config);
|
|
487
566
|
}
|
|
488
567
|
/**
|
|
489
568
|
* Get access token for credential.
|
|
@@ -518,20 +597,21 @@ class M365TenantCredential {
|
|
|
518
597
|
const scopesStr = typeof scopes === "string" ? scopes : scopes.join(" ");
|
|
519
598
|
internalLogger.info("Get access token with scopes: " + scopesStr);
|
|
520
599
|
try {
|
|
521
|
-
|
|
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
|
+
}
|
|
522
610
|
}
|
|
523
611
|
catch (err) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
internalLogger.error(errorMsg);
|
|
528
|
-
throw new ErrorWithCode(errorMsg, exports.ErrorCode.ServiceError);
|
|
529
|
-
}
|
|
530
|
-
else {
|
|
531
|
-
const errorMsg = "Get M365 tenant credential failed with error: " + err.message;
|
|
532
|
-
internalLogger.error(errorMsg);
|
|
533
|
-
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InternalError);
|
|
534
|
-
}
|
|
612
|
+
const errorMsg = "Get M365 tenant credential failed with error: " + err.message;
|
|
613
|
+
internalLogger.error(errorMsg);
|
|
614
|
+
throw new ErrorWithCode(errorMsg, exports.ErrorCode.ServiceError);
|
|
535
615
|
}
|
|
536
616
|
if (!accessToken) {
|
|
537
617
|
const errorMsg = "Get M365 tenant credential access token failed with empty access token";
|
|
@@ -552,15 +632,15 @@ class M365TenantCredential {
|
|
|
552
632
|
internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);
|
|
553
633
|
throw new ErrorWithCode(ErrorMessage.AuthenticationConfigurationNotExists, exports.ErrorCode.InvalidConfiguration);
|
|
554
634
|
}
|
|
555
|
-
if (config.clientId && config.clientSecret && config.tenantId) {
|
|
635
|
+
if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {
|
|
556
636
|
return config;
|
|
557
637
|
}
|
|
558
638
|
const missingValues = [];
|
|
559
639
|
if (!config.clientId) {
|
|
560
640
|
missingValues.push("clientId");
|
|
561
641
|
}
|
|
562
|
-
if (!config.clientSecret) {
|
|
563
|
-
missingValues.push("clientSecret");
|
|
642
|
+
if (!config.clientSecret && !config.certificateContent) {
|
|
643
|
+
missingValues.push("clientSecret or certificateContent");
|
|
564
644
|
}
|
|
565
645
|
if (!config.tenantId) {
|
|
566
646
|
missingValues.push("tenantId");
|
|
@@ -595,7 +675,7 @@ class OnBehalfOfUserCredential {
|
|
|
595
675
|
*
|
|
596
676
|
* @param {string} ssoToken - User token provided by Teams SSO feature.
|
|
597
677
|
*
|
|
598
|
-
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, authority host or tenant id is not found in config.
|
|
678
|
+
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
|
|
599
679
|
* @throws {@link ErrorCode|InternalError} when SSO token is not valid.
|
|
600
680
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
601
681
|
*
|
|
@@ -611,10 +691,10 @@ class OnBehalfOfUserCredential {
|
|
|
611
691
|
if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.authorityHost)) {
|
|
612
692
|
missingConfigurations.push("authorityHost");
|
|
613
693
|
}
|
|
614
|
-
if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.clientSecret)) {
|
|
615
|
-
missingConfigurations.push("clientSecret");
|
|
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");
|
|
616
696
|
}
|
|
617
|
-
if (!((
|
|
697
|
+
if (!((_e = config === null || config === void 0 ? void 0 : config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId)) {
|
|
618
698
|
missingConfigurations.push("tenantId");
|
|
619
699
|
}
|
|
620
700
|
if (missingConfigurations.length != 0) {
|
|
@@ -622,15 +702,7 @@ class OnBehalfOfUserCredential {
|
|
|
622
702
|
internalLogger.error(errorMsg);
|
|
623
703
|
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
|
|
624
704
|
}
|
|
625
|
-
|
|
626
|
-
const authority = normalizedAuthorityHost + "/" + ((_e = config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId);
|
|
627
|
-
this.msalClient = new msalNode.ConfidentialClientApplication({
|
|
628
|
-
auth: {
|
|
629
|
-
clientId: config.authentication.clientId,
|
|
630
|
-
authority: authority,
|
|
631
|
-
clientSecret: config.authentication.clientSecret,
|
|
632
|
-
},
|
|
633
|
-
});
|
|
705
|
+
this.msalClient = createConfidentialClientApplication(config.authentication);
|
|
634
706
|
const decodedSsoToken = parseJwt(ssoToken);
|
|
635
707
|
this.ssoToken = {
|
|
636
708
|
token: ssoToken,
|
|
@@ -675,8 +747,7 @@ class OnBehalfOfUserCredential {
|
|
|
675
747
|
getToken(scopes, options) {
|
|
676
748
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
677
749
|
validateScopesType(scopes);
|
|
678
|
-
|
|
679
|
-
scopesArray = scopesArray.filter((x) => x !== null && x !== "");
|
|
750
|
+
const scopesArray = getScopesArray(scopes);
|
|
680
751
|
let result;
|
|
681
752
|
if (!scopesArray.length) {
|
|
682
753
|
internalLogger.info("Get SSO token.");
|
|
@@ -1022,7 +1093,7 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1022
1093
|
this.ensureMsTeamsChannel(dc);
|
|
1023
1094
|
// Check for timeout
|
|
1024
1095
|
const state = (_a = dc.activeDialog) === null || _a === void 0 ? void 0 : _a.state;
|
|
1025
|
-
const isMessage = dc.context.activity.type ===
|
|
1096
|
+
const isMessage = dc.context.activity.type === botbuilder.ActivityTypes.Message;
|
|
1026
1097
|
const isTimeoutActivityType = isMessage ||
|
|
1027
1098
|
this.isTeamsVerificationInvoke(dc.context) ||
|
|
1028
1099
|
this.isTokenExchangeRequestInvoke(dc.context);
|
|
@@ -1057,7 +1128,7 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1057
1128
|
* @internal
|
|
1058
1129
|
*/
|
|
1059
1130
|
ensureMsTeamsChannel(dc) {
|
|
1060
|
-
if (dc.context.activity.channelId !=
|
|
1131
|
+
if (dc.context.activity.channelId != botbuilder.Channels.Msteams) {
|
|
1061
1132
|
const errorMsg = formatString(ErrorMessage.OnlyMSTeamsChannelSupported, "Teams Bot SSO Prompt");
|
|
1062
1133
|
internalLogger.error(errorMsg);
|
|
1063
1134
|
throw new ErrorWithCode(errorMsg, exports.ErrorCode.ChannelNotSupported);
|
|
@@ -1072,10 +1143,13 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1072
1143
|
sendOAuthCardAsync(context) {
|
|
1073
1144
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1074
1145
|
internalLogger.verbose("Send OAuth card to get SSO token");
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
const
|
|
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);
|
|
1079
1153
|
// Send prompt
|
|
1080
1154
|
yield context.sendActivity(msg);
|
|
1081
1155
|
});
|
|
@@ -1087,7 +1161,7 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1087
1161
|
*
|
|
1088
1162
|
* @internal
|
|
1089
1163
|
*/
|
|
1090
|
-
getSignInResource() {
|
|
1164
|
+
getSignInResource(loginHint) {
|
|
1091
1165
|
var _a, _b, _c, _d, _e;
|
|
1092
1166
|
internalLogger.verbose("Get sign in authentication configuration");
|
|
1093
1167
|
const missingConfigurations = [];
|
|
@@ -1108,7 +1182,7 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1108
1182
|
internalLogger.error(errorMsg);
|
|
1109
1183
|
throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
|
|
1110
1184
|
}
|
|
1111
|
-
const signInLink = `${config.authentication.initiateLoginEndpoint}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${config.authentication.clientId}&tenantId=${config.authentication.tenantId}`;
|
|
1185
|
+
const signInLink = `${config.authentication.initiateLoginEndpoint}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${config.authentication.clientId}&tenantId=${config.authentication.tenantId}&loginHint=${loginHint}`;
|
|
1112
1186
|
internalLogger.verbose("Sign in link: " + signInLink);
|
|
1113
1187
|
const tokenExchangeResource = {
|
|
1114
1188
|
id: uuid.v4(),
|
|
@@ -1133,7 +1207,7 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1133
1207
|
if (!(context.activity.value && this.isTokenExchangeRequest(context.activity.value))) {
|
|
1134
1208
|
const warningMsg = "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.";
|
|
1135
1209
|
internalLogger.warn(warningMsg);
|
|
1136
|
-
yield context.sendActivity(this.getTokenExchangeInvokeResponse(
|
|
1210
|
+
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder.StatusCodes.BAD_REQUEST, warningMsg));
|
|
1137
1211
|
}
|
|
1138
1212
|
else {
|
|
1139
1213
|
const ssoToken = context.activity.value.token;
|
|
@@ -1142,7 +1216,7 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1142
1216
|
try {
|
|
1143
1217
|
exchangedToken = yield credential.getToken(this.settings.scopes);
|
|
1144
1218
|
if (exchangedToken) {
|
|
1145
|
-
yield context.sendActivity(this.getTokenExchangeInvokeResponse(
|
|
1219
|
+
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder.StatusCodes.OK, "", context.activity.value.id));
|
|
1146
1220
|
const ssoTokenExpiration = parseJwt(ssoToken).exp;
|
|
1147
1221
|
tokenResponse = {
|
|
1148
1222
|
ssoToken: ssoToken,
|
|
@@ -1156,14 +1230,14 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1156
1230
|
catch (error) {
|
|
1157
1231
|
const warningMsg = "The bot is unable to exchange token. Ask for user consent.";
|
|
1158
1232
|
internalLogger.info(warningMsg);
|
|
1159
|
-
yield context.sendActivity(this.getTokenExchangeInvokeResponse(
|
|
1233
|
+
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder.StatusCodes.PRECONDITION_FAILED, warningMsg, context.activity.value.id));
|
|
1160
1234
|
}
|
|
1161
1235
|
}
|
|
1162
1236
|
}
|
|
1163
1237
|
else if (this.isTeamsVerificationInvoke(context)) {
|
|
1164
1238
|
internalLogger.verbose("Receive Teams state verification request");
|
|
1165
1239
|
yield this.sendOAuthCardAsync(dc.context);
|
|
1166
|
-
yield context.sendActivity({ type: invokeResponseType, value: { status:
|
|
1240
|
+
yield context.sendActivity({ type: invokeResponseType, value: { status: botbuilder.StatusCodes.OK } });
|
|
1167
1241
|
}
|
|
1168
1242
|
return tokenResponse !== undefined
|
|
1169
1243
|
? { succeeded: true, value: tokenResponse }
|
|
@@ -1185,14 +1259,14 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
|
|
|
1185
1259
|
*/
|
|
1186
1260
|
isTeamsVerificationInvoke(context) {
|
|
1187
1261
|
const activity = context.activity;
|
|
1188
|
-
return activity.type ===
|
|
1262
|
+
return activity.type === botbuilder.ActivityTypes.Invoke && activity.name === botbuilder.verifyStateOperationName;
|
|
1189
1263
|
}
|
|
1190
1264
|
/**
|
|
1191
1265
|
* @internal
|
|
1192
1266
|
*/
|
|
1193
1267
|
isTokenExchangeRequestInvoke(context) {
|
|
1194
1268
|
const activity = context.activity;
|
|
1195
|
-
return activity.type ===
|
|
1269
|
+
return activity.type === botbuilder.ActivityTypes.Invoke && activity.name === botbuilder.tokenExchangeOperationName;
|
|
1196
1270
|
}
|
|
1197
1271
|
/**
|
|
1198
1272
|
* @internal
|