@microsoft/teamsfx 0.5.1-rc.0 → 0.5.2-alpha.1c67600f6.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.
@@ -63,6 +63,10 @@ var ErrorCode;
63
63
  * Invalid response error.
64
64
  */
65
65
  ErrorCode["InvalidResponse"] = "InvalidResponse";
66
+ /**
67
+ * Identity type error.
68
+ */
69
+ ErrorCode["IdentityTypeNotSupported"] = "IdentityTypeNotSupported";
66
70
  })(ErrorCode || (ErrorCode = {}));
67
71
  /**
68
72
  * @internal
@@ -82,6 +86,8 @@ ErrorMessage.NodejsRuntimeNotSupported = "{0} is not supported in Node.";
82
86
  ErrorMessage.FailToAcquireTokenOnBehalfOfUser = "Failed to acquire access token on behalf of user: {0}";
83
87
  // ChannelNotSupported Error
84
88
  ErrorMessage.OnlyMSTeamsChannelSupported = "{0} is only supported in MS Teams Channel";
89
+ // IdentityTypeNotSupported Error
90
+ ErrorMessage.IdentityTypeNotSupported = "{0} identity is not supported in {1}";
85
91
  /**
86
92
  * Error class with code and message thrown by the SDK.
87
93
  *
@@ -108,26 +114,6 @@ class ErrorWithCode extends Error {
108
114
  }
109
115
  }
110
116
 
111
- // Copyright (c) Microsoft Corporation.
112
- // Licensed under the MIT license.
113
- /**
114
- * Available resource type.
115
- * @beta
116
- */
117
- var ResourceType;
118
- (function (ResourceType) {
119
- /**
120
- * SQL database.
121
- *
122
- */
123
- ResourceType[ResourceType["SQL"] = 0] = "SQL";
124
- /**
125
- * Rest API.
126
- *
127
- */
128
- ResourceType[ResourceType["API"] = 1] = "API";
129
- })(ResourceType || (ResourceType = {}));
130
-
131
117
  // Copyright (c) Microsoft Corporation.
132
118
  // Licensed under the MIT license.
133
119
  /**
@@ -370,131 +356,6 @@ function getScopesArray(scopes) {
370
356
  function getAuthority(authorityHost, tenantId) {
371
357
  const normalizedAuthorityHost = authorityHost.replace(/\/+$/g, "");
372
358
  return normalizedAuthorityHost + "/" + tenantId;
373
- }
374
- /**
375
- * @internal
376
- */
377
- const isNode = typeof process !== "undefined" &&
378
- !!process.version &&
379
- !!process.versions &&
380
- !!process.versions.node;
381
-
382
- // Copyright (c) Microsoft Corporation.
383
- /**
384
- * Global configuration instance
385
- *
386
- */
387
- let config;
388
- /**
389
- * Initialize configuration from environment variables or configuration object and set the global instance
390
- *
391
- * @param {Configuration} configuration - Optional configuration that overrides the default configuration values. The override depth is 1.
392
- *
393
- * @throws {@link ErrorCode|InvalidParameter} when configuration is not passed in browser environment
394
- *
395
- * @beta
396
- */
397
- function loadConfiguration(configuration) {
398
- internalLogger.info("load configuration");
399
- // browser environment
400
- if (!isNode) {
401
- if (!configuration) {
402
- const errorMsg = "You are running the code in browser. Configuration must be passed in.";
403
- internalLogger.error(errorMsg);
404
- throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
405
- }
406
- config = configuration;
407
- return;
408
- }
409
- // node environment
410
- let newAuthentication;
411
- let newResources = [];
412
- const defaultResourceName = "default";
413
- if (configuration === null || configuration === void 0 ? void 0 : configuration.authentication) {
414
- newAuthentication = configuration.authentication;
415
- }
416
- else {
417
- newAuthentication = {
418
- authorityHost: process.env.M365_AUTHORITY_HOST,
419
- tenantId: process.env.M365_TENANT_ID,
420
- clientId: process.env.M365_CLIENT_ID,
421
- clientSecret: process.env.M365_CLIENT_SECRET,
422
- simpleAuthEndpoint: process.env.SIMPLE_AUTH_ENDPOINT,
423
- initiateLoginEndpoint: process.env.INITIATE_LOGIN_ENDPOINT,
424
- applicationIdUri: process.env.M365_APPLICATION_ID_URI,
425
- };
426
- }
427
- if (configuration === null || configuration === void 0 ? void 0 : configuration.resources) {
428
- newResources = configuration.resources;
429
- }
430
- else {
431
- newResources = [
432
- {
433
- // SQL resource
434
- type: ResourceType.SQL,
435
- name: defaultResourceName,
436
- properties: {
437
- sqlServerEndpoint: process.env.SQL_ENDPOINT,
438
- sqlUsername: process.env.SQL_USER_NAME,
439
- sqlPassword: process.env.SQL_PASSWORD,
440
- sqlDatabaseName: process.env.SQL_DATABASE_NAME,
441
- sqlIdentityId: process.env.IDENTITY_ID,
442
- },
443
- },
444
- {
445
- // API resource
446
- type: ResourceType.API,
447
- name: defaultResourceName,
448
- properties: {
449
- endpoint: process.env.API_ENDPOINT,
450
- },
451
- },
452
- ];
453
- }
454
- config = {
455
- authentication: newAuthentication,
456
- resources: newResources,
457
- };
458
- }
459
- /**
460
- * Get configuration for a specific resource.
461
- * @param {ResourceType} resourceType - The type of resource
462
- * @param {string} resourceName - The name of resource, default value is "default".
463
- *
464
- * @returns Resource configuration for target resource from global configuration instance.
465
- *
466
- * @throws {@link ErrorCode|InvalidConfiguration} when resource configuration with the specific type and name is not found
467
- *
468
- * @beta
469
- */
470
- function getResourceConfiguration(resourceType, resourceName = "default") {
471
- var _a;
472
- internalLogger.info(`Get resource configuration of ${ResourceType[resourceType]} from ${resourceName}`);
473
- const result = (_a = config.resources) === null || _a === void 0 ? void 0 : _a.find((item) => item.type === resourceType && item.name === resourceName);
474
- if (result) {
475
- return result.properties;
476
- }
477
- const errorMsg = formatString(ErrorMessage.MissingResourceConfiguration, ResourceType[resourceType], resourceName);
478
- internalLogger.error(errorMsg);
479
- throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
480
- }
481
- /**
482
- * Get configuration for authentication.
483
- *
484
- * @returns Authentication configuration from global configuration instance, the value may be undefined if no authentication config exists in current environment.
485
- *
486
- * @throws {@link ErrorCode|InvalidConfiguration} when global configuration does not exist
487
- *
488
- * @beta
489
- */
490
- function getAuthenticationConfiguration() {
491
- internalLogger.info("Get authentication configuration");
492
- if (config) {
493
- return config.authentication;
494
- }
495
- const errorMsg = "Please call loadConfiguration() first before calling getAuthenticationConfiguration().";
496
- internalLogger.error(errorMsg);
497
- throw new ErrorWithCode(formatString(ErrorMessage.ConfigurationNotExists, errorMsg), ErrorCode.InvalidConfiguration);
498
359
  }
499
360
 
500
361
  /**
@@ -548,7 +409,7 @@ function parseCertificate(certificateContent) {
548
409
  * @example
549
410
  * ```typescript
550
411
  * loadConfiguration(); // load configuration from environment variables
551
- * const credential = new M365TenantCredential();
412
+ * const credential = new AppCredential();
552
413
  * ```
553
414
  *
554
415
  * @remarks
@@ -556,21 +417,23 @@ function parseCertificate(certificateContent) {
556
417
  *
557
418
  * @beta
558
419
  */
559
- class M365TenantCredential {
420
+ class AppCredential {
560
421
  /**
561
- * Constructor of M365TenantCredential.
422
+ * Constructor of AppCredential.
562
423
  *
563
424
  * @remarks
564
425
  * Only works in in server side.
565
426
  *
427
+ * @param {AuthenticationConfiguration} authConfig - The authentication configuration. Use environment variables if not provided.
428
+ *
566
429
  * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.
567
430
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
568
431
  *
569
432
  * @beta
570
433
  */
571
- constructor() {
434
+ constructor(authConfig) {
572
435
  internalLogger.info("Create M365 tenant credential");
573
- const config = this.loadAndValidateConfig();
436
+ const config = this.loadAndValidateConfig(authConfig);
574
437
  this.msalClient = createConfidentialClientApplication(config);
575
438
  }
576
439
  /**
@@ -630,15 +493,13 @@ class M365TenantCredential {
630
493
  }
631
494
  /**
632
495
  * Load and validate authentication configuration
496
+ *
497
+ * @param {AuthenticationConfiguration} authConfig - The authentication configuration. Use environment variables if not provided.
498
+ *
633
499
  * @returns Authentication configuration
634
500
  */
635
- loadAndValidateConfig() {
501
+ loadAndValidateConfig(config) {
636
502
  internalLogger.verbose("Validate authentication configuration");
637
- const config = getAuthenticationConfiguration();
638
- if (!config) {
639
- internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);
640
- throw new ErrorWithCode(ErrorMessage.AuthenticationConfigurationNotExists, ErrorCode.InvalidConfiguration);
641
- }
642
503
  if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {
643
504
  return config;
644
505
  }
@@ -664,7 +525,6 @@ class M365TenantCredential {
664
525
  *
665
526
  * @example
666
527
  * ```typescript
667
- * loadConfiguration(); // load configuration from environment variables
668
528
  * const credential = new OnBehalfOfUserCredential(ssoToken);
669
529
  * ```
670
530
  *
@@ -681,6 +541,7 @@ class OnBehalfOfUserCredential {
681
541
  * Only works in in server side.
682
542
  *
683
543
  * @param {string} ssoToken - User token provided by Teams SSO feature.
544
+ * @param {AuthenticationConfiguration} config - The authentication configuration. Use environment variables if not provided.
684
545
  *
685
546
  * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
686
547
  * @throws {@link ErrorCode|InternalError} when SSO token is not valid.
@@ -688,20 +549,19 @@ class OnBehalfOfUserCredential {
688
549
  *
689
550
  * @beta
690
551
  */
691
- constructor(ssoToken) {
692
- var _a, _b, _c, _d, _e;
552
+ constructor(ssoToken, config) {
693
553
  internalLogger.info("Get on behalf of user credential");
694
554
  const missingConfigurations = [];
695
- if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.clientId)) {
555
+ if (!config.clientId) {
696
556
  missingConfigurations.push("clientId");
697
557
  }
698
- if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.authorityHost)) {
558
+ if (!config.authorityHost) {
699
559
  missingConfigurations.push("authorityHost");
700
560
  }
701
- 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)) {
561
+ if (!config.clientSecret && !config.certificateContent) {
702
562
  missingConfigurations.push("clientSecret or certificateContent");
703
563
  }
704
- if (!((_e = config === null || config === void 0 ? void 0 : config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId)) {
564
+ if (!config.tenantId) {
705
565
  missingConfigurations.push("tenantId");
706
566
  }
707
567
  if (missingConfigurations.length != 0) {
@@ -709,7 +569,7 @@ class OnBehalfOfUserCredential {
709
569
  internalLogger.error(errorMsg);
710
570
  throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
711
571
  }
712
- this.msalClient = createConfidentialClientApplication(config.authentication);
572
+ this.msalClient = createConfidentialClientApplication(config);
713
573
  const decodedSsoToken = parseJwt(ssoToken);
714
574
  this.ssoToken = {
715
575
  token: ssoToken,
@@ -843,7 +703,7 @@ class TeamsUserCredential {
843
703
  * Can only be used within Teams.
844
704
  * @beta
845
705
  */
846
- constructor() {
706
+ constructor(authConfig) {
847
707
  throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
848
708
  }
849
709
  /**
@@ -886,7 +746,7 @@ class MsGraphAuthProvider {
886
746
  /**
887
747
  * Constructor of MsGraphAuthProvider.
888
748
  *
889
- * @param {TokenCredential} credential - Credential used to invoke Microsoft Graph APIs.
749
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth.
890
750
  * @param {string | string[]} scopes - The list of scopes for which the token will have access.
891
751
  *
892
752
  * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
@@ -895,8 +755,8 @@ class MsGraphAuthProvider {
895
755
  *
896
756
  * @beta
897
757
  */
898
- constructor(credential, scopes) {
899
- this.credential = credential;
758
+ constructor(teamsfx, scopes) {
759
+ this.teamsfx = teamsfx;
900
760
  let scopesStr = defaultScope;
901
761
  if (scopes) {
902
762
  validateScopesType(scopes);
@@ -922,7 +782,7 @@ class MsGraphAuthProvider {
922
782
  */
923
783
  async getAccessToken() {
924
784
  internalLogger.info(`Get Graph Access token with scopes: '${this.scopes}'`);
925
- const accessToken = await this.credential.getToken(this.scopes);
785
+ const accessToken = await this.teamsfx.getCredential().getToken(this.scopes);
926
786
  return new Promise((resolve, reject) => {
927
787
  if (accessToken) {
928
788
  resolve(accessToken.token);
@@ -979,7 +839,7 @@ class MsGraphAuthProvider {
979
839
  * }
980
840
  * ```
981
841
  *
982
- * @param {TokenCredential} credential - token credential instance.
842
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth.
983
843
  * @param scopes - The array of Microsoft Token scope of access. Default value is `[.default]`.
984
844
  *
985
845
  * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
@@ -988,9 +848,9 @@ class MsGraphAuthProvider {
988
848
  *
989
849
  * @beta
990
850
  */
991
- function createMicrosoftGraphClient(credential, scopes) {
851
+ function createMicrosoftGraphClient(teamsfx, scopes) {
992
852
  internalLogger.info("Create Microsoft Graph Client");
993
- const authProvider = new MsGraphAuthProvider(credential, scopes);
853
+ const authProvider = new MsGraphAuthProvider(teamsfx, scopes);
994
854
  const graphClient = Client.initWithMiddleware({
995
855
  authProvider,
996
856
  });
@@ -999,172 +859,161 @@ function createMicrosoftGraphClient(credential, scopes) {
999
859
 
1000
860
  // Copyright (c) Microsoft Corporation.
1001
861
  /**
1002
- * SQL connection configuration instance.
1003
- * @remarks
1004
- * Only works in in server side.
862
+ * MSSQL default scope
863
+ * https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
864
+ */
865
+ const defaultSQLScope = "https://database.windows.net/";
866
+ /**
867
+ * Generate connection configuration consumed by tedious.
868
+ *
869
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
870
+ * @param { string? } databaseName - specify database name to override default one if there are multiple databases.
871
+ *
872
+ * @returns Connection configuration of tedious for the SQL.
873
+ *
874
+ * @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
875
+ * @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
876
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1005
877
  *
1006
878
  * @beta
879
+ */
880
+ async function getTediousConnectionConfig(teamsfx, databaseName) {
881
+ internalLogger.info("Get SQL configuration");
882
+ try {
883
+ isSQLConfigurationValid(teamsfx);
884
+ }
885
+ catch (err) {
886
+ throw err;
887
+ }
888
+ if (databaseName === "") {
889
+ internalLogger.warn(`SQL database name is empty string`);
890
+ }
891
+ const dbName = databaseName !== null && databaseName !== void 0 ? databaseName : (teamsfx.hasConfig("sqlDatabaseName") ? teamsfx.getConfig("sqlDatabaseName") : undefined);
892
+ if (!isMsiAuthentication(teamsfx)) {
893
+ const configWithUPS = generateDefaultConfig(teamsfx, dbName);
894
+ internalLogger.verbose("SQL configuration with username and password generated");
895
+ return configWithUPS;
896
+ }
897
+ try {
898
+ const configWithToken = await generateTokenConfig(teamsfx, dbName);
899
+ internalLogger.verbose("SQL configuration with MSI token generated");
900
+ return configWithToken;
901
+ }
902
+ catch (error) {
903
+ throw error;
904
+ }
905
+ }
906
+ /**
907
+ * check configuration is an available configurations.
908
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
1007
909
  *
910
+ * @returns true - SQL configuration has a valid SQL endpoints, SQL username with password or identity ID.
911
+ * false - configuration is not valid.
912
+ * @internal
1008
913
  */
1009
- class DefaultTediousConnectionConfiguration {
1010
- constructor() {
1011
- /**
1012
- * MSSQL default scope
1013
- * https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
1014
- */
1015
- this.defaultSQLScope = "https://database.windows.net/";
914
+ function isSQLConfigurationValid(teamsfx) {
915
+ internalLogger.verbose("Check SQL configuration if valid");
916
+ if (!teamsfx.hasConfig("sqlServerEndpoint")) {
917
+ internalLogger.error("SQL configuration is not valid without SQL server endpoint exist");
918
+ throw new ErrorWithCode("SQL configuration error without SQL server endpoint exist", ErrorCode.InvalidConfiguration);
1016
919
  }
1017
- /**
1018
- * Generate connection configuration consumed by tedious.
1019
- *
1020
- * @param { string? } databaseName - specify database name to override default one if there are multiple databases.
1021
- *
1022
- * @returns Connection configuration of tedious for the SQL.
1023
- *
1024
- * @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
1025
- * @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
1026
- * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1027
- *
1028
- * @beta
1029
- */
1030
- async getConfig(databaseName) {
1031
- internalLogger.info("Get SQL configuration");
1032
- const configuration = getResourceConfiguration(ResourceType.SQL);
1033
- if (!configuration) {
1034
- const errMsg = "SQL resource configuration not exist";
1035
- internalLogger.error(errMsg);
1036
- throw new ErrorWithCode(errMsg, ErrorCode.InvalidConfiguration);
1037
- }
1038
- try {
1039
- this.isSQLConfigurationValid(configuration);
1040
- }
1041
- catch (err) {
1042
- throw err;
1043
- }
1044
- if (!this.isMsiAuthentication()) {
1045
- const configWithUPS = this.generateDefaultConfig(configuration, databaseName);
1046
- internalLogger.verbose("SQL configuration with username and password generated");
1047
- return configWithUPS;
1048
- }
1049
- try {
1050
- const configWithToken = await this.generateTokenConfig(configuration, databaseName);
1051
- internalLogger.verbose("SQL configuration with MSI token generated");
1052
- return configWithToken;
1053
- }
1054
- catch (error) {
1055
- throw error;
1056
- }
920
+ if (!(teamsfx.hasConfig("sqlUsername") && teamsfx.hasConfig("sqlPassword")) &&
921
+ !teamsfx.hasConfig("sqlIdentityId")) {
922
+ const errMsg = `SQL configuration is not valid without ${teamsfx.hasConfig("sqlIdentityId") ? "" : "identity id "} ${teamsfx.hasConfig("sqlUsername") ? "" : "SQL username "} ${teamsfx.hasConfig("sqlPassword") ? "" : "SQL password"} exist`;
923
+ internalLogger.error(errMsg);
924
+ throw new ErrorWithCode(errMsg, ErrorCode.InvalidConfiguration);
1057
925
  }
1058
- /**
1059
- * Check SQL use MSI identity or username and password.
1060
- *
1061
- * @returns false - login with SQL MSI identity, true - login with username and password.
1062
- * @internal
1063
- */
1064
- isMsiAuthentication() {
1065
- internalLogger.verbose("Check connection config using MSI access token or username and password");
1066
- const configuration = getResourceConfiguration(ResourceType.SQL);
1067
- if ((configuration === null || configuration === void 0 ? void 0 : configuration.sqlUsername) != null && (configuration === null || configuration === void 0 ? void 0 : configuration.sqlPassword) != null) {
1068
- internalLogger.verbose("Login with username and password");
1069
- return false;
1070
- }
1071
- internalLogger.verbose("Login with MSI identity");
1072
- return true;
926
+ internalLogger.verbose("SQL configuration is valid");
927
+ }
928
+ /**
929
+ * Check SQL use MSI identity or username and password.
930
+ *
931
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
932
+ *
933
+ * @returns false - login with SQL MSI identity, true - login with username and password.
934
+ * @internal
935
+ */
936
+ function isMsiAuthentication(teamsfx) {
937
+ internalLogger.verbose("Check connection config using MSI access token or username and password");
938
+ if (teamsfx.hasConfig("sqlUsername") && teamsfx.hasConfig("sqlPassword")) {
939
+ internalLogger.verbose("Login with username and password");
940
+ return false;
1073
941
  }
1074
- /**
1075
- * check configuration is an available configurations.
1076
- * @param { SqlConfiguration } sqlConfig
1077
- *
1078
- * @returns true - SQL configuration has a valid SQL endpoints, SQL username with password or identity ID.
1079
- * false - configuration is not valid.
1080
- * @internal
1081
- */
1082
- isSQLConfigurationValid(sqlConfig) {
1083
- internalLogger.verbose("Check SQL configuration if valid");
1084
- if (!sqlConfig.sqlServerEndpoint) {
1085
- internalLogger.error("SQL configuration is not valid without SQL server endpoint exist");
1086
- throw new ErrorWithCode("SQL configuration error without SQL server endpoint exist", ErrorCode.InvalidConfiguration);
1087
- }
1088
- if (!(sqlConfig.sqlUsername && sqlConfig.sqlPassword) && !sqlConfig.sqlIdentityId) {
1089
- const errMsg = `SQL configuration is not valid without ${sqlConfig.sqlIdentityId ? "" : "identity id "} ${sqlConfig.sqlUsername ? "" : "SQL username "} ${sqlConfig.sqlPassword ? "" : "SQL password"} exist`;
1090
- internalLogger.error(errMsg);
1091
- throw new ErrorWithCode(errMsg, ErrorCode.InvalidConfiguration);
1092
- }
1093
- internalLogger.verbose("SQL configuration is valid");
942
+ internalLogger.verbose("Login with MSI identity");
943
+ return true;
944
+ }
945
+ /**
946
+ * Generate tedious connection configuration with default authentication type.
947
+ *
948
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
949
+ * @param { string? } databaseName - specify database name to override default one if there are multiple databases.
950
+ *
951
+ * @returns Tedious connection configuration with username and password.
952
+ * @internal
953
+ */
954
+ function generateDefaultConfig(teamsfx, databaseName) {
955
+ internalLogger.verbose(`SQL server ${teamsfx.getConfig("sqlServerEndpoint")}
956
+ , user name ${teamsfx.getConfig("sqlUsername")}
957
+ , database name ${databaseName}`);
958
+ const config = {
959
+ server: teamsfx.getConfig("sqlServerEndpoint"),
960
+ authentication: {
961
+ type: TediousAuthenticationType.default,
962
+ options: {
963
+ userName: teamsfx.getConfig("sqlUsername"),
964
+ password: teamsfx.getConfig("sqlPassword"),
965
+ },
966
+ },
967
+ options: {
968
+ database: databaseName,
969
+ encrypt: true,
970
+ },
971
+ };
972
+ return config;
973
+ }
974
+ /**
975
+ * Generate tedious connection configuration with azure-active-directory-access-token authentication type.
976
+ *
977
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
978
+ *
979
+ * @returns Tedious connection configuration with access token.
980
+ * @internal
981
+ */
982
+ async function generateTokenConfig(teamsfx, databaseName) {
983
+ internalLogger.verbose("Generate tedious config with MSI token");
984
+ let token;
985
+ try {
986
+ const credential = new ManagedIdentityCredential(teamsfx.getConfig("sqlIdentityId"));
987
+ token = await credential.getToken(defaultSQLScope);
1094
988
  }
1095
- /**
1096
- * Generate tedious connection configuration with default authentication type.
1097
- *
1098
- * @param { SqlConfiguration } SQL configuration with username and password.
1099
- *
1100
- * @returns Tedious connection configuration with username and password.
1101
- * @internal
1102
- */
1103
- generateDefaultConfig(sqlConfig, databaseName) {
1104
- if (databaseName === "") {
1105
- internalLogger.warn(`SQL database name is empty string`);
1106
- }
1107
- const dbName = databaseName !== null && databaseName !== void 0 ? databaseName : sqlConfig.sqlDatabaseName;
1108
- internalLogger.verbose(`SQL server ${sqlConfig.sqlServerEndpoint}, user name ${sqlConfig.sqlUsername}, database name ${dbName}`);
989
+ catch (error) {
990
+ const errMsg = "Get user MSI token failed";
991
+ internalLogger.error(errMsg);
992
+ throw new ErrorWithCode(errMsg, ErrorCode.InternalError);
993
+ }
994
+ if (token) {
1109
995
  const config = {
1110
- server: sqlConfig.sqlServerEndpoint,
996
+ server: teamsfx.getConfig("sqlServerEndpoint"),
1111
997
  authentication: {
1112
- type: TediousAuthenticationType.default,
998
+ type: TediousAuthenticationType.MSI,
1113
999
  options: {
1114
- userName: sqlConfig.sqlUsername,
1115
- password: sqlConfig.sqlPassword,
1000
+ token: token.token,
1116
1001
  },
1117
1002
  },
1118
1003
  options: {
1119
- database: dbName,
1004
+ database: databaseName,
1120
1005
  encrypt: true,
1121
1006
  },
1122
1007
  };
1008
+ internalLogger.verbose(`Generate token configuration success
1009
+ , server endpoint is ${teamsfx.getConfig("sqlServerEndpoint")}
1010
+ , database name is ${databaseName}`);
1123
1011
  return config;
1124
1012
  }
1125
- /**
1126
- * Generate tedious connection configuration with azure-active-directory-access-token authentication type.
1127
- *
1128
- * @param { SqlConfiguration } SQL configuration with AAD access token.
1129
- *
1130
- * @returns Tedious connection configuration with access token.
1131
- * @internal
1132
- */
1133
- async generateTokenConfig(sqlConfig, databaseName) {
1134
- internalLogger.verbose("Generate tedious config with MSI token");
1135
- if (databaseName === "") {
1136
- internalLogger.warn(`SQL database name is empty string`);
1137
- }
1138
- let token;
1139
- try {
1140
- const credential = new ManagedIdentityCredential(sqlConfig.sqlIdentityId);
1141
- token = await credential.getToken(this.defaultSQLScope);
1142
- }
1143
- catch (error) {
1144
- const errMsg = "Get user MSI token failed";
1145
- internalLogger.error(errMsg);
1146
- throw new ErrorWithCode(errMsg, ErrorCode.InternalError);
1147
- }
1148
- if (token) {
1149
- const config = {
1150
- server: sqlConfig.sqlServerEndpoint,
1151
- authentication: {
1152
- type: TediousAuthenticationType.MSI,
1153
- options: {
1154
- token: token.token,
1155
- },
1156
- },
1157
- options: {
1158
- database: databaseName !== null && databaseName !== void 0 ? databaseName : sqlConfig.sqlDatabaseName,
1159
- encrypt: true,
1160
- },
1161
- };
1162
- internalLogger.verbose(`Generate token configuration success, server endpoint is ${sqlConfig.sqlServerEndpoint}, database name is ${databaseName !== null && databaseName !== void 0 ? databaseName : sqlConfig.sqlDatabaseName}`);
1163
- return config;
1164
- }
1165
- internalLogger.error(`Generate token configuration, server endpoint is ${sqlConfig.sqlServerEndpoint}, MSI token is not valid`);
1166
- throw new ErrorWithCode("MSI token is not valid", ErrorCode.InternalError);
1167
- }
1013
+ internalLogger.error(`Generate token configuration
1014
+ , server endpoint is ${teamsfx.getConfig("sqlServerEndpoint")}
1015
+ , MSI token is not valid`);
1016
+ throw new ErrorWithCode("MSI token is not valid", ErrorCode.InternalError);
1168
1017
  }
1169
1018
  /**
1170
1019
  * tedious connection config authentication type.
@@ -1177,6 +1026,25 @@ var TediousAuthenticationType;
1177
1026
  TediousAuthenticationType["MSI"] = "azure-active-directory-access-token";
1178
1027
  })(TediousAuthenticationType || (TediousAuthenticationType = {}));
1179
1028
 
1029
+ // Copyright (c) Microsoft Corporation.
1030
+ // Licensed under the MIT license.
1031
+ /**
1032
+ * Identity type to use in authentication.
1033
+ *
1034
+ * @beta
1035
+ */
1036
+ var IdentityType;
1037
+ (function (IdentityType) {
1038
+ /**
1039
+ * Represents the current user of Teams.
1040
+ */
1041
+ IdentityType["User"] = "User";
1042
+ /**
1043
+ * Represents the application itself.
1044
+ */
1045
+ IdentityType["App"] = "Application";
1046
+ })(IdentityType || (IdentityType = {}));
1047
+
1180
1048
  // Copyright (c) Microsoft Corporation.
1181
1049
  const invokeResponseType = "invokeResponse";
1182
1050
  /**
@@ -1216,7 +1084,6 @@ class TokenExchangeInvokeResponse {
1216
1084
  * const dialogState = convoState.createProperty('dialogState');
1217
1085
  * const dialogs = new DialogSet(dialogState);
1218
1086
  *
1219
- * loadConfiguration();
1220
1087
  * dialogs.add(new TeamsBotSsoPrompt('TeamsBotSsoPrompt', {
1221
1088
  * scopes: ["User.Read"],
1222
1089
  * }));
@@ -1245,6 +1112,7 @@ class TeamsBotSsoPrompt extends Dialog {
1245
1112
  /**
1246
1113
  * Constructor of TeamsBotSsoPrompt.
1247
1114
  *
1115
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth
1248
1116
  * @param dialogId Unique ID of the dialog within its parent `DialogSet` or `ComponentDialog`.
1249
1117
  * @param settings Settings used to configure the prompt.
1250
1118
  *
@@ -1253,10 +1121,12 @@ class TeamsBotSsoPrompt extends Dialog {
1253
1121
  *
1254
1122
  * @beta
1255
1123
  */
1256
- constructor(dialogId, settings) {
1124
+ constructor(teamsfx, dialogId, settings) {
1257
1125
  super(dialogId);
1126
+ this.teamsfx = teamsfx;
1258
1127
  this.settings = settings;
1259
1128
  validateScopesType(settings.scopes);
1129
+ this.loadAndValidateConfig();
1260
1130
  internalLogger.info("Create a new Teams Bot SSO Prompt");
1261
1131
  }
1262
1132
  /**
@@ -1357,6 +1227,31 @@ class TeamsBotSsoPrompt extends Dialog {
1357
1227
  return Dialog.EndOfTurn;
1358
1228
  }
1359
1229
  }
1230
+ loadAndValidateConfig() {
1231
+ if (this.teamsfx.getIdentityType() !== IdentityType.User) {
1232
+ const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.teamsfx.getIdentityType().toString(), "TeamsBotSsoPrompt");
1233
+ internalLogger.error(errorMsg);
1234
+ throw new ErrorWithCode(errorMsg, ErrorCode.IdentityTypeNotSupported);
1235
+ }
1236
+ const missingConfigurations = [];
1237
+ if (!this.teamsfx.hasConfig("initiateLoginEndpoint")) {
1238
+ missingConfigurations.push("initiateLoginEndpoint");
1239
+ }
1240
+ if (!this.teamsfx.hasConfig("clientId")) {
1241
+ missingConfigurations.push("clientId");
1242
+ }
1243
+ if (!this.teamsfx.hasConfig("tenantId")) {
1244
+ missingConfigurations.push("tenantId");
1245
+ }
1246
+ if (!this.teamsfx.hasConfig("applicationIdUri")) {
1247
+ missingConfigurations.push("applicationIdUri");
1248
+ }
1249
+ if (missingConfigurations.length != 0) {
1250
+ const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
1251
+ internalLogger.error(errorMsg);
1252
+ throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
1253
+ }
1254
+ }
1360
1255
  /**
1361
1256
  * Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
1362
1257
  * @param dc dialog context
@@ -1396,31 +1291,12 @@ class TeamsBotSsoPrompt extends Dialog {
1396
1291
  * @internal
1397
1292
  */
1398
1293
  getSignInResource(loginHint) {
1399
- var _a, _b, _c, _d, _e;
1400
1294
  internalLogger.verbose("Get sign in authentication configuration");
1401
- const missingConfigurations = [];
1402
- if (!((_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.initiateLoginEndpoint)) {
1403
- missingConfigurations.push("initiateLoginEndpoint");
1404
- }
1405
- if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.clientId)) {
1406
- missingConfigurations.push("clientId");
1407
- }
1408
- if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.tenantId)) {
1409
- missingConfigurations.push("tenantId");
1410
- }
1411
- if (!((_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.applicationIdUri)) {
1412
- missingConfigurations.push("applicationIdUri");
1413
- }
1414
- if (missingConfigurations.length != 0) {
1415
- const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
1416
- internalLogger.error(errorMsg);
1417
- throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
1418
- }
1419
- const signInLink = `${config.authentication.initiateLoginEndpoint}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${config.authentication.clientId}&tenantId=${config.authentication.tenantId}&loginHint=${loginHint}`;
1295
+ const signInLink = `${this.teamsfx.getConfig("initiateLoginEndpoint")}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${this.teamsfx.getConfig("clientId")}&tenantId=${this.teamsfx.getConfig("tenantId")}&loginHint=${loginHint}`;
1420
1296
  internalLogger.verbose("Sign in link: " + signInLink);
1421
1297
  const tokenExchangeResource = {
1422
1298
  id: v4(),
1423
- uri: ((_e = config.authentication) === null || _e === void 0 ? void 0 : _e.applicationIdUri.replace(/\/$/, "")) + "/access_as_user",
1299
+ uri: this.teamsfx.getConfig("applicationIdUri").replace(/\/$/, "") + "/access_as_user",
1424
1300
  };
1425
1301
  internalLogger.verbose("Token exchange resource uri: " + tokenExchangeResource.uri);
1426
1302
  return {
@@ -1444,7 +1320,8 @@ class TeamsBotSsoPrompt extends Dialog {
1444
1320
  }
1445
1321
  else {
1446
1322
  const ssoToken = context.activity.value.token;
1447
- const credential = new OnBehalfOfUserCredential(ssoToken);
1323
+ this.teamsfx.setSsoToken(ssoToken);
1324
+ const credential = this.teamsfx.getCredential();
1448
1325
  let exchangedToken;
1449
1326
  try {
1450
1327
  exchangedToken = await credential.getToken(this.settings.scopes);
@@ -1508,5 +1385,187 @@ class TeamsBotSsoPrompt extends Dialog {
1508
1385
  }
1509
1386
  }
1510
1387
 
1511
- export { DefaultTediousConnectionConfiguration, ErrorCode, ErrorWithCode, LogLevel, M365TenantCredential, MsGraphAuthProvider, OnBehalfOfUserCredential, ResourceType, TeamsBotSsoPrompt, TeamsUserCredential, createMicrosoftGraphClient, getAuthenticationConfiguration, getLogLevel, getResourceConfiguration, loadConfiguration, setLogFunction, setLogLevel, setLogger };
1388
+ // Copyright (c) Microsoft Corporation.
1389
+ /**
1390
+ * A class providing credential and configuration.
1391
+ * @beta
1392
+ */
1393
+ class TeamsFx {
1394
+ /**
1395
+ * Constructor of TeamsFx
1396
+ *
1397
+ * @param {IdentityType} identityType - Choose user or app identity
1398
+ * @param customConfig - key/value pairs of customized configuration that overrides default ones.
1399
+ *
1400
+ * @throws {@link ErrorCode|IdentityTypeNotSupported} when setting app identity in browser.
1401
+ *
1402
+ * @beta
1403
+ */
1404
+ constructor(identityType, customConfig) {
1405
+ this.identityType = identityType !== null && identityType !== void 0 ? identityType : IdentityType.User;
1406
+ this.configuration = new Map();
1407
+ this.loadFromEnv();
1408
+ if (customConfig) {
1409
+ for (const key of Object.keys(customConfig)) {
1410
+ const value = customConfig[key];
1411
+ if (value) {
1412
+ this.configuration.set(key, value);
1413
+ }
1414
+ }
1415
+ }
1416
+ }
1417
+ /**
1418
+ * Identity type set by user.
1419
+ *
1420
+ * @returns identity type.
1421
+ * @beta
1422
+ */
1423
+ getIdentityType() {
1424
+ return this.identityType;
1425
+ }
1426
+ /**
1427
+ * Credential instance according to identity type choice.
1428
+ *
1429
+ * @remarks If user identity is chose, will return {@link TeamsUserCredential}
1430
+ * in browser environment and {@link OnBehalfOfUserCredential} in NodeJS. If app
1431
+ * identity is chose, will return {@link AppCredential}.
1432
+ *
1433
+ * @returns instance implements TokenCredential interface.
1434
+ * @beta
1435
+ */
1436
+ getCredential() {
1437
+ if (this.identityType === IdentityType.User) {
1438
+ if (this.oboUserCredential) {
1439
+ return this.oboUserCredential;
1440
+ }
1441
+ const errorMsg = "SSO token is required to user identity. Please use setSsoToken().";
1442
+ internalLogger.error(errorMsg);
1443
+ throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
1444
+ }
1445
+ else {
1446
+ if (!this.appCredential) {
1447
+ this.appCredential = new AppCredential(Object.fromEntries(this.configuration));
1448
+ }
1449
+ return this.appCredential;
1450
+ }
1451
+ }
1452
+ /**
1453
+ * Get user information.
1454
+ * @returns UserInfo object.
1455
+ * @beta
1456
+ */
1457
+ async getUserInfo() {
1458
+ if (this.identityType !== IdentityType.User) {
1459
+ const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.identityType.toString(), "TeamsFx");
1460
+ internalLogger.error(errorMsg);
1461
+ throw new ErrorWithCode(errorMsg, ErrorCode.IdentityTypeNotSupported);
1462
+ }
1463
+ return Promise.resolve(this.getCredential().getUserInfo());
1464
+ }
1465
+ /**
1466
+ * Popup login page to get user's access token with specific scopes.
1467
+ *
1468
+ * @remarks
1469
+ * Only works in Teams client APP. User will be redirected to the authorization page to login and consent.
1470
+ *
1471
+ * @example
1472
+ * ```typescript
1473
+ * await teamsfx.login(["https://graph.microsoft.com/User.Read"]); // single scope using string array
1474
+ * await teamsfx.login("https://graph.microsoft.com/User.Read"); // single scopes using string
1475
+ * await teamsfx.login(["https://graph.microsoft.com/User.Read", "Calendars.Read"]); // multiple scopes using string array
1476
+ * await teamsfx.login("https://graph.microsoft.com/User.Read Calendars.Read"); // multiple scopes using string
1477
+ * ```
1478
+ * @param scopes - The list of scopes for which the token will have access, before that, we will request user to consent.
1479
+ *
1480
+ * @throws {@link ErrorCode|InternalError} when failed to login with unknown error.
1481
+ * @throws {@link ErrorCode|ConsentFailed} when user canceled or failed to consent.
1482
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
1483
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
1484
+ *
1485
+ * @beta
1486
+ */
1487
+ async login(scopes) {
1488
+ throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "login"), ErrorCode.RuntimeNotSupported);
1489
+ }
1490
+ /**
1491
+ * Set SSO token when using user identity in NodeJS.
1492
+ * @param {string} ssoToken - used for on behalf of user flow.
1493
+ * @returns self instance.
1494
+ * @beta
1495
+ */
1496
+ setSsoToken(ssoToken) {
1497
+ if (this.identityType !== IdentityType.User) {
1498
+ throw new Error();
1499
+ }
1500
+ this.oboUserCredential = new OnBehalfOfUserCredential(ssoToken, Object.fromEntries(this.configuration));
1501
+ return this;
1502
+ }
1503
+ /**
1504
+ * Usually used by service plugins to retrieve specific config
1505
+ * @param {string} key - configuration key.
1506
+ * @returns value in configuration.
1507
+ * @beta
1508
+ */
1509
+ getConfig(key) {
1510
+ const value = this.configuration.get(key);
1511
+ if (!value) {
1512
+ const errorMsg = `Cannot find ${key} in configuration`;
1513
+ internalLogger.error(errorMsg);
1514
+ throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);
1515
+ }
1516
+ return value;
1517
+ }
1518
+ /**
1519
+ * Check the value of specific key.
1520
+ * @param {string} key - configuration key.
1521
+ * @returns true if corresponding value is not empty string.
1522
+ * @beta
1523
+ */
1524
+ hasConfig(key) {
1525
+ const value = this.configuration.get(key);
1526
+ return !!value;
1527
+ }
1528
+ /**
1529
+ * Get all configurations.
1530
+ * @returns key value mappings.
1531
+ * @beta
1532
+ */
1533
+ getConfigs() {
1534
+ const config = {};
1535
+ for (const key of this.configuration.keys()) {
1536
+ const value = this.configuration.get(key);
1537
+ if (value) {
1538
+ config[key] = value;
1539
+ }
1540
+ }
1541
+ return config;
1542
+ }
1543
+ /**
1544
+ * Load configuration from environment variables.
1545
+ */
1546
+ loadFromEnv() {
1547
+ const env = process.env;
1548
+ this.configuration.set("authorityHost", env.M365_AUTHORITY_HOST);
1549
+ this.configuration.set("tenantId", env.M365_TENANT_ID);
1550
+ this.configuration.set("clientId", env.M365_CLIENT_ID);
1551
+ this.configuration.set("clientSecret", env.M365_CLIENT_SECRET);
1552
+ this.configuration.set("initiateLoginEndpoint", env.INITIATE_LOGIN_ENDPOINT);
1553
+ this.configuration.set("applicationIdUri", env.M365_APPLICATION_ID_URI);
1554
+ this.configuration.set("apiEndpoint", env.API_ENDPOINT);
1555
+ this.configuration.set("apiName", env.API_NAME);
1556
+ this.configuration.set("sqlServerEndpoint", env.SQL_ENDPOINT);
1557
+ this.configuration.set("sqlUsername", env.SQL_USER_NAME);
1558
+ this.configuration.set("sqlPassword", env.SQL_PASSWORD);
1559
+ this.configuration.set("sqlDatabaseName", env.SQL_DATABASE_NAME);
1560
+ this.configuration.set("sqlIdentityId", env.IDENTITY_ID);
1561
+ Object.keys(env).forEach((key) => {
1562
+ const value = env[key];
1563
+ if (key.startsWith("TEAMSFX_") && value) {
1564
+ this.configuration.set(key.substring(8), value);
1565
+ }
1566
+ });
1567
+ }
1568
+ }
1569
+
1570
+ export { AppCredential, ErrorCode, ErrorWithCode, IdentityType, LogLevel, MsGraphAuthProvider, OnBehalfOfUserCredential, TeamsBotSsoPrompt, TeamsFx, TeamsUserCredential, createMicrosoftGraphClient, getLogLevel, getTediousConnectionConfig, setLogFunction, setLogLevel, setLogger };
1512
1571
  //# sourceMappingURL=index.esm2017.mjs.map