@microsoft/teamsfx 0.5.2-alpha.313131ea.0 → 0.5.2-alpha.e7b82d5e0.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.
@@ -2,8 +2,8 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var jwt_decode = require('jwt-decode');
6
5
  var tslib = require('tslib');
6
+ var jwt_decode = require('jwt-decode');
7
7
  var msalNode = require('@azure/msal-node');
8
8
  var crypto = require('crypto');
9
9
  var microsoftGraphClient = require('@microsoft/microsoft-graph-client');
@@ -72,6 +72,10 @@ exports.ErrorCode = void 0;
72
72
  * Invalid response error.
73
73
  */
74
74
  ErrorCode["InvalidResponse"] = "InvalidResponse";
75
+ /**
76
+ * Identity type error.
77
+ */
78
+ ErrorCode["IdentityTypeNotSupported"] = "IdentityTypeNotSupported";
75
79
  })(exports.ErrorCode || (exports.ErrorCode = {}));
76
80
  /**
77
81
  * @internal
@@ -91,6 +95,8 @@ ErrorMessage.NodejsRuntimeNotSupported = "{0} is not supported in Node.";
91
95
  ErrorMessage.FailToAcquireTokenOnBehalfOfUser = "Failed to acquire access token on behalf of user: {0}";
92
96
  // ChannelNotSupported Error
93
97
  ErrorMessage.OnlyMSTeamsChannelSupported = "{0} is only supported in MS Teams Channel";
98
+ // IdentityTypeNotSupported Error
99
+ ErrorMessage.IdentityTypeNotSupported = "{0} identity is not supported in {1}";
94
100
  /**
95
101
  * Error class with code and message thrown by the SDK.
96
102
  *
@@ -117,26 +123,6 @@ class ErrorWithCode extends Error {
117
123
  }
118
124
  }
119
125
 
120
- // Copyright (c) Microsoft Corporation.
121
- // Licensed under the MIT license.
122
- /**
123
- * Available resource type.
124
- * @beta
125
- */
126
- exports.ResourceType = void 0;
127
- (function (ResourceType) {
128
- /**
129
- * SQL database.
130
- *
131
- */
132
- ResourceType[ResourceType["SQL"] = 0] = "SQL";
133
- /**
134
- * Rest API.
135
- *
136
- */
137
- ResourceType[ResourceType["API"] = 1] = "API";
138
- })(exports.ResourceType || (exports.ResourceType = {}));
139
-
140
126
  // Copyright (c) Microsoft Corporation.
141
127
  // Licensed under the MIT license.
142
128
  /**
@@ -379,131 +365,6 @@ function getScopesArray(scopes) {
379
365
  function getAuthority(authorityHost, tenantId) {
380
366
  const normalizedAuthorityHost = authorityHost.replace(/\/+$/g, "");
381
367
  return normalizedAuthorityHost + "/" + tenantId;
382
- }
383
- /**
384
- * @internal
385
- */
386
- const isNode = typeof process !== "undefined" &&
387
- !!process.version &&
388
- !!process.versions &&
389
- !!process.versions.node;
390
-
391
- // Copyright (c) Microsoft Corporation.
392
- /**
393
- * Global configuration instance
394
- *
395
- */
396
- let config;
397
- /**
398
- * Initialize configuration from environment variables or configuration object and set the global instance
399
- *
400
- * @param {Configuration} configuration - Optional configuration that overrides the default configuration values. The override depth is 1.
401
- *
402
- * @throws {@link ErrorCode|InvalidParameter} when configuration is not passed in browser environment
403
- *
404
- * @beta
405
- */
406
- function loadConfiguration(configuration) {
407
- internalLogger.info("load configuration");
408
- // browser environment
409
- if (!isNode) {
410
- if (!configuration) {
411
- const errorMsg = "You are running the code in browser. Configuration must be passed in.";
412
- internalLogger.error(errorMsg);
413
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidParameter);
414
- }
415
- config = configuration;
416
- return;
417
- }
418
- // node environment
419
- let newAuthentication;
420
- let newResources = [];
421
- const defaultResourceName = "default";
422
- if (configuration === null || configuration === void 0 ? void 0 : configuration.authentication) {
423
- newAuthentication = configuration.authentication;
424
- }
425
- else {
426
- newAuthentication = {
427
- authorityHost: process.env.M365_AUTHORITY_HOST,
428
- tenantId: process.env.M365_TENANT_ID,
429
- clientId: process.env.M365_CLIENT_ID,
430
- clientSecret: process.env.M365_CLIENT_SECRET,
431
- simpleAuthEndpoint: process.env.SIMPLE_AUTH_ENDPOINT,
432
- initiateLoginEndpoint: process.env.INITIATE_LOGIN_ENDPOINT,
433
- applicationIdUri: process.env.M365_APPLICATION_ID_URI,
434
- };
435
- }
436
- if (configuration === null || configuration === void 0 ? void 0 : configuration.resources) {
437
- newResources = configuration.resources;
438
- }
439
- else {
440
- newResources = [
441
- {
442
- // SQL resource
443
- type: exports.ResourceType.SQL,
444
- name: defaultResourceName,
445
- properties: {
446
- sqlServerEndpoint: process.env.SQL_ENDPOINT,
447
- sqlUsername: process.env.SQL_USER_NAME,
448
- sqlPassword: process.env.SQL_PASSWORD,
449
- sqlDatabaseName: process.env.SQL_DATABASE_NAME,
450
- sqlIdentityId: process.env.IDENTITY_ID,
451
- },
452
- },
453
- {
454
- // API resource
455
- type: exports.ResourceType.API,
456
- name: defaultResourceName,
457
- properties: {
458
- endpoint: process.env.API_ENDPOINT,
459
- },
460
- },
461
- ];
462
- }
463
- config = {
464
- authentication: newAuthentication,
465
- resources: newResources,
466
- };
467
- }
468
- /**
469
- * Get configuration for a specific resource.
470
- * @param {ResourceType} resourceType - The type of resource
471
- * @param {string} resourceName - The name of resource, default value is "default".
472
- *
473
- * @returns Resource configuration for target resource from global configuration instance.
474
- *
475
- * @throws {@link ErrorCode|InvalidConfiguration} when resource configuration with the specific type and name is not found
476
- *
477
- * @beta
478
- */
479
- function getResourceConfiguration(resourceType, resourceName = "default") {
480
- var _a;
481
- internalLogger.info(`Get resource configuration of ${exports.ResourceType[resourceType]} from ${resourceName}`);
482
- const result = (_a = config.resources) === null || _a === void 0 ? void 0 : _a.find((item) => item.type === resourceType && item.name === resourceName);
483
- if (result) {
484
- return result.properties;
485
- }
486
- const errorMsg = formatString(ErrorMessage.MissingResourceConfiguration, exports.ResourceType[resourceType], resourceName);
487
- internalLogger.error(errorMsg);
488
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
489
- }
490
- /**
491
- * Get configuration for authentication.
492
- *
493
- * @returns Authentication configuration from global configuration instance, the value may be undefined if no authentication config exists in current environment.
494
- *
495
- * @throws {@link ErrorCode|InvalidConfiguration} when global configuration does not exist
496
- *
497
- * @beta
498
- */
499
- function getAuthenticationConfiguration() {
500
- internalLogger.info("Get authentication configuration");
501
- if (config) {
502
- return config.authentication;
503
- }
504
- const errorMsg = "Please call loadConfiguration() first before calling getAuthenticationConfiguration().";
505
- internalLogger.error(errorMsg);
506
- throw new ErrorWithCode(formatString(ErrorMessage.ConfigurationNotExists, errorMsg), exports.ErrorCode.InvalidConfiguration);
507
368
  }
508
369
 
509
370
  /**
@@ -557,7 +418,7 @@ function parseCertificate(certificateContent) {
557
418
  * @example
558
419
  * ```typescript
559
420
  * loadConfiguration(); // load configuration from environment variables
560
- * const credential = new M365TenantCredential();
421
+ * const credential = new AppCredential();
561
422
  * ```
562
423
  *
563
424
  * @remarks
@@ -565,21 +426,23 @@ function parseCertificate(certificateContent) {
565
426
  *
566
427
  * @beta
567
428
  */
568
- class M365TenantCredential {
429
+ class AppCredential {
569
430
  /**
570
- * Constructor of M365TenantCredential.
431
+ * Constructor of AppCredential.
571
432
  *
572
433
  * @remarks
573
434
  * Only works in in server side.
574
435
  *
436
+ * @param {AuthenticationConfiguration} authConfig - The authentication configuration. Use environment variables if not provided.
437
+ *
575
438
  * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.
576
439
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
577
440
  *
578
441
  * @beta
579
442
  */
580
- constructor() {
443
+ constructor(authConfig) {
581
444
  internalLogger.info("Create M365 tenant credential");
582
- const config = this.loadAndValidateConfig();
445
+ const config = this.loadAndValidateConfig(authConfig);
583
446
  this.msalClient = createConfidentialClientApplication(config);
584
447
  }
585
448
  /**
@@ -641,15 +504,13 @@ class M365TenantCredential {
641
504
  }
642
505
  /**
643
506
  * Load and validate authentication configuration
507
+ *
508
+ * @param {AuthenticationConfiguration} authConfig - The authentication configuration. Use environment variables if not provided.
509
+ *
644
510
  * @returns Authentication configuration
645
511
  */
646
- loadAndValidateConfig() {
512
+ loadAndValidateConfig(config) {
647
513
  internalLogger.verbose("Validate authentication configuration");
648
- const config = getAuthenticationConfiguration();
649
- if (!config) {
650
- internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);
651
- throw new ErrorWithCode(ErrorMessage.AuthenticationConfigurationNotExists, exports.ErrorCode.InvalidConfiguration);
652
- }
653
514
  if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {
654
515
  return config;
655
516
  }
@@ -675,7 +536,6 @@ class M365TenantCredential {
675
536
  *
676
537
  * @example
677
538
  * ```typescript
678
- * loadConfiguration(); // load configuration from environment variables
679
539
  * const credential = new OnBehalfOfUserCredential(ssoToken);
680
540
  * ```
681
541
  *
@@ -692,6 +552,7 @@ class OnBehalfOfUserCredential {
692
552
  * Only works in in server side.
693
553
  *
694
554
  * @param {string} ssoToken - User token provided by Teams SSO feature.
555
+ * @param {AuthenticationConfiguration} config - The authentication configuration. Use environment variables if not provided.
695
556
  *
696
557
  * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
697
558
  * @throws {@link ErrorCode|InternalError} when SSO token is not valid.
@@ -699,20 +560,19 @@ class OnBehalfOfUserCredential {
699
560
  *
700
561
  * @beta
701
562
  */
702
- constructor(ssoToken) {
703
- var _a, _b, _c, _d, _e;
563
+ constructor(ssoToken, config) {
704
564
  internalLogger.info("Get on behalf of user credential");
705
565
  const missingConfigurations = [];
706
- if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.clientId)) {
566
+ if (!config.clientId) {
707
567
  missingConfigurations.push("clientId");
708
568
  }
709
- if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.authorityHost)) {
569
+ if (!config.authorityHost) {
710
570
  missingConfigurations.push("authorityHost");
711
571
  }
712
- 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)) {
572
+ if (!config.clientSecret && !config.certificateContent) {
713
573
  missingConfigurations.push("clientSecret or certificateContent");
714
574
  }
715
- if (!((_e = config === null || config === void 0 ? void 0 : config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId)) {
575
+ if (!config.tenantId) {
716
576
  missingConfigurations.push("tenantId");
717
577
  }
718
578
  if (missingConfigurations.length != 0) {
@@ -720,7 +580,7 @@ class OnBehalfOfUserCredential {
720
580
  internalLogger.error(errorMsg);
721
581
  throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
722
582
  }
723
- this.msalClient = createConfidentialClientApplication(config.authentication);
583
+ this.msalClient = createConfidentialClientApplication(config);
724
584
  const decodedSsoToken = parseJwt(ssoToken);
725
585
  this.ssoToken = {
726
586
  token: ssoToken,
@@ -856,7 +716,7 @@ class TeamsUserCredential {
856
716
  * Can only be used within Teams.
857
717
  * @beta
858
718
  */
859
- constructor() {
719
+ constructor(authConfig) {
860
720
  throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), exports.ErrorCode.RuntimeNotSupported);
861
721
  }
862
722
  /**
@@ -903,7 +763,7 @@ class MsGraphAuthProvider {
903
763
  /**
904
764
  * Constructor of MsGraphAuthProvider.
905
765
  *
906
- * @param {TokenCredential} credential - Credential used to invoke Microsoft Graph APIs.
766
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth.
907
767
  * @param {string | string[]} scopes - The list of scopes for which the token will have access.
908
768
  *
909
769
  * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
@@ -912,8 +772,8 @@ class MsGraphAuthProvider {
912
772
  *
913
773
  * @beta
914
774
  */
915
- constructor(credential, scopes) {
916
- this.credential = credential;
775
+ constructor(teamsfx, scopes) {
776
+ this.teamsfx = teamsfx;
917
777
  let scopesStr = defaultScope;
918
778
  if (scopes) {
919
779
  validateScopesType(scopes);
@@ -940,7 +800,7 @@ class MsGraphAuthProvider {
940
800
  getAccessToken() {
941
801
  return tslib.__awaiter(this, void 0, void 0, function* () {
942
802
  internalLogger.info(`Get Graph Access token with scopes: '${this.scopes}'`);
943
- const accessToken = yield this.credential.getToken(this.scopes);
803
+ const accessToken = yield this.teamsfx.getCredential().getToken(this.scopes);
944
804
  return new Promise((resolve, reject) => {
945
805
  if (accessToken) {
946
806
  resolve(accessToken.token);
@@ -998,7 +858,7 @@ class MsGraphAuthProvider {
998
858
  * }
999
859
  * ```
1000
860
  *
1001
- * @param {TokenCredential} credential - token credential instance.
861
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth.
1002
862
  * @param scopes - The array of Microsoft Token scope of access. Default value is `[.default]`.
1003
863
  *
1004
864
  * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
@@ -1007,9 +867,9 @@ class MsGraphAuthProvider {
1007
867
  *
1008
868
  * @beta
1009
869
  */
1010
- function createMicrosoftGraphClient(credential, scopes) {
870
+ function createMicrosoftGraphClient(teamsfx, scopes) {
1011
871
  internalLogger.info("Create Microsoft Graph Client");
1012
- const authProvider = new MsGraphAuthProvider(credential, scopes);
872
+ const authProvider = new MsGraphAuthProvider(teamsfx, scopes);
1013
873
  const graphClient = microsoftGraphClient.Client.initWithMiddleware({
1014
874
  authProvider,
1015
875
  });
@@ -1018,176 +878,165 @@ function createMicrosoftGraphClient(credential, scopes) {
1018
878
 
1019
879
  // Copyright (c) Microsoft Corporation.
1020
880
  /**
1021
- * SQL connection configuration instance.
1022
- * @remarks
1023
- * Only works in in server side.
881
+ * MSSQL default scope
882
+ * https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
883
+ */
884
+ const defaultSQLScope = "https://database.windows.net/";
885
+ /**
886
+ * Generate connection configuration consumed by tedious.
1024
887
  *
1025
- * @beta
888
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
889
+ * @param { string? } databaseName - specify database name to override default one if there are multiple databases.
890
+ *
891
+ * @returns Connection configuration of tedious for the SQL.
1026
892
  *
893
+ * @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
894
+ * @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
895
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
896
+ *
897
+ * @beta
1027
898
  */
1028
- class DefaultTediousConnectionConfiguration {
1029
- constructor() {
1030
- /**
1031
- * MSSQL default scope
1032
- * https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
1033
- */
1034
- this.defaultSQLScope = "https://database.windows.net/";
1035
- }
1036
- /**
1037
- * Generate connection configuration consumed by tedious.
1038
- *
1039
- * @param { string? } databaseName - specify database name to override default one if there are multiple databases.
1040
- *
1041
- * @returns Connection configuration of tedious for the SQL.
1042
- *
1043
- * @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
1044
- * @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
1045
- * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1046
- *
1047
- * @beta
1048
- */
1049
- getConfig(databaseName) {
1050
- return tslib.__awaiter(this, void 0, void 0, function* () {
1051
- internalLogger.info("Get SQL configuration");
1052
- const configuration = getResourceConfiguration(exports.ResourceType.SQL);
1053
- if (!configuration) {
1054
- const errMsg = "SQL resource configuration not exist";
1055
- internalLogger.error(errMsg);
1056
- throw new ErrorWithCode(errMsg, exports.ErrorCode.InvalidConfiguration);
1057
- }
1058
- try {
1059
- this.isSQLConfigurationValid(configuration);
1060
- }
1061
- catch (err) {
1062
- throw err;
1063
- }
1064
- if (!this.isMsiAuthentication()) {
1065
- const configWithUPS = this.generateDefaultConfig(configuration, databaseName);
1066
- internalLogger.verbose("SQL configuration with username and password generated");
1067
- return configWithUPS;
1068
- }
1069
- try {
1070
- const configWithToken = yield this.generateTokenConfig(configuration, databaseName);
1071
- internalLogger.verbose("SQL configuration with MSI token generated");
1072
- return configWithToken;
1073
- }
1074
- catch (error) {
1075
- throw error;
1076
- }
1077
- });
1078
- }
1079
- /**
1080
- * Check SQL use MSI identity or username and password.
1081
- *
1082
- * @returns false - login with SQL MSI identity, true - login with username and password.
1083
- * @internal
1084
- */
1085
- isMsiAuthentication() {
1086
- internalLogger.verbose("Check connection config using MSI access token or username and password");
1087
- const configuration = getResourceConfiguration(exports.ResourceType.SQL);
1088
- if ((configuration === null || configuration === void 0 ? void 0 : configuration.sqlUsername) != null && (configuration === null || configuration === void 0 ? void 0 : configuration.sqlPassword) != null) {
1089
- internalLogger.verbose("Login with username and password");
1090
- return false;
899
+ function getTediousConnectionConfig(teamsfx, databaseName) {
900
+ return tslib.__awaiter(this, void 0, void 0, function* () {
901
+ internalLogger.info("Get SQL configuration");
902
+ try {
903
+ isSQLConfigurationValid(teamsfx);
1091
904
  }
1092
- internalLogger.verbose("Login with MSI identity");
1093
- return true;
1094
- }
1095
- /**
1096
- * check configuration is an available configurations.
1097
- * @param { SqlConfiguration } sqlConfig
1098
- *
1099
- * @returns true - SQL configuration has a valid SQL endpoints, SQL username with password or identity ID.
1100
- * false - configuration is not valid.
1101
- * @internal
1102
- */
1103
- isSQLConfigurationValid(sqlConfig) {
1104
- internalLogger.verbose("Check SQL configuration if valid");
1105
- if (!sqlConfig.sqlServerEndpoint) {
1106
- internalLogger.error("SQL configuration is not valid without SQL server endpoint exist");
1107
- throw new ErrorWithCode("SQL configuration error without SQL server endpoint exist", exports.ErrorCode.InvalidConfiguration);
905
+ catch (err) {
906
+ throw err;
1108
907
  }
1109
- if (!(sqlConfig.sqlUsername && sqlConfig.sqlPassword) && !sqlConfig.sqlIdentityId) {
1110
- const errMsg = `SQL configuration is not valid without ${sqlConfig.sqlIdentityId ? "" : "identity id "} ${sqlConfig.sqlUsername ? "" : "SQL username "} ${sqlConfig.sqlPassword ? "" : "SQL password"} exist`;
1111
- internalLogger.error(errMsg);
1112
- throw new ErrorWithCode(errMsg, exports.ErrorCode.InvalidConfiguration);
1113
- }
1114
- internalLogger.verbose("SQL configuration is valid");
1115
- }
1116
- /**
1117
- * Generate tedious connection configuration with default authentication type.
1118
- *
1119
- * @param { SqlConfiguration } SQL configuration with username and password.
1120
- *
1121
- * @returns Tedious connection configuration with username and password.
1122
- * @internal
1123
- */
1124
- generateDefaultConfig(sqlConfig, databaseName) {
1125
908
  if (databaseName === "") {
1126
909
  internalLogger.warn(`SQL database name is empty string`);
1127
910
  }
1128
- const dbName = databaseName !== null && databaseName !== void 0 ? databaseName : sqlConfig.sqlDatabaseName;
1129
- internalLogger.verbose(`SQL server ${sqlConfig.sqlServerEndpoint}, user name ${sqlConfig.sqlUsername}, database name ${dbName}`);
1130
- const config = {
1131
- server: sqlConfig.sqlServerEndpoint,
1132
- authentication: {
1133
- type: TediousAuthenticationType.default,
1134
- options: {
1135
- userName: sqlConfig.sqlUsername,
1136
- password: sqlConfig.sqlPassword,
1137
- },
1138
- },
911
+ const dbName = databaseName !== null && databaseName !== void 0 ? databaseName : (teamsfx.hasConfig("sqlDatabaseName") ? teamsfx.getConfig("sqlDatabaseName") : undefined);
912
+ if (!isMsiAuthentication(teamsfx)) {
913
+ const configWithUPS = generateDefaultConfig(teamsfx, dbName);
914
+ internalLogger.verbose("SQL configuration with username and password generated");
915
+ return configWithUPS;
916
+ }
917
+ try {
918
+ const configWithToken = yield generateTokenConfig(teamsfx, dbName);
919
+ internalLogger.verbose("SQL configuration with MSI token generated");
920
+ return configWithToken;
921
+ }
922
+ catch (error) {
923
+ throw error;
924
+ }
925
+ });
926
+ }
927
+ /**
928
+ * check configuration is an available configurations.
929
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
930
+ *
931
+ * @returns true - SQL configuration has a valid SQL endpoints, SQL username with password or identity ID.
932
+ * false - configuration is not valid.
933
+ * @internal
934
+ */
935
+ function isSQLConfigurationValid(teamsfx) {
936
+ internalLogger.verbose("Check SQL configuration if valid");
937
+ if (!teamsfx.hasConfig("sqlServerEndpoint")) {
938
+ internalLogger.error("SQL configuration is not valid without SQL server endpoint exist");
939
+ throw new ErrorWithCode("SQL configuration error without SQL server endpoint exist", exports.ErrorCode.InvalidConfiguration);
940
+ }
941
+ if (!(teamsfx.hasConfig("sqlUsername") && teamsfx.hasConfig("sqlPassword")) &&
942
+ !teamsfx.hasConfig("sqlIdentityId")) {
943
+ const errMsg = `SQL configuration is not valid without ${teamsfx.hasConfig("sqlIdentityId") ? "" : "identity id "} ${teamsfx.hasConfig("sqlUsername") ? "" : "SQL username "} ${teamsfx.hasConfig("sqlPassword") ? "" : "SQL password"} exist`;
944
+ internalLogger.error(errMsg);
945
+ throw new ErrorWithCode(errMsg, exports.ErrorCode.InvalidConfiguration);
946
+ }
947
+ internalLogger.verbose("SQL configuration is valid");
948
+ }
949
+ /**
950
+ * Check SQL use MSI identity or username and password.
951
+ *
952
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
953
+ *
954
+ * @returns false - login with SQL MSI identity, true - login with username and password.
955
+ * @internal
956
+ */
957
+ function isMsiAuthentication(teamsfx) {
958
+ internalLogger.verbose("Check connection config using MSI access token or username and password");
959
+ if (teamsfx.hasConfig("sqlUsername") && teamsfx.hasConfig("sqlPassword")) {
960
+ internalLogger.verbose("Login with username and password");
961
+ return false;
962
+ }
963
+ internalLogger.verbose("Login with MSI identity");
964
+ return true;
965
+ }
966
+ /**
967
+ * Generate tedious connection configuration with default authentication type.
968
+ *
969
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
970
+ * @param { string? } databaseName - specify database name to override default one if there are multiple databases.
971
+ *
972
+ * @returns Tedious connection configuration with username and password.
973
+ * @internal
974
+ */
975
+ function generateDefaultConfig(teamsfx, databaseName) {
976
+ internalLogger.verbose(`SQL server ${teamsfx.getConfig("sqlServerEndpoint")}
977
+ , user name ${teamsfx.getConfig("sqlUsername")}
978
+ , database name ${databaseName}`);
979
+ const config = {
980
+ server: teamsfx.getConfig("sqlServerEndpoint"),
981
+ authentication: {
982
+ type: TediousAuthenticationType.default,
1139
983
  options: {
1140
- database: dbName,
1141
- encrypt: true,
984
+ userName: teamsfx.getConfig("sqlUsername"),
985
+ password: teamsfx.getConfig("sqlPassword"),
1142
986
  },
1143
- };
1144
- return config;
1145
- }
1146
- /**
1147
- * Generate tedious connection configuration with azure-active-directory-access-token authentication type.
1148
- *
1149
- * @param { SqlConfiguration } SQL configuration with AAD access token.
1150
- *
1151
- * @returns Tedious connection configuration with access token.
1152
- * @internal
1153
- */
1154
- generateTokenConfig(sqlConfig, databaseName) {
1155
- return tslib.__awaiter(this, void 0, void 0, function* () {
1156
- internalLogger.verbose("Generate tedious config with MSI token");
1157
- if (databaseName === "") {
1158
- internalLogger.warn(`SQL database name is empty string`);
1159
- }
1160
- let token;
1161
- try {
1162
- const credential = new identity.ManagedIdentityCredential(sqlConfig.sqlIdentityId);
1163
- token = yield credential.getToken(this.defaultSQLScope);
1164
- }
1165
- catch (error) {
1166
- const errMsg = "Get user MSI token failed";
1167
- internalLogger.error(errMsg);
1168
- throw new ErrorWithCode(errMsg, exports.ErrorCode.InternalError);
1169
- }
1170
- if (token) {
1171
- const config = {
1172
- server: sqlConfig.sqlServerEndpoint,
1173
- authentication: {
1174
- type: TediousAuthenticationType.MSI,
1175
- options: {
1176
- token: token.token,
1177
- },
1178
- },
987
+ },
988
+ options: {
989
+ database: databaseName,
990
+ encrypt: true,
991
+ },
992
+ };
993
+ return config;
994
+ }
995
+ /**
996
+ * Generate tedious connection configuration with azure-active-directory-access-token authentication type.
997
+ *
998
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
999
+ *
1000
+ * @returns Tedious connection configuration with access token.
1001
+ * @internal
1002
+ */
1003
+ function generateTokenConfig(teamsfx, databaseName) {
1004
+ return tslib.__awaiter(this, void 0, void 0, function* () {
1005
+ internalLogger.verbose("Generate tedious config with MSI token");
1006
+ let token;
1007
+ try {
1008
+ const credential = new identity.ManagedIdentityCredential(teamsfx.getConfig("sqlIdentityId"));
1009
+ token = yield credential.getToken(defaultSQLScope);
1010
+ }
1011
+ catch (error) {
1012
+ const errMsg = "Get user MSI token failed";
1013
+ internalLogger.error(errMsg);
1014
+ throw new ErrorWithCode(errMsg, exports.ErrorCode.InternalError);
1015
+ }
1016
+ if (token) {
1017
+ const config = {
1018
+ server: teamsfx.getConfig("sqlServerEndpoint"),
1019
+ authentication: {
1020
+ type: TediousAuthenticationType.MSI,
1179
1021
  options: {
1180
- database: databaseName !== null && databaseName !== void 0 ? databaseName : sqlConfig.sqlDatabaseName,
1181
- encrypt: true,
1022
+ token: token.token,
1182
1023
  },
1183
- };
1184
- internalLogger.verbose(`Generate token configuration success, server endpoint is ${sqlConfig.sqlServerEndpoint}, database name is ${databaseName !== null && databaseName !== void 0 ? databaseName : sqlConfig.sqlDatabaseName}`);
1185
- return config;
1186
- }
1187
- internalLogger.error(`Generate token configuration, server endpoint is ${sqlConfig.sqlServerEndpoint}, MSI token is not valid`);
1188
- throw new ErrorWithCode("MSI token is not valid", exports.ErrorCode.InternalError);
1189
- });
1190
- }
1024
+ },
1025
+ options: {
1026
+ database: databaseName,
1027
+ encrypt: true,
1028
+ },
1029
+ };
1030
+ internalLogger.verbose(`Generate token configuration success
1031
+ , server endpoint is ${teamsfx.getConfig("sqlServerEndpoint")}
1032
+ , database name is ${databaseName}`);
1033
+ return config;
1034
+ }
1035
+ internalLogger.error(`Generate token configuration
1036
+ , server endpoint is ${teamsfx.getConfig("sqlServerEndpoint")}
1037
+ , MSI token is not valid`);
1038
+ throw new ErrorWithCode("MSI token is not valid", exports.ErrorCode.InternalError);
1039
+ });
1191
1040
  }
1192
1041
  /**
1193
1042
  * tedious connection config authentication type.
@@ -1200,6 +1049,25 @@ var TediousAuthenticationType;
1200
1049
  TediousAuthenticationType["MSI"] = "azure-active-directory-access-token";
1201
1050
  })(TediousAuthenticationType || (TediousAuthenticationType = {}));
1202
1051
 
1052
+ // Copyright (c) Microsoft Corporation.
1053
+ // Licensed under the MIT license.
1054
+ /**
1055
+ * Identity type to use in authentication.
1056
+ *
1057
+ * @beta
1058
+ */
1059
+ exports.IdentityType = void 0;
1060
+ (function (IdentityType) {
1061
+ /**
1062
+ * Represents the current user of Teams.
1063
+ */
1064
+ IdentityType["User"] = "User";
1065
+ /**
1066
+ * Represents the application itself.
1067
+ */
1068
+ IdentityType["App"] = "Application";
1069
+ })(exports.IdentityType || (exports.IdentityType = {}));
1070
+
1203
1071
  // Copyright (c) Microsoft Corporation.
1204
1072
  const invokeResponseType = "invokeResponse";
1205
1073
  /**
@@ -1239,7 +1107,6 @@ class TokenExchangeInvokeResponse {
1239
1107
  * const dialogState = convoState.createProperty('dialogState');
1240
1108
  * const dialogs = new DialogSet(dialogState);
1241
1109
  *
1242
- * loadConfiguration();
1243
1110
  * dialogs.add(new TeamsBotSsoPrompt('TeamsBotSsoPrompt', {
1244
1111
  * scopes: ["User.Read"],
1245
1112
  * }));
@@ -1268,6 +1135,7 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
1268
1135
  /**
1269
1136
  * Constructor of TeamsBotSsoPrompt.
1270
1137
  *
1138
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
1271
1139
  * @param dialogId Unique ID of the dialog within its parent `DialogSet` or `ComponentDialog`.
1272
1140
  * @param settings Settings used to configure the prompt.
1273
1141
  *
@@ -1276,10 +1144,12 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
1276
1144
  *
1277
1145
  * @beta
1278
1146
  */
1279
- constructor(dialogId, settings) {
1147
+ constructor(teamsfx, dialogId, settings) {
1280
1148
  super(dialogId);
1149
+ this.teamsfx = teamsfx;
1281
1150
  this.settings = settings;
1282
1151
  validateScopesType(settings.scopes);
1152
+ this.loadAndValidateConfig();
1283
1153
  internalLogger.info("Create a new Teams Bot SSO Prompt");
1284
1154
  }
1285
1155
  /**
@@ -1384,6 +1254,31 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
1384
1254
  }
1385
1255
  });
1386
1256
  }
1257
+ loadAndValidateConfig() {
1258
+ if (this.teamsfx.getIdentityType() !== exports.IdentityType.User) {
1259
+ const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.teamsfx.getIdentityType().toString(), "TeamsBotSsoPrompt");
1260
+ internalLogger.error(errorMsg);
1261
+ throw new ErrorWithCode(errorMsg, exports.ErrorCode.IdentityTypeNotSupported);
1262
+ }
1263
+ const missingConfigurations = [];
1264
+ if (!this.teamsfx.hasConfig("initiateLoginEndpoint")) {
1265
+ missingConfigurations.push("initiateLoginEndpoint");
1266
+ }
1267
+ if (!this.teamsfx.hasConfig("clientId")) {
1268
+ missingConfigurations.push("clientId");
1269
+ }
1270
+ if (!this.teamsfx.hasConfig("tenantId")) {
1271
+ missingConfigurations.push("tenantId");
1272
+ }
1273
+ if (!this.teamsfx.hasConfig("applicationIdUri")) {
1274
+ missingConfigurations.push("applicationIdUri");
1275
+ }
1276
+ if (missingConfigurations.length != 0) {
1277
+ const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
1278
+ internalLogger.error(errorMsg);
1279
+ throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
1280
+ }
1281
+ }
1387
1282
  /**
1388
1283
  * Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
1389
1284
  * @param dc dialog context
@@ -1425,31 +1320,12 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
1425
1320
  * @internal
1426
1321
  */
1427
1322
  getSignInResource(loginHint) {
1428
- var _a, _b, _c, _d, _e;
1429
1323
  internalLogger.verbose("Get sign in authentication configuration");
1430
- const missingConfigurations = [];
1431
- if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.initiateLoginEndpoint)) {
1432
- missingConfigurations.push("initiateLoginEndpoint");
1433
- }
1434
- if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.clientId)) {
1435
- missingConfigurations.push("clientId");
1436
- }
1437
- if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.tenantId)) {
1438
- missingConfigurations.push("tenantId");
1439
- }
1440
- if (!((_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.applicationIdUri)) {
1441
- missingConfigurations.push("applicationIdUri");
1442
- }
1443
- if (missingConfigurations.length != 0) {
1444
- const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
1445
- internalLogger.error(errorMsg);
1446
- throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidConfiguration);
1447
- }
1448
- const signInLink = `${config.authentication.initiateLoginEndpoint}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${config.authentication.clientId}&tenantId=${config.authentication.tenantId}&loginHint=${loginHint}`;
1324
+ const signInLink = `${this.teamsfx.getConfig("initiateLoginEndpoint")}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${this.teamsfx.getConfig("clientId")}&tenantId=${this.teamsfx.getConfig("tenantId")}&loginHint=${loginHint}`;
1449
1325
  internalLogger.verbose("Sign in link: " + signInLink);
1450
1326
  const tokenExchangeResource = {
1451
1327
  id: uuid.v4(),
1452
- uri: ((_e = config.authentication) === null || _e === void 0 ? void 0 : _e.applicationIdUri.replace(/\/$/, "")) + "/access_as_user",
1328
+ uri: this.teamsfx.getConfig("applicationIdUri").replace(/\/$/, "") + "/access_as_user",
1453
1329
  };
1454
1330
  internalLogger.verbose("Token exchange resource uri: " + tokenExchangeResource.uri);
1455
1331
  return {
@@ -1474,7 +1350,8 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
1474
1350
  }
1475
1351
  else {
1476
1352
  const ssoToken = context.activity.value.token;
1477
- const credential = new OnBehalfOfUserCredential(ssoToken);
1353
+ this.teamsfx.setSsoToken(ssoToken);
1354
+ const credential = this.teamsfx.getCredential();
1478
1355
  let exchangedToken;
1479
1356
  try {
1480
1357
  exchangedToken = yield credential.getToken(this.settings.scopes);
@@ -1539,18 +1416,202 @@ class TeamsBotSsoPrompt extends botbuilderDialogs.Dialog {
1539
1416
  }
1540
1417
  }
1541
1418
 
1542
- exports.DefaultTediousConnectionConfiguration = DefaultTediousConnectionConfiguration;
1419
+ // Copyright (c) Microsoft Corporation.
1420
+ /**
1421
+ * A class providing credential and configuration.
1422
+ * @beta
1423
+ */
1424
+ class TeamsFx {
1425
+ /**
1426
+ * Constructor of TeamsFx
1427
+ *
1428
+ * @param {IdentityType} identityType - Choose user or app identity
1429
+ * @param customConfig - key/value pairs of customized configuration that overrides default ones.
1430
+ *
1431
+ * @throws {@link ErrorCode|IdentityTypeNotSupported} when setting app identity in browser.
1432
+ *
1433
+ * @beta
1434
+ */
1435
+ constructor(identityType, customConfig) {
1436
+ this.identityType = identityType !== null && identityType !== void 0 ? identityType : exports.IdentityType.User;
1437
+ this.configuration = new Map();
1438
+ this.loadFromEnv();
1439
+ if (customConfig) {
1440
+ for (const key of Object.keys(customConfig)) {
1441
+ const value = customConfig[key];
1442
+ if (value) {
1443
+ this.configuration.set(key, value);
1444
+ }
1445
+ }
1446
+ }
1447
+ }
1448
+ /**
1449
+ * Identity type set by user.
1450
+ *
1451
+ * @returns identity type.
1452
+ * @beta
1453
+ */
1454
+ getIdentityType() {
1455
+ return this.identityType;
1456
+ }
1457
+ /**
1458
+ * Credential instance according to identity type choice.
1459
+ *
1460
+ * @remarks If user identity is chose, will return {@link TeamsUserCredential}
1461
+ * in browser environment and {@link OnBehalfOfUserCredential} in NodeJS. If app
1462
+ * identity is chose, will return {@link AppCredential}.
1463
+ *
1464
+ * @returns instance implements TokenCredential interface.
1465
+ * @beta
1466
+ */
1467
+ getCredential() {
1468
+ if (this.identityType === exports.IdentityType.User) {
1469
+ if (this.oboUserCredential) {
1470
+ return this.oboUserCredential;
1471
+ }
1472
+ const errorMsg = "SSO token is required to user identity. Please use setSsoToken().";
1473
+ internalLogger.error(errorMsg);
1474
+ throw new ErrorWithCode(errorMsg, exports.ErrorCode.InvalidParameter);
1475
+ }
1476
+ else {
1477
+ if (!this.appCredential) {
1478
+ this.appCredential = new AppCredential(Object.fromEntries(this.configuration));
1479
+ }
1480
+ return this.appCredential;
1481
+ }
1482
+ }
1483
+ /**
1484
+ * Get user information.
1485
+ * @returns UserInfo object.
1486
+ * @beta
1487
+ */
1488
+ getUserInfo() {
1489
+ return tslib.__awaiter(this, void 0, void 0, function* () {
1490
+ if (this.identityType !== exports.IdentityType.User) {
1491
+ const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.identityType.toString(), "TeamsFx");
1492
+ internalLogger.error(errorMsg);
1493
+ throw new ErrorWithCode(errorMsg, exports.ErrorCode.IdentityTypeNotSupported);
1494
+ }
1495
+ return Promise.resolve(this.getCredential().getUserInfo());
1496
+ });
1497
+ }
1498
+ /**
1499
+ * Popup login page to get user's access token with specific scopes.
1500
+ *
1501
+ * @remarks
1502
+ * Only works in Teams client APP. User will be redirected to the authorization page to login and consent.
1503
+ *
1504
+ * @example
1505
+ * ```typescript
1506
+ * await teamsfx.login(["https://graph.microsoft.com/User.Read"]); // single scope using string array
1507
+ * await teamsfx.login("https://graph.microsoft.com/User.Read"); // single scopes using string
1508
+ * await teamsfx.login(["https://graph.microsoft.com/User.Read", "Calendars.Read"]); // multiple scopes using string array
1509
+ * await teamsfx.login("https://graph.microsoft.com/User.Read Calendars.Read"); // multiple scopes using string
1510
+ * ```
1511
+ * @param scopes - The list of scopes for which the token will have access, before that, we will request user to consent.
1512
+ *
1513
+ * @throws {@link ErrorCode|InternalError} when failed to login with unknown error.
1514
+ * @throws {@link ErrorCode|ConsentFailed} when user canceled or failed to consent.
1515
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
1516
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
1517
+ *
1518
+ * @beta
1519
+ */
1520
+ login(scopes) {
1521
+ return tslib.__awaiter(this, void 0, void 0, function* () {
1522
+ throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "login"), exports.ErrorCode.RuntimeNotSupported);
1523
+ });
1524
+ }
1525
+ /**
1526
+ * Set SSO token when using user identity in NodeJS.
1527
+ * @param {string} ssoToken - used for on behalf of user flow.
1528
+ * @returns self instance.
1529
+ * @beta
1530
+ */
1531
+ setSsoToken(ssoToken) {
1532
+ if (this.identityType !== exports.IdentityType.User) {
1533
+ throw new Error();
1534
+ }
1535
+ this.oboUserCredential = new OnBehalfOfUserCredential(ssoToken, Object.fromEntries(this.configuration));
1536
+ return this;
1537
+ }
1538
+ /**
1539
+ * Usually used by service plugins to retrieve specific config
1540
+ * @param {string} key - configuration key.
1541
+ * @returns value in configuration.
1542
+ * @beta
1543
+ */
1544
+ getConfig(key) {
1545
+ const value = this.configuration.get(key);
1546
+ if (!value) {
1547
+ const errorMsg = `Cannot find ${key} in configuration`;
1548
+ internalLogger.error(errorMsg);
1549
+ throw new ErrorWithCode(errorMsg, exports.ErrorCode.InternalError);
1550
+ }
1551
+ return value;
1552
+ }
1553
+ /**
1554
+ * Check the value of specific key.
1555
+ * @param {string} key - configuration key.
1556
+ * @returns true if corresponding value is not empty string.
1557
+ * @beta
1558
+ */
1559
+ hasConfig(key) {
1560
+ const value = this.configuration.get(key);
1561
+ return !!value;
1562
+ }
1563
+ /**
1564
+ * Get all configurations.
1565
+ * @returns key value mappings.
1566
+ * @beta
1567
+ */
1568
+ getConfigs() {
1569
+ const config = {};
1570
+ for (const key of this.configuration.keys()) {
1571
+ const value = this.configuration.get(key);
1572
+ if (value) {
1573
+ config[key] = value;
1574
+ }
1575
+ }
1576
+ return config;
1577
+ }
1578
+ /**
1579
+ * Load configuration from environment variables.
1580
+ */
1581
+ loadFromEnv() {
1582
+ const env = process.env;
1583
+ this.configuration.set("authorityHost", env.M365_AUTHORITY_HOST);
1584
+ this.configuration.set("tenantId", env.M365_TENANT_ID);
1585
+ this.configuration.set("clientId", env.M365_CLIENT_ID);
1586
+ this.configuration.set("clientSecret", env.M365_CLIENT_SECRET);
1587
+ this.configuration.set("initiateLoginEndpoint", env.INITIATE_LOGIN_ENDPOINT);
1588
+ this.configuration.set("applicationIdUri", env.M365_APPLICATION_ID_URI);
1589
+ this.configuration.set("apiEndpoint", env.API_ENDPOINT);
1590
+ this.configuration.set("apiName", env.API_NAME);
1591
+ this.configuration.set("sqlServerEndpoint", env.SQL_ENDPOINT);
1592
+ this.configuration.set("sqlUsername", env.SQL_USER_NAME);
1593
+ this.configuration.set("sqlPassword", env.SQL_PASSWORD);
1594
+ this.configuration.set("sqlDatabaseName", env.SQL_DATABASE_NAME);
1595
+ this.configuration.set("sqlIdentityId", env.IDENTITY_ID);
1596
+ Object.keys(env).forEach((key) => {
1597
+ const value = env[key];
1598
+ if (key.startsWith("TEAMSFX_") && value) {
1599
+ this.configuration.set(key.substring(8), value);
1600
+ }
1601
+ });
1602
+ }
1603
+ }
1604
+
1605
+ exports.AppCredential = AppCredential;
1543
1606
  exports.ErrorWithCode = ErrorWithCode;
1544
- exports.M365TenantCredential = M365TenantCredential;
1545
1607
  exports.MsGraphAuthProvider = MsGraphAuthProvider;
1546
1608
  exports.OnBehalfOfUserCredential = OnBehalfOfUserCredential;
1547
1609
  exports.TeamsBotSsoPrompt = TeamsBotSsoPrompt;
1610
+ exports.TeamsFx = TeamsFx;
1548
1611
  exports.TeamsUserCredential = TeamsUserCredential;
1549
1612
  exports.createMicrosoftGraphClient = createMicrosoftGraphClient;
1550
- exports.getAuthenticationConfiguration = getAuthenticationConfiguration;
1551
1613
  exports.getLogLevel = getLogLevel;
1552
- exports.getResourceConfiguration = getResourceConfiguration;
1553
- exports.loadConfiguration = loadConfiguration;
1614
+ exports.getTediousConnectionConfig = getTediousConnectionConfig;
1554
1615
  exports.setLogFunction = setLogFunction;
1555
1616
  exports.setLogLevel = setLogLevel;
1556
1617
  exports.setLogger = setLogger;