@microsoft/teamsfx 0.2.8-alpha.9ef5081b.0 → 0.2.8-alpha.b4c7969d.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.
@@ -14,6 +14,10 @@ export var ErrorCode;
14
14
  * Invalid configuration error.
15
15
  */
16
16
  ErrorCode["InvalidConfiguration"] = "InvalidConfiguration";
17
+ /**
18
+ * Invalid certificate error.
19
+ */
20
+ ErrorCode["InvalidCertificate"] = "InvalidCertificate";
17
21
  /**
18
22
  * Internal error.
19
23
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;;GAGG;AACH,MAAM,CAAN,IAAY,SAkDX;AAlDD,WAAY,SAAS;IACnB;;OAEG;IACH,kDAAqC,CAAA;IAErC;;OAEG;IACH,0DAA6C,CAAA;IAE7C;;OAEG;IACH,4CAA+B,CAAA;IAE/B;;OAEG;IACH,wDAA2C,CAAA;IAE3C;;OAEG;IACH,wDAA2C,CAAA;IAE3C;;OAEG;IACH,4CAA+B,CAAA;IAE/B;;OAEG;IACH,gDAAmC,CAAA;IAEnC;;OAEG;IACH,oDAAuC,CAAA;IAEvC;;OAEG;IACH,0CAA6B,CAAA;IAE7B;;OAEG;IACH,gDAAmC,CAAA;AACrC,CAAC,EAlDW,SAAS,KAAT,SAAS,QAkDpB;AAED;;GAEG;AACH,MAAM,OAAO,YAAY;;AACvB,6BAA6B;AACb,iCAAoB,GAAG,uCAAuC,CAAC;AAC/D,mCAAsB,GAAG,mCAAmC,CAAC;AAC7D,2CAA8B,GAAG,4CAA4C,CAAC;AAC9E,yCAA4B,GAC1C,2DAA2D,CAAC;AAC9C,iDAAoC,GAClD,8CAA8C,CAAC;AAEjD,4BAA4B;AACZ,uCAA0B,GAAG,kCAAkC,CAAC;AAChE,sCAAyB,GAAG,+BAA+B,CAAC;AAE5E,iBAAiB;AACD,6CAAgC,GAC9C,uDAAuD,CAAC;AAE1D,4BAA4B;AACZ,wCAA2B,GAAG,2CAA2C,CAAC;AAG5F;;;;GAIG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAQtC;;;;;;;OAOG;IACH,YAAY,OAAgB,EAAE,IAAgB;QAC5C,IAAI,CAAC,IAAI,EAAE;YACT,KAAK,CAAC,OAAO,CAAC,CAAC;YACf,OAAO;SACR;QAED,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Error code to trace the error types.\n * @beta\n */\nexport enum ErrorCode {\n /**\n * Invalid parameter error.\n */\n InvalidParameter = \"InvalidParameter\",\n\n /**\n * Invalid configuration error.\n */\n InvalidConfiguration = \"InvalidConfiguration\",\n\n /**\n * Internal error.\n */\n InternalError = \"InternalError\",\n\n /**\n * Channel is not supported error.\n */\n ChannelNotSupported = \"ChannelNotSupported\",\n\n /**\n * Runtime is not supported error.\n */\n RuntimeNotSupported = \"RuntimeNotSupported\",\n\n /**\n * User failed to finish the AAD consent flow failed.\n */\n ConsentFailed = \"ConsentFailed\",\n\n /**\n * The user or administrator has not consented to use the application error.\n */\n UiRequiredError = \"UiRequiredError\",\n\n /**\n * Token is not within its valid time range error.\n */\n TokenExpiredError = \"TokenExpiredError\",\n\n /**\n * Call service (AAD or simple authentication server) failed.\n */\n ServiceError = \"ServiceError\",\n\n /**\n * Operation failed.\n */\n FailedOperation = \"FailedOperation\",\n}\n\n/**\n * @internal\n */\nexport class ErrorMessage {\n // InvalidConfiguration Error\n static readonly InvalidConfiguration = \"{0} in configuration is invalid: {1}.\";\n static readonly ConfigurationNotExists = \"Configuration does not exist. {0}\";\n static readonly ResourceConfigurationNotExists = \"{0} resource configuration does not exist.\";\n static readonly MissingResourceConfiguration =\n \"Missing resource configuration with type: {0}, name: {1}.\";\n static readonly AuthenticationConfigurationNotExists =\n \"Authentication configuration does not exist.\";\n\n // RuntimeNotSupported Error\n static readonly BrowserRuntimeNotSupported = \"{0} is not supported in browser.\";\n static readonly NodejsRuntimeNotSupported = \"{0} is not supported in Node.\";\n\n // Internal Error\n static readonly FailToAcquireTokenOnBehalfOfUser =\n \"Failed to acquire access token on behalf of user: {0}\";\n\n // ChannelNotSupported Error\n static readonly OnlyMSTeamsChannelSupported = \"{0} is only supported in MS Teams Channel\";\n}\n\n/**\n * Error class with code and message thrown by the SDK.\n *\n * @beta\n */\nexport class ErrorWithCode extends Error {\n /**\n * Error code\n *\n * @readonly\n */\n code: string | undefined;\n\n /**\n * Constructor of ErrorWithCode.\n *\n * @param {string} message - error message.\n * @param {ErrorCode} code - error code.\n *\n * @beta\n */\n constructor(message?: string, code?: ErrorCode) {\n if (!code) {\n super(message);\n return;\n }\n\n super(message);\n Object.setPrototypeOf(this, ErrorWithCode.prototype);\n this.name = `${new.target.name}.${code}`;\n this.code = code;\n }\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;;GAGG;AACH,MAAM,CAAN,IAAY,SAuDX;AAvDD,WAAY,SAAS;IACnB;;OAEG;IACH,kDAAqC,CAAA;IAErC;;OAEG;IACH,0DAA6C,CAAA;IAE7C;;OAEG;IACH,sDAAyC,CAAA;IAEzC;;OAEG;IACH,4CAA+B,CAAA;IAE/B;;OAEG;IACH,wDAA2C,CAAA;IAE3C;;OAEG;IACH,wDAA2C,CAAA;IAE3C;;OAEG;IACH,4CAA+B,CAAA;IAE/B;;OAEG;IACH,gDAAmC,CAAA;IAEnC;;OAEG;IACH,oDAAuC,CAAA;IAEvC;;OAEG;IACH,0CAA6B,CAAA;IAE7B;;OAEG;IACH,gDAAmC,CAAA;AACrC,CAAC,EAvDW,SAAS,KAAT,SAAS,QAuDpB;AAED;;GAEG;AACH,MAAM,OAAO,YAAY;;AACvB,6BAA6B;AACb,iCAAoB,GAAG,uCAAuC,CAAC;AAC/D,mCAAsB,GAAG,mCAAmC,CAAC;AAC7D,2CAA8B,GAAG,4CAA4C,CAAC;AAC9E,yCAA4B,GAC1C,2DAA2D,CAAC;AAC9C,iDAAoC,GAClD,8CAA8C,CAAC;AAEjD,4BAA4B;AACZ,uCAA0B,GAAG,kCAAkC,CAAC;AAChE,sCAAyB,GAAG,+BAA+B,CAAC;AAE5E,iBAAiB;AACD,6CAAgC,GAC9C,uDAAuD,CAAC;AAE1D,4BAA4B;AACZ,wCAA2B,GAAG,2CAA2C,CAAC;AAG5F;;;;GAIG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAQtC;;;;;;;OAOG;IACH,YAAY,OAAgB,EAAE,IAAgB;QAC5C,IAAI,CAAC,IAAI,EAAE;YACT,KAAK,CAAC,OAAO,CAAC,CAAC;YACf,OAAO;SACR;QAED,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Error code to trace the error types.\n * @beta\n */\nexport enum ErrorCode {\n /**\n * Invalid parameter error.\n */\n InvalidParameter = \"InvalidParameter\",\n\n /**\n * Invalid configuration error.\n */\n InvalidConfiguration = \"InvalidConfiguration\",\n\n /**\n * Invalid certificate error.\n */\n InvalidCertificate = \"InvalidCertificate\",\n\n /**\n * Internal error.\n */\n InternalError = \"InternalError\",\n\n /**\n * Channel is not supported error.\n */\n ChannelNotSupported = \"ChannelNotSupported\",\n\n /**\n * Runtime is not supported error.\n */\n RuntimeNotSupported = \"RuntimeNotSupported\",\n\n /**\n * User failed to finish the AAD consent flow failed.\n */\n ConsentFailed = \"ConsentFailed\",\n\n /**\n * The user or administrator has not consented to use the application error.\n */\n UiRequiredError = \"UiRequiredError\",\n\n /**\n * Token is not within its valid time range error.\n */\n TokenExpiredError = \"TokenExpiredError\",\n\n /**\n * Call service (AAD or simple authentication server) failed.\n */\n ServiceError = \"ServiceError\",\n\n /**\n * Operation failed.\n */\n FailedOperation = \"FailedOperation\",\n}\n\n/**\n * @internal\n */\nexport class ErrorMessage {\n // InvalidConfiguration Error\n static readonly InvalidConfiguration = \"{0} in configuration is invalid: {1}.\";\n static readonly ConfigurationNotExists = \"Configuration does not exist. {0}\";\n static readonly ResourceConfigurationNotExists = \"{0} resource configuration does not exist.\";\n static readonly MissingResourceConfiguration =\n \"Missing resource configuration with type: {0}, name: {1}.\";\n static readonly AuthenticationConfigurationNotExists =\n \"Authentication configuration does not exist.\";\n\n // RuntimeNotSupported Error\n static readonly BrowserRuntimeNotSupported = \"{0} is not supported in browser.\";\n static readonly NodejsRuntimeNotSupported = \"{0} is not supported in Node.\";\n\n // Internal Error\n static readonly FailToAcquireTokenOnBehalfOfUser =\n \"Failed to acquire access token on behalf of user: {0}\";\n\n // ChannelNotSupported Error\n static readonly OnlyMSTeamsChannelSupported = \"{0} is only supported in MS Teams Channel\";\n}\n\n/**\n * Error class with code and message thrown by the SDK.\n *\n * @beta\n */\nexport class ErrorWithCode extends Error {\n /**\n * Error code\n *\n * @readonly\n */\n code: string | undefined;\n\n /**\n * Constructor of ErrorWithCode.\n *\n * @param {string} message - error message.\n * @param {ErrorCode} code - error code.\n *\n * @beta\n */\n constructor(message?: string, code?: ErrorCode) {\n if (!code) {\n super(message);\n return;\n }\n\n super(message);\n Object.setPrototypeOf(this, ErrorWithCode.prototype);\n this.name = `${new.target.name}.${code}`;\n this.code = code;\n }\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT license.
3
3
  import { __awaiter } from "tslib";
4
- import { ClientSecretCredential, AuthenticationError, } from "@azure/identity";
5
4
  import { internalLogger } from "../util/logger";
6
- import { validateScopesType, formatString } from "../util/utils";
5
+ import { validateScopesType, formatString, getScopesArray } from "../util/utils";
7
6
  import { getAuthenticationConfiguration } from "../core/configurationProvider";
8
7
  import { ErrorCode, ErrorMessage, ErrorWithCode } from "../core/errors";
8
+ import { createConfidentialClientApplication } from "../util/utils.node";
9
9
  /**
10
10
  * Represent Microsoft 365 tenant identity, and it is usually used when user is not involved like time-triggered automation job.
11
11
  *
@@ -35,10 +35,7 @@ export class M365TenantCredential {
35
35
  constructor() {
36
36
  internalLogger.info("Create M365 tenant credential");
37
37
  const config = this.loadAndValidateConfig();
38
- const tokenCredentialOptions = {
39
- authorityHost: config.authorityHost,
40
- };
41
- this.clientSecretCredential = new ClientSecretCredential(config.tenantId, config.clientId, config.clientSecret, tokenCredentialOptions);
38
+ this.msalClient = createConfidentialClientApplication(config);
42
39
  }
43
40
  /**
44
41
  * Get access token for credential.
@@ -73,20 +70,21 @@ export class M365TenantCredential {
73
70
  const scopesStr = typeof scopes === "string" ? scopes : scopes.join(" ");
74
71
  internalLogger.info("Get access token with scopes: " + scopesStr);
75
72
  try {
76
- accessToken = yield this.clientSecretCredential.getToken(scopes);
73
+ const scopesArray = getScopesArray(scopes);
74
+ const authenticationResult = yield this.msalClient.acquireTokenByClientCredential({
75
+ scopes: scopesArray,
76
+ });
77
+ if (authenticationResult) {
78
+ accessToken = {
79
+ token: authenticationResult.accessToken,
80
+ expiresOnTimestamp: authenticationResult.expiresOn.getTime(),
81
+ };
82
+ }
77
83
  }
78
84
  catch (err) {
79
- if (err instanceof AuthenticationError) {
80
- const authError = err;
81
- const errorMsg = `Get M365 tenant credential with authentication error: status code ${authError.statusCode}, error messages: ${authError.message}`;
82
- internalLogger.error(errorMsg);
83
- throw new ErrorWithCode(errorMsg, ErrorCode.ServiceError);
84
- }
85
- else {
86
- const errorMsg = "Get M365 tenant credential failed with error: " + err.message;
87
- internalLogger.error(errorMsg);
88
- throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);
89
- }
85
+ const errorMsg = "Get M365 tenant credential failed with error: " + err.message;
86
+ internalLogger.error(errorMsg);
87
+ throw new ErrorWithCode(errorMsg, ErrorCode.ServiceError);
90
88
  }
91
89
  if (!accessToken) {
92
90
  const errorMsg = "Get M365 tenant credential access token failed with empty access token";
@@ -107,15 +105,15 @@ export class M365TenantCredential {
107
105
  internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);
108
106
  throw new ErrorWithCode(ErrorMessage.AuthenticationConfigurationNotExists, ErrorCode.InvalidConfiguration);
109
107
  }
110
- if (config.clientId && config.clientSecret && config.tenantId) {
108
+ if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {
111
109
  return config;
112
110
  }
113
111
  const missingValues = [];
114
112
  if (!config.clientId) {
115
113
  missingValues.push("clientId");
116
114
  }
117
- if (!config.clientSecret) {
118
- missingValues.push("clientSecret");
115
+ if (!config.clientSecret && !config.certificateContent) {
116
+ missingValues.push("clientSecret or certificateContent");
119
117
  }
120
118
  if (!config.tenantId) {
121
119
  missingValues.push("tenantId");
@@ -1 +1 @@
1
- {"version":3,"file":"m365TenantCredential.js","sourceRoot":"","sources":["../../../src/credential/m365TenantCredential.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport {\n AccessToken,\n TokenCredential,\n GetTokenOptions,\n ClientSecretCredential,\n TokenCredentialOptions,\n AuthenticationError,\n} from \"@azure/identity\";\nimport { AuthenticationConfiguration } from \"../models/configuration\";\nimport { internalLogger } from \"../util/logger\";\nimport { validateScopesType, formatString } from \"../util/utils\";\nimport { getAuthenticationConfiguration } from \"../core/configurationProvider\";\nimport { ErrorCode, ErrorMessage, ErrorWithCode } from \"../core/errors\";\n\n/**\n * Represent Microsoft 365 tenant identity, and it is usually used when user is not involved like time-triggered automation job.\n *\n * @example\n * ```typescript\n * loadConfiguration(); // load configuration from environment variables\n * const credential = new M365TenantCredential();\n * ```\n *\n * @remarks\n * Only works in in server side.\n *\n * @beta\n */\nexport class M365TenantCredential implements TokenCredential {\n private readonly clientSecretCredential: ClientSecretCredential;\n\n /**\n * Constructor of M365TenantCredential.\n *\n * @remarks\n * Only works in in server side.\n *\n * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.\n *\n * @beta\n */\n constructor() {\n internalLogger.info(\"Create M365 tenant credential\");\n\n const config = this.loadAndValidateConfig();\n\n const tokenCredentialOptions: TokenCredentialOptions = {\n authorityHost: config.authorityHost,\n };\n\n this.clientSecretCredential = new ClientSecretCredential(\n config.tenantId!,\n config.clientId!,\n config.clientSecret!,\n tokenCredentialOptions\n );\n }\n\n /**\n * Get access token for credential.\n *\n * @example\n * ```typescript\n * await credential.getToken([\"User.Read.All\"]) // Get Graph access token for single scope using string array\n * await credential.getToken(\"User.Read.All\") // Get Graph access token for single scope using string\n * await credential.getToken([\"User.Read.All\", \"Calendars.Read\"]) // Get Graph access token for multiple scopes using string array\n * await credential.getToken(\"User.Read.All Calendars.Read\") // Get Graph access token for multiple scopes using space-separated string\n * await credential.getToken(\"https://graph.microsoft.com/User.Read.All\") // Get Graph access token with full resource URI\n * await credential.getToken([\"https://outlook.office.com/Mail.Read\"]) // Get Outlook access token\n * ```\n *\n * @param {string | string[]} scopes - The list of scopes for which the token will have access.\n * @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.\n *\n * @throws {@link ErrorCode|ServiceError} when get access token with authentication error.\n * @throws {@link ErrorCode|InternalError} when get access token with unknown error.\n * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.\n *\n * @returns Access token with expected scopes.\n * Throw error if get access token failed.\n *\n * @beta\n */\n async getToken(\n scopes: string | string[],\n options?: GetTokenOptions\n ): Promise<AccessToken | null> {\n let accessToken;\n validateScopesType(scopes);\n const scopesStr = typeof scopes === \"string\" ? scopes : scopes.join(\" \");\n internalLogger.info(\"Get access token with scopes: \" + scopesStr);\n\n try {\n accessToken = await this.clientSecretCredential.getToken(scopes);\n } catch (err: any) {\n if (err instanceof AuthenticationError) {\n const authError = err as AuthenticationError;\n const errorMsg = `Get M365 tenant credential with authentication error: status code ${authError.statusCode}, error messages: ${authError.message}`;\n internalLogger.error(errorMsg);\n\n throw new ErrorWithCode(errorMsg, ErrorCode.ServiceError);\n } else {\n const errorMsg = \"Get M365 tenant credential failed with error: \" + err.message;\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);\n }\n }\n\n if (!accessToken) {\n const errorMsg = \"Get M365 tenant credential access token failed with empty access token\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);\n }\n\n return accessToken;\n }\n\n /**\n * Load and validate authentication configuration\n * @returns Authentication configuration\n */\n private loadAndValidateConfig(): AuthenticationConfiguration {\n internalLogger.verbose(\"Validate authentication configuration\");\n\n const config = getAuthenticationConfiguration();\n\n if (!config) {\n internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);\n throw new ErrorWithCode(\n ErrorMessage.AuthenticationConfigurationNotExists,\n ErrorCode.InvalidConfiguration\n );\n }\n\n if (config.clientId && config.clientSecret && config.tenantId) {\n return config;\n }\n\n const missingValues = [];\n\n if (!config.clientId) {\n missingValues.push(\"clientId\");\n }\n\n if (!config.clientSecret) {\n missingValues.push(\"clientSecret\");\n }\n\n if (!config.tenantId) {\n missingValues.push(\"tenantId\");\n }\n\n const errorMsg = formatString(\n ErrorMessage.InvalidConfiguration,\n missingValues.join(\", \"),\n \"undefined\"\n );\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);\n }\n}\n"]}
1
+ {"version":3,"file":"m365TenantCredential.js","sourceRoot":"","sources":["../../../src/credential/m365TenantCredential.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { AccessToken, TokenCredential, GetTokenOptions } from \"@azure/identity\";\nimport { AuthenticationConfiguration } from \"../models/configuration\";\nimport { internalLogger } from \"../util/logger\";\nimport { validateScopesType, formatString, getScopesArray } from \"../util/utils\";\nimport { getAuthenticationConfiguration } from \"../core/configurationProvider\";\nimport { ErrorCode, ErrorMessage, ErrorWithCode } from \"../core/errors\";\nimport { ConfidentialClientApplication } from \"@azure/msal-node\";\nimport { createConfidentialClientApplication } from \"../util/utils.node\";\n\n/**\n * Represent Microsoft 365 tenant identity, and it is usually used when user is not involved like time-triggered automation job.\n *\n * @example\n * ```typescript\n * loadConfiguration(); // load configuration from environment variables\n * const credential = new M365TenantCredential();\n * ```\n *\n * @remarks\n * Only works in in server side.\n *\n * @beta\n */\nexport class M365TenantCredential implements TokenCredential {\n private readonly msalClient: ConfidentialClientApplication;\n\n /**\n * Constructor of M365TenantCredential.\n *\n * @remarks\n * Only works in in server side.\n *\n * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.\n *\n * @beta\n */\n constructor() {\n internalLogger.info(\"Create M365 tenant credential\");\n\n const config = this.loadAndValidateConfig();\n\n this.msalClient = createConfidentialClientApplication(config);\n }\n\n /**\n * Get access token for credential.\n *\n * @example\n * ```typescript\n * await credential.getToken([\"User.Read.All\"]) // Get Graph access token for single scope using string array\n * await credential.getToken(\"User.Read.All\") // Get Graph access token for single scope using string\n * await credential.getToken([\"User.Read.All\", \"Calendars.Read\"]) // Get Graph access token for multiple scopes using string array\n * await credential.getToken(\"User.Read.All Calendars.Read\") // Get Graph access token for multiple scopes using space-separated string\n * await credential.getToken(\"https://graph.microsoft.com/User.Read.All\") // Get Graph access token with full resource URI\n * await credential.getToken([\"https://outlook.office.com/Mail.Read\"]) // Get Outlook access token\n * ```\n *\n * @param {string | string[]} scopes - The list of scopes for which the token will have access.\n * @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.\n *\n * @throws {@link ErrorCode|ServiceError} when get access token with authentication error.\n * @throws {@link ErrorCode|InternalError} when get access token with unknown error.\n * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.\n *\n * @returns Access token with expected scopes.\n * Throw error if get access token failed.\n *\n * @beta\n */\n async getToken(\n scopes: string | string[],\n options?: GetTokenOptions\n ): Promise<AccessToken | null> {\n let accessToken;\n validateScopesType(scopes);\n const scopesStr = typeof scopes === \"string\" ? scopes : scopes.join(\" \");\n internalLogger.info(\"Get access token with scopes: \" + scopesStr);\n\n try {\n const scopesArray = getScopesArray(scopes);\n const authenticationResult = await this.msalClient.acquireTokenByClientCredential({\n scopes: scopesArray,\n });\n if (authenticationResult) {\n accessToken = {\n token: authenticationResult.accessToken,\n expiresOnTimestamp: authenticationResult.expiresOn!.getTime(),\n };\n }\n } catch (err: any) {\n const errorMsg = \"Get M365 tenant credential failed with error: \" + err.message;\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.ServiceError);\n }\n\n if (!accessToken) {\n const errorMsg = \"Get M365 tenant credential access token failed with empty access token\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);\n }\n\n return accessToken;\n }\n\n /**\n * Load and validate authentication configuration\n * @returns Authentication configuration\n */\n private loadAndValidateConfig(): AuthenticationConfiguration {\n internalLogger.verbose(\"Validate authentication configuration\");\n\n const config = getAuthenticationConfiguration();\n\n if (!config) {\n internalLogger.error(ErrorMessage.AuthenticationConfigurationNotExists);\n throw new ErrorWithCode(\n ErrorMessage.AuthenticationConfigurationNotExists,\n ErrorCode.InvalidConfiguration\n );\n }\n\n if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {\n return config;\n }\n\n const missingValues = [];\n\n if (!config.clientId) {\n missingValues.push(\"clientId\");\n }\n\n if (!config.clientSecret && !config.certificateContent) {\n missingValues.push(\"clientSecret or certificateContent\");\n }\n\n if (!config.tenantId) {\n missingValues.push(\"tenantId\");\n }\n\n const errorMsg = formatString(\n ErrorMessage.InvalidConfiguration,\n missingValues.join(\", \"),\n \"undefined\"\n );\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);\n }\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT license.
3
3
  import { __awaiter } from "tslib";
4
- import { ConfidentialClientApplication } from "@azure/msal-node";
5
4
  import { config } from "../core/configurationProvider";
6
5
  import { internalLogger } from "../util/logger";
7
- import { formatString, getUserInfoFromSsoToken, parseJwt, validateScopesType } from "../util/utils";
6
+ import { formatString, getScopesArray, getUserInfoFromSsoToken, parseJwt, validateScopesType, } from "../util/utils";
8
7
  import { ErrorWithCode, ErrorCode, ErrorMessage } from "../core/errors";
8
+ import { createConfidentialClientApplication } from "../util/utils.node";
9
9
  /**
10
10
  * Represent on-behalf-of flow to get user identity, and it is designed to be used in server side.
11
11
  *
@@ -29,7 +29,7 @@ export class OnBehalfOfUserCredential {
29
29
  *
30
30
  * @param {string} ssoToken - User token provided by Teams SSO feature.
31
31
  *
32
- * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, authority host or tenant id is not found in config.
32
+ * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
33
33
  * @throws {@link ErrorCode|InternalError} when SSO token is not valid.
34
34
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
35
35
  *
@@ -45,10 +45,10 @@ export class OnBehalfOfUserCredential {
45
45
  if (!((_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.authorityHost)) {
46
46
  missingConfigurations.push("authorityHost");
47
47
  }
48
- if (!((_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.clientSecret)) {
49
- missingConfigurations.push("clientSecret");
48
+ 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)) {
49
+ missingConfigurations.push("clientSecret or certificateContent");
50
50
  }
51
- if (!((_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.tenantId)) {
51
+ if (!((_e = config === null || config === void 0 ? void 0 : config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId)) {
52
52
  missingConfigurations.push("tenantId");
53
53
  }
54
54
  if (missingConfigurations.length != 0) {
@@ -56,15 +56,7 @@ export class OnBehalfOfUserCredential {
56
56
  internalLogger.error(errorMsg);
57
57
  throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
58
58
  }
59
- const normalizedAuthorityHost = config.authentication.authorityHost.replace(/\/+$/g, "");
60
- const authority = normalizedAuthorityHost + "/" + ((_e = config.authentication) === null || _e === void 0 ? void 0 : _e.tenantId);
61
- this.msalClient = new ConfidentialClientApplication({
62
- auth: {
63
- clientId: config.authentication.clientId,
64
- authority: authority,
65
- clientSecret: config.authentication.clientSecret,
66
- },
67
- });
59
+ this.msalClient = createConfidentialClientApplication(config.authentication);
68
60
  const decodedSsoToken = parseJwt(ssoToken);
69
61
  this.ssoToken = {
70
62
  token: ssoToken,
@@ -109,8 +101,7 @@ export class OnBehalfOfUserCredential {
109
101
  getToken(scopes, options) {
110
102
  return __awaiter(this, void 0, void 0, function* () {
111
103
  validateScopesType(scopes);
112
- let scopesArray = typeof scopes === "string" ? scopes.split(" ") : scopes;
113
- scopesArray = scopesArray.filter((x) => x !== null && x !== "");
104
+ const scopesArray = getScopesArray(scopes);
114
105
  let result;
115
106
  if (!scopesArray.length) {
116
107
  internalLogger.info("Get SSO token.");
@@ -1 +1 @@
1
- {"version":3,"file":"onBehalfOfUserCredential.js","sourceRoot":"","sources":["../../../src/credential/onBehalfOfUserCredential.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { AccessToken, GetTokenOptions, TokenCredential } from \"@azure/identity\";\nimport { AuthenticationResult, ConfidentialClientApplication } from \"@azure/msal-node\";\nimport { config } from \"../core/configurationProvider\";\nimport { UserInfo } from \"../models/userinfo\";\nimport { internalLogger } from \"../util/logger\";\nimport { formatString, getUserInfoFromSsoToken, parseJwt, validateScopesType } from \"../util/utils\";\nimport { ErrorWithCode, ErrorCode, ErrorMessage } from \"../core/errors\";\n\n/**\n * Represent on-behalf-of flow to get user identity, and it is designed to be used in server side.\n *\n * @example\n * ```typescript\n * loadConfiguration(); // load configuration from environment variables\n * const credential = new OnBehalfOfUserCredential(ssoToken);\n * ```\n *\n * @remarks\n * Can only be used in server side.\n *\n * @beta\n */\nexport class OnBehalfOfUserCredential implements TokenCredential {\n private msalClient: ConfidentialClientApplication;\n private ssoToken: AccessToken;\n\n /**\n * Constructor of OnBehalfOfUserCredential\n *\n * @remarks\n * Only works in in server side.\n *\n * @param {string} ssoToken - User token provided by Teams SSO feature.\n *\n * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, authority host or tenant id is not found in config.\n * @throws {@link ErrorCode|InternalError} when SSO token is not valid.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.\n *\n * @beta\n */\n constructor(ssoToken: string) {\n internalLogger.info(\"Get on behalf of user credential\");\n\n const missingConfigurations: string[] = [];\n if (!config?.authentication?.clientId) {\n missingConfigurations.push(\"clientId\");\n }\n\n if (!config?.authentication?.authorityHost) {\n missingConfigurations.push(\"authorityHost\");\n }\n\n if (!config?.authentication?.clientSecret) {\n missingConfigurations.push(\"clientSecret\");\n }\n\n if (!config?.authentication?.tenantId) {\n missingConfigurations.push(\"tenantId\");\n }\n\n if (missingConfigurations.length != 0) {\n const errorMsg = formatString(\n ErrorMessage.InvalidConfiguration,\n missingConfigurations.join(\", \"),\n \"undefined\"\n );\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);\n }\n\n const normalizedAuthorityHost = config.authentication!.authorityHost!.replace(/\\/+$/g, \"\");\n const authority: string =\n normalizedAuthorityHost + \"/\" + config.authentication?.tenantId;\n this.msalClient = new ConfidentialClientApplication({\n auth: {\n clientId: config.authentication!.clientId!,\n authority: authority,\n clientSecret: config.authentication!.clientSecret!,\n },\n });\n\n const decodedSsoToken = parseJwt(ssoToken);\n this.ssoToken = {\n token: ssoToken,\n expiresOnTimestamp: decodedSsoToken.exp,\n };\n }\n\n /**\n * Get access token from credential.\n *\n * @example\n * ```typescript\n * await credential.getToken([]) // Get SSO token using empty string array\n * await credential.getToken(\"\") // Get SSO token using empty string\n * await credential.getToken([\".default\"]) // Get Graph access token with default scope using string array\n * await credential.getToken(\".default\") // Get Graph access token with default scope using string\n * await credential.getToken([\"User.Read\"]) // Get Graph access token for single scope using string array\n * await credential.getToken(\"User.Read\") // Get Graph access token for single scope using string\n * await credential.getToken([\"User.Read\", \"Application.Read.All\"]) // Get Graph access token for multiple scopes using string array\n * await credential.getToken(\"User.Read Application.Read.All\") // Get Graph access token for multiple scopes using space-separated string\n * await credential.getToken(\"https://graph.microsoft.com/User.Read\") // Get Graph access token with full resource URI\n * await credential.getToken([\"https://outlook.office.com/Mail.Read\"]) // Get Outlook access token\n * ```\n *\n * @param {string | string[]} scopes - The list of scopes for which the token will have access.\n * @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.\n *\n * @throws {@link ErrorCode|InternalError} when failed to acquire access token on behalf of user with unknown error.\n * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.\n * @throws {@link ErrorCode|UiRequiredError} when need user consent to get access token.\n * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.\n * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.\n *\n * @returns Access token with expected scopes.\n *\n * @remarks\n * If scopes is empty string or array, it returns SSO token.\n * If scopes is non-empty, it returns access token for target scope.\n *\n * @beta\n */\n async getToken(\n scopes: string | string[],\n options?: GetTokenOptions\n ): Promise<AccessToken | null> {\n validateScopesType(scopes);\n\n let scopesArray: string[] = typeof scopes === \"string\" ? scopes.split(\" \") : scopes;\n scopesArray = scopesArray.filter((x) => x !== null && x !== \"\");\n\n let result: AccessToken | null;\n if (!scopesArray.length) {\n internalLogger.info(\"Get SSO token.\");\n if (Math.floor(Date.now() / 1000) > this.ssoToken.expiresOnTimestamp) {\n const errorMsg = \"Sso token has already expired.\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.TokenExpiredError);\n }\n result = this.ssoToken;\n } else {\n internalLogger.info(\"Get access token with scopes: \" + scopesArray.join(\" \"));\n\n let authenticationResult: AuthenticationResult | null;\n try {\n authenticationResult = await this.msalClient.acquireTokenOnBehalfOf({\n oboAssertion: this.ssoToken.token,\n scopes: scopesArray,\n });\n } catch (error) {\n throw this.generateAuthServerError(error);\n }\n\n if (!authenticationResult) {\n const errorMsg = \"Access token is null\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(\n formatString(ErrorMessage.FailToAcquireTokenOnBehalfOfUser, errorMsg),\n ErrorCode.InternalError\n );\n }\n\n result = {\n token: authenticationResult.accessToken,\n expiresOnTimestamp: authenticationResult.expiresOn!.getTime(),\n };\n }\n\n return result;\n }\n\n /**\n * Get basic user info from SSO token.\n *\n * @example\n * ```typescript\n * const currentUser = getUserInfo();\n * ```\n *\n * @throws {@link ErrorCode|InternalError} when SSO token is not valid.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.\n *\n * @returns Basic user info with user displayName, objectId and preferredUserName.\n *\n * @beta\n */\n public getUserInfo(): UserInfo {\n internalLogger.info(\"Get basic user info from SSO token\");\n return getUserInfoFromSsoToken(this.ssoToken.token);\n }\n\n private generateAuthServerError(err: any): Error {\n const errorMessage = err.errorMessage;\n if (err.name === \"InteractionRequiredAuthError\") {\n const fullErrorMsg =\n \"Failed to get access token from AAD server, interaction required: \" + errorMessage;\n internalLogger.warn(fullErrorMsg);\n return new ErrorWithCode(fullErrorMsg, ErrorCode.UiRequiredError);\n } else if (errorMessage && errorMessage.indexOf(\"AADSTS500133\") >= 0) {\n const fullErrorMsg =\n \"Failed to get access token from AAD server, sso token expired: \" + errorMessage;\n internalLogger.error(fullErrorMsg);\n return new ErrorWithCode(fullErrorMsg, ErrorCode.TokenExpiredError);\n } else {\n const fullErrorMsg = formatString(\n ErrorMessage.FailToAcquireTokenOnBehalfOfUser,\n errorMessage\n );\n internalLogger.error(fullErrorMsg);\n return new ErrorWithCode(fullErrorMsg, ErrorCode.ServiceError);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"onBehalfOfUserCredential.js","sourceRoot":"","sources":["../../../src/credential/onBehalfOfUserCredential.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { AccessToken, GetTokenOptions, TokenCredential } from \"@azure/identity\";\nimport { AuthenticationResult, ConfidentialClientApplication } from \"@azure/msal-node\";\nimport { config } from \"../core/configurationProvider\";\nimport { UserInfo } from \"../models/userinfo\";\nimport { internalLogger } from \"../util/logger\";\nimport {\n formatString,\n getScopesArray,\n getUserInfoFromSsoToken,\n parseJwt,\n validateScopesType,\n} from \"../util/utils\";\nimport { ErrorWithCode, ErrorCode, ErrorMessage } from \"../core/errors\";\nimport { createConfidentialClientApplication } from \"../util/utils.node\";\n\n/**\n * Represent on-behalf-of flow to get user identity, and it is designed to be used in server side.\n *\n * @example\n * ```typescript\n * loadConfiguration(); // load configuration from environment variables\n * const credential = new OnBehalfOfUserCredential(ssoToken);\n * ```\n *\n * @remarks\n * Can only be used in server side.\n *\n * @beta\n */\nexport class OnBehalfOfUserCredential implements TokenCredential {\n private msalClient: ConfidentialClientApplication;\n private ssoToken: AccessToken;\n\n /**\n * Constructor of OnBehalfOfUserCredential\n *\n * @remarks\n * Only works in in server side.\n *\n * @param {string} ssoToken - User token provided by Teams SSO feature.\n *\n * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.\n * @throws {@link ErrorCode|InternalError} when SSO token is not valid.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.\n *\n * @beta\n */\n constructor(ssoToken: string) {\n internalLogger.info(\"Get on behalf of user credential\");\n\n const missingConfigurations: string[] = [];\n if (!config?.authentication?.clientId) {\n missingConfigurations.push(\"clientId\");\n }\n\n if (!config?.authentication?.authorityHost) {\n missingConfigurations.push(\"authorityHost\");\n }\n\n if (!config?.authentication?.clientSecret && !config?.authentication?.certificateContent) {\n missingConfigurations.push(\"clientSecret or certificateContent\");\n }\n\n if (!config?.authentication?.tenantId) {\n missingConfigurations.push(\"tenantId\");\n }\n\n if (missingConfigurations.length != 0) {\n const errorMsg = formatString(\n ErrorMessage.InvalidConfiguration,\n missingConfigurations.join(\", \"),\n \"undefined\"\n );\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);\n }\n\n this.msalClient = createConfidentialClientApplication(config.authentication!);\n\n const decodedSsoToken = parseJwt(ssoToken);\n this.ssoToken = {\n token: ssoToken,\n expiresOnTimestamp: decodedSsoToken.exp,\n };\n }\n\n /**\n * Get access token from credential.\n *\n * @example\n * ```typescript\n * await credential.getToken([]) // Get SSO token using empty string array\n * await credential.getToken(\"\") // Get SSO token using empty string\n * await credential.getToken([\".default\"]) // Get Graph access token with default scope using string array\n * await credential.getToken(\".default\") // Get Graph access token with default scope using string\n * await credential.getToken([\"User.Read\"]) // Get Graph access token for single scope using string array\n * await credential.getToken(\"User.Read\") // Get Graph access token for single scope using string\n * await credential.getToken([\"User.Read\", \"Application.Read.All\"]) // Get Graph access token for multiple scopes using string array\n * await credential.getToken(\"User.Read Application.Read.All\") // Get Graph access token for multiple scopes using space-separated string\n * await credential.getToken(\"https://graph.microsoft.com/User.Read\") // Get Graph access token with full resource URI\n * await credential.getToken([\"https://outlook.office.com/Mail.Read\"]) // Get Outlook access token\n * ```\n *\n * @param {string | string[]} scopes - The list of scopes for which the token will have access.\n * @param {GetTokenOptions} options - The options used to configure any requests this TokenCredential implementation might make.\n *\n * @throws {@link ErrorCode|InternalError} when failed to acquire access token on behalf of user with unknown error.\n * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.\n * @throws {@link ErrorCode|UiRequiredError} when need user consent to get access token.\n * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.\n * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.\n *\n * @returns Access token with expected scopes.\n *\n * @remarks\n * If scopes is empty string or array, it returns SSO token.\n * If scopes is non-empty, it returns access token for target scope.\n *\n * @beta\n */\n async getToken(\n scopes: string | string[],\n options?: GetTokenOptions\n ): Promise<AccessToken | null> {\n validateScopesType(scopes);\n\n const scopesArray = getScopesArray(scopes);\n\n let result: AccessToken | null;\n if (!scopesArray.length) {\n internalLogger.info(\"Get SSO token.\");\n if (Math.floor(Date.now() / 1000) > this.ssoToken.expiresOnTimestamp) {\n const errorMsg = \"Sso token has already expired.\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.TokenExpiredError);\n }\n result = this.ssoToken;\n } else {\n internalLogger.info(\"Get access token with scopes: \" + scopesArray.join(\" \"));\n\n let authenticationResult: AuthenticationResult | null;\n try {\n authenticationResult = await this.msalClient.acquireTokenOnBehalfOf({\n oboAssertion: this.ssoToken.token,\n scopes: scopesArray,\n });\n } catch (error) {\n throw this.generateAuthServerError(error);\n }\n\n if (!authenticationResult) {\n const errorMsg = \"Access token is null\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(\n formatString(ErrorMessage.FailToAcquireTokenOnBehalfOfUser, errorMsg),\n ErrorCode.InternalError\n );\n }\n\n result = {\n token: authenticationResult.accessToken,\n expiresOnTimestamp: authenticationResult.expiresOn!.getTime(),\n };\n }\n\n return result;\n }\n\n /**\n * Get basic user info from SSO token.\n *\n * @example\n * ```typescript\n * const currentUser = getUserInfo();\n * ```\n *\n * @throws {@link ErrorCode|InternalError} when SSO token is not valid.\n * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.\n *\n * @returns Basic user info with user displayName, objectId and preferredUserName.\n *\n * @beta\n */\n public getUserInfo(): UserInfo {\n internalLogger.info(\"Get basic user info from SSO token\");\n return getUserInfoFromSsoToken(this.ssoToken.token);\n }\n\n private generateAuthServerError(err: any): Error {\n const errorMessage = err.errorMessage;\n if (err.name === \"InteractionRequiredAuthError\") {\n const fullErrorMsg =\n \"Failed to get access token from AAD server, interaction required: \" + errorMessage;\n internalLogger.warn(fullErrorMsg);\n return new ErrorWithCode(fullErrorMsg, ErrorCode.UiRequiredError);\n } else if (errorMessage && errorMessage.indexOf(\"AADSTS500133\") >= 0) {\n const fullErrorMsg =\n \"Failed to get access token from AAD server, sso token expired: \" + errorMessage;\n internalLogger.error(fullErrorMsg);\n return new ErrorWithCode(fullErrorMsg, ErrorCode.TokenExpiredError);\n } else {\n const fullErrorMsg = formatString(\n ErrorMessage.FailToAcquireTokenOnBehalfOfUser,\n errorMessage\n );\n internalLogger.error(fullErrorMsg);\n return new ErrorWithCode(fullErrorMsg, ErrorCode.ServiceError);\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/models/configuration.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAsGlC;;;GAGG;AACH,MAAM,CAAN,IAAY,YAYX;AAZD,WAAY,YAAY;IACtB;;;OAGG;IACH,6CAAG,CAAA;IAEH;;;OAGG;IACH,6CAAG,CAAA;AACL,CAAC,EAZW,YAAY,KAAZ,YAAY,QAYvB","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Configuration for current environment.\n * @beta\n */\nexport interface Configuration {\n /**\n * Authentication related configuration.\n *\n * @readonly\n */\n readonly authentication?: AuthenticationConfiguration;\n\n /**\n * Configuration for resources.\n *\n * @readonly\n */\n readonly resources?: ResourceConfiguration[];\n}\n\n/**\n * Authentication related configuration.\n * @beta\n */\nexport interface AuthenticationConfiguration {\n /**\n * Hostname of AAD authority. Default value comes from M365_AUTHORITY_HOST environment variable.\n *\n * @readonly\n */\n readonly authorityHost?: string;\n\n /**\n * AAD tenant id, default value comes from M365_TENANT_ID environment variable.\n *\n * @readonly\n */\n readonly tenantId?: string;\n\n /**\n * The client (application) ID of an App Registration in the tenant, default value comes from M365_CLIENT_ID environment variable\n *\n * @readonly\n */\n readonly clientId?: string;\n\n /**\n * Secret string that the application uses when requesting a token. Only used in confidential client applications. Can be created in the Azure app registration portal. Default value comes from M365_CLIENT_SECRET environment variable\n *\n * @readonly\n */\n readonly clientSecret?: string;\n\n /**\n * Endpoint of auth service provisioned by Teams Framework. Default value comes from SIMPLE_AUTH_ENDPOINT environment variable.\n *\n * @readonly\n */\n readonly simpleAuthEndpoint?: string;\n\n /**\n * Login page for Teams to redirect to. Default value comes from INITIATE_LOGIN_ENDPOINT environment variable.\n *\n * @readonly\n */\n readonly initiateLoginEndpoint?: string;\n\n /**\n * Application ID URI. Default value comes from M365_APPLICATION_ID_URI environment variable.\n */\n readonly applicationIdUri?: string;\n}\n\n/**\n * Configuration for resources.\n * @beta\n */\nexport interface ResourceConfiguration {\n /**\n * Resource type.\n *\n * @readonly\n */\n readonly type: ResourceType;\n\n /**\n * Resource name.\n *\n * @readonly\n */\n readonly name: string;\n\n /**\n * Config for the resource.\n *\n * @readonly\n */\n readonly properties: { [index: string]: any };\n}\n\n/**\n * Available resource type.\n * @beta\n */\nexport enum ResourceType {\n /**\n * SQL database.\n *\n */\n SQL,\n\n /**\n * Rest API.\n *\n */\n API,\n}\n"]}
1
+ {"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../../src/models/configuration.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AA6GlC;;;GAGG;AACH,MAAM,CAAN,IAAY,YAYX;AAZD,WAAY,YAAY;IACtB;;;OAGG;IACH,6CAAG,CAAA;IAEH;;;OAGG;IACH,6CAAG,CAAA;AACL,CAAC,EAZW,YAAY,KAAZ,YAAY,QAYvB","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Configuration for current environment.\n * @beta\n */\nexport interface Configuration {\n /**\n * Authentication related configuration.\n *\n * @readonly\n */\n readonly authentication?: AuthenticationConfiguration;\n\n /**\n * Configuration for resources.\n *\n * @readonly\n */\n readonly resources?: ResourceConfiguration[];\n}\n\n/**\n * Authentication related configuration.\n * @beta\n */\nexport interface AuthenticationConfiguration {\n /**\n * Hostname of AAD authority. Default value comes from M365_AUTHORITY_HOST environment variable.\n *\n * @readonly\n */\n readonly authorityHost?: string;\n\n /**\n * AAD tenant id, default value comes from M365_TENANT_ID environment variable.\n *\n * @readonly\n */\n readonly tenantId?: string;\n\n /**\n * The client (application) ID of an App Registration in the tenant, default value comes from M365_CLIENT_ID environment variable\n *\n * @readonly\n */\n readonly clientId?: string;\n\n /**\n * Secret string that the application uses when requesting a token. Only used in confidential client applications. Can be created in the Azure app registration portal. Default value comes from M365_CLIENT_SECRET environment variable\n *\n * @readonly\n */\n readonly clientSecret?: string;\n\n /**\n * The content of a PEM-encoded public/private key certificate.\n *\n * @readonly\n */\n readonly certificateContent?: string;\n\n /**\n * Endpoint of auth service provisioned by Teams Framework. Default value comes from SIMPLE_AUTH_ENDPOINT environment variable.\n *\n * @readonly\n */\n readonly simpleAuthEndpoint?: string;\n\n /**\n * Login page for Teams to redirect to. Default value comes from INITIATE_LOGIN_ENDPOINT environment variable.\n *\n * @readonly\n */\n readonly initiateLoginEndpoint?: string;\n\n /**\n * Application ID URI. Default value comes from M365_APPLICATION_ID_URI environment variable.\n */\n readonly applicationIdUri?: string;\n}\n\n/**\n * Configuration for resources.\n * @beta\n */\nexport interface ResourceConfiguration {\n /**\n * Resource type.\n *\n * @readonly\n */\n readonly type: ResourceType;\n\n /**\n * Resource name.\n *\n * @readonly\n */\n readonly name: string;\n\n /**\n * Config for the resource.\n *\n * @readonly\n */\n readonly properties: { [index: string]: any };\n}\n\n/**\n * Available resource type.\n * @beta\n */\nexport enum ResourceType {\n /**\n * SQL database.\n *\n */\n SQL,\n\n /**\n * Rest API.\n *\n */\n API,\n}\n"]}
@@ -3,6 +3,7 @@
3
3
  import { ErrorWithCode, ErrorCode } from "../core/errors";
4
4
  import jwt_decode from "jwt-decode";
5
5
  import { internalLogger } from "./logger";
6
+ import { createHash } from "crypto";
6
7
  /**
7
8
  * Parse jwt token payload
8
9
  *
@@ -89,4 +90,41 @@ export function validateScopesType(value) {
89
90
  internalLogger.error(errorMsg);
90
91
  throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);
91
92
  }
93
+ /**
94
+ * @internal
95
+ */
96
+ export function getScopesArray(scopes) {
97
+ const scopesArray = typeof scopes === "string" ? scopes.split(" ") : scopes;
98
+ return scopesArray.filter((x) => x !== null && x !== "");
99
+ }
100
+ /**
101
+ * @internal
102
+ */
103
+ export function getAuthority(authorityHost, tenantId) {
104
+ const normalizedAuthorityHost = authorityHost.replace(/\/+$/g, "");
105
+ return normalizedAuthorityHost + "/" + tenantId;
106
+ }
107
+ /**
108
+ * @internal
109
+ */
110
+ export function parseCertificate(certificateContent) {
111
+ if (!certificateContent) {
112
+ return undefined;
113
+ }
114
+ const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/;
115
+ const match = certificatePattern.exec(certificateContent);
116
+ if (!match) {
117
+ const errorMsg = "The certificate content does not contain a PEM-encoded certificate.";
118
+ internalLogger.error(errorMsg);
119
+ throw new ErrorWithCode(errorMsg, ErrorCode.InvalidCertificate);
120
+ }
121
+ const thumbprint = createHash("sha1")
122
+ .update(Buffer.from(match[3], "base64"))
123
+ .digest("hex")
124
+ .toUpperCase();
125
+ return {
126
+ thumbprint: thumbprint,
127
+ privateKey: certificateContent,
128
+ };
129
+ }
92
130
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/util/utils.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAClC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG1D,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,IAAI;QACF,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAqB,CAAC;QACvD,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;YAC9B,MAAM,IAAI,aAAa,CACrB,qDAAqD,EACrD,SAAS,CAAC,aAAa,CACxB,CAAC;SACH;QAED,OAAO,QAAQ,CAAC;KACjB;IAAC,OAAO,GAAQ,EAAE;QACjB,MAAM,QAAQ,GAAG,iDAAiD,GAAG,GAAG,CAAC,OAAO,CAAC;QACjF,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC/B,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;KAC5D;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,IAAI,CAAC,QAAQ,EAAE;QACb,MAAM,QAAQ,GAAG,yBAAyB,CAAC;QAC3C,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC/B,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAC;KAC/D;IACD,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAoC,CAAC;IAE1E,MAAM,QAAQ,GAAa;QACzB,WAAW,EAAE,WAAW,CAAC,IAAI;QAC7B,QAAQ,EAAE,WAAW,CAAC,GAAG;QACzB,iBAAiB,EAAE,EAAE;KACtB,CAAC;IAEF,IAAI,WAAW,CAAC,GAAG,KAAK,KAAK,EAAE;QAC7B,QAAQ,CAAC,iBAAiB,GAAI,WAA8B,CAAC,kBAAkB,CAAC;KACjF;SAAM,IAAI,WAAW,CAAC,GAAG,KAAK,KAAK,EAAE;QACpC,QAAQ,CAAC,iBAAiB,GAAI,WAA8B,CAAC,GAAG,CAAC;KAClE;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,GAAG,YAAsB;IACjE,MAAM,IAAI,GAAG,YAAY,CAAC;IAC1B,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,KAAK,EAAE,MAAM;QACpD,OAAO,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAU;IAC3C,SAAS;IACT,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,MAAM,EAAE;QACxD,OAAO;KACR;IAED,cAAc;IACd,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;QAC9C,OAAO;KACR;IAED,eAAe;IACf,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE;QAC/F,OAAO;KACR;IAED,MAAM,QAAQ,GAAG,oEAAoE,CAAC;IACtF,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAC;AAChE,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\nimport { ErrorWithCode, ErrorCode } from \"../core/errors\";\nimport { SSOTokenInfoBase, SSOTokenV1Info, SSOTokenV2Info } from \"../models/ssoTokenInfo\";\nimport { UserInfo } from \"../models/userinfo\";\nimport jwt_decode from \"jwt-decode\";\nimport { internalLogger } from \"./logger\";\n\n/**\n * Parse jwt token payload\n *\n * @param token\n *\n * @returns Payload object\n *\n * @internal\n */\nexport function parseJwt(token: string): SSOTokenInfoBase {\n try {\n const tokenObj = jwt_decode(token) as SSOTokenInfoBase;\n if (!tokenObj || !tokenObj.exp) {\n throw new ErrorWithCode(\n \"Decoded token is null or exp claim does not exists.\",\n ErrorCode.InternalError\n );\n }\n\n return tokenObj;\n } catch (err: any) {\n const errorMsg = \"Parse jwt token failed in node env with error: \" + err.message;\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);\n }\n}\n\n/**\n * @internal\n */\nexport function getUserInfoFromSsoToken(ssoToken: string): UserInfo {\n if (!ssoToken) {\n const errorMsg = \"SSO token is undefined.\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);\n }\n const tokenObject = parseJwt(ssoToken) as SSOTokenV1Info | SSOTokenV2Info;\n\n const userInfo: UserInfo = {\n displayName: tokenObject.name,\n objectId: tokenObject.oid,\n preferredUserName: \"\",\n };\n\n if (tokenObject.ver === \"2.0\") {\n userInfo.preferredUserName = (tokenObject as SSOTokenV2Info).preferred_username;\n } else if (tokenObject.ver === \"1.0\") {\n userInfo.preferredUserName = (tokenObject as SSOTokenV1Info).upn;\n }\n return userInfo;\n}\n\n/**\n * Format string template with replacements\n *\n * ```typescript\n * const template = \"{0} and {1} are fruit. {0} is my favorite one.\"\n * const formattedStr = formatString(template, \"apple\", \"pear\"); // formattedStr: \"apple and pear are fruit. apple is my favorite one.\"\n * ```\n *\n * @param str string template\n * @param replacements replacement string array\n * @returns Formatted string\n *\n * @internal\n */\nexport function formatString(str: string, ...replacements: string[]): string {\n const args = replacements;\n return str.replace(/{(\\d+)}/g, function (match, number) {\n return typeof args[number] != \"undefined\" ? args[number] : match;\n });\n}\n\n/**\n * @internal\n */\nexport function validateScopesType(value: any): void {\n // string\n if (typeof value === \"string\" || value instanceof String) {\n return;\n }\n\n // empty array\n if (Array.isArray(value) && value.length === 0) {\n return;\n }\n\n // string array\n if (Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === \"string\")) {\n return;\n }\n\n const errorMsg = \"The type of scopes is not valid, it must be string or string array\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/util/utils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\nimport { ErrorWithCode, ErrorCode } from \"../core/errors\";\nimport { SSOTokenInfoBase, SSOTokenV1Info, SSOTokenV2Info } from \"../models/ssoTokenInfo\";\nimport { UserInfo } from \"../models/userinfo\";\nimport jwt_decode from \"jwt-decode\";\nimport { internalLogger } from \"./logger\";\nimport { createHash } from \"crypto\";\n\n/**\n * Parse jwt token payload\n *\n * @param token\n *\n * @returns Payload object\n *\n * @internal\n */\nexport function parseJwt(token: string): SSOTokenInfoBase {\n try {\n const tokenObj = jwt_decode(token) as SSOTokenInfoBase;\n if (!tokenObj || !tokenObj.exp) {\n throw new ErrorWithCode(\n \"Decoded token is null or exp claim does not exists.\",\n ErrorCode.InternalError\n );\n }\n\n return tokenObj;\n } catch (err: any) {\n const errorMsg = \"Parse jwt token failed in node env with error: \" + err.message;\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InternalError);\n }\n}\n\n/**\n * @internal\n */\nexport function getUserInfoFromSsoToken(ssoToken: string): UserInfo {\n if (!ssoToken) {\n const errorMsg = \"SSO token is undefined.\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);\n }\n const tokenObject = parseJwt(ssoToken) as SSOTokenV1Info | SSOTokenV2Info;\n\n const userInfo: UserInfo = {\n displayName: tokenObject.name,\n objectId: tokenObject.oid,\n preferredUserName: \"\",\n };\n\n if (tokenObject.ver === \"2.0\") {\n userInfo.preferredUserName = (tokenObject as SSOTokenV2Info).preferred_username;\n } else if (tokenObject.ver === \"1.0\") {\n userInfo.preferredUserName = (tokenObject as SSOTokenV1Info).upn;\n }\n return userInfo;\n}\n\n/**\n * Format string template with replacements\n *\n * ```typescript\n * const template = \"{0} and {1} are fruit. {0} is my favorite one.\"\n * const formattedStr = formatString(template, \"apple\", \"pear\"); // formattedStr: \"apple and pear are fruit. apple is my favorite one.\"\n * ```\n *\n * @param str string template\n * @param replacements replacement string array\n * @returns Formatted string\n *\n * @internal\n */\nexport function formatString(str: string, ...replacements: string[]): string {\n const args = replacements;\n return str.replace(/{(\\d+)}/g, function (match, number) {\n return typeof args[number] != \"undefined\" ? args[number] : match;\n });\n}\n\n/**\n * @internal\n */\nexport function validateScopesType(value: any): void {\n // string\n if (typeof value === \"string\" || value instanceof String) {\n return;\n }\n\n // empty array\n if (Array.isArray(value) && value.length === 0) {\n return;\n }\n\n // string array\n if (Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === \"string\")) {\n return;\n }\n\n const errorMsg = \"The type of scopes is not valid, it must be string or string array\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidParameter);\n}\n\n/**\n * @internal\n */\nexport function getScopesArray(scopes: string | string[]): string[] {\n const scopesArray: string[] = typeof scopes === \"string\" ? scopes.split(\" \") : scopes;\n return scopesArray.filter((x) => x !== null && x !== \"\");\n}\n\n/**\n * @internal\n */\nexport function getAuthority(authorityHost: string, tenantId: string): string {\n const normalizedAuthorityHost = authorityHost.replace(/\\/+$/g, \"\");\n return normalizedAuthorityHost + \"/\" + tenantId;\n}\n\n/**\n * @internal\n */\nexport function parseCertificate(\n certificateContent: string | undefined\n): ClientCertificate | undefined {\n if (!certificateContent) {\n return undefined;\n }\n\n const certificatePattern =\n /(-+BEGIN CERTIFICATE-+)(\\n\\r?|\\r\\n?)([A-Za-z0-9+/\\n\\r]+=*)(\\n\\r?|\\r\\n?)(-+END CERTIFICATE-+)/;\n const match = certificatePattern.exec(certificateContent);\n if (!match) {\n const errorMsg = \"The certificate content does not contain a PEM-encoded certificate.\";\n internalLogger.error(errorMsg);\n throw new ErrorWithCode(errorMsg, ErrorCode.InvalidCertificate);\n }\n const thumbprint = createHash(\"sha1\")\n .update(Buffer.from(match[3], \"base64\"))\n .digest(\"hex\")\n .toUpperCase();\n\n return {\n thumbprint: thumbprint,\n privateKey: certificateContent,\n };\n}\n\n/**\n * @internal\n */\nexport interface ClientCertificate {\n thumbprint: string;\n privateKey: string;\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import { ConfidentialClientApplication } from "@azure/msal-node";
2
+ import { getAuthority, parseCertificate } from "./utils";
3
+ /**
4
+ * @internal
5
+ */
6
+ export function createConfidentialClientApplication(authentication) {
7
+ const authority = getAuthority(authentication.authorityHost, authentication.tenantId);
8
+ const clientCertificate = parseCertificate(authentication.certificateContent);
9
+ const auth = {
10
+ clientId: authentication.clientId,
11
+ authority: authority,
12
+ };
13
+ if (clientCertificate) {
14
+ auth.clientCertificate = clientCertificate;
15
+ }
16
+ else {
17
+ auth.clientSecret = authentication.clientSecret;
18
+ }
19
+ return new ConfidentialClientApplication({
20
+ auth,
21
+ });
22
+ }
23
+ //# sourceMappingURL=utils.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.node.js","sourceRoot":"","sources":["../../../src/util/utils.node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAmB,MAAM,kBAAkB,CAAC;AAElF,OAAO,EAAqB,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE5E;;GAEG;AACH,MAAM,UAAU,mCAAmC,CACjD,cAA2C;IAE3C,MAAM,SAAS,GAAG,YAAY,CAAC,cAAc,CAAC,aAAc,EAAE,cAAc,CAAC,QAAS,CAAC,CAAC;IACxF,MAAM,iBAAiB,GAAkC,gBAAgB,CACvE,cAAc,CAAC,kBAAkB,CAClC,CAAC;IAEF,MAAM,IAAI,GAAoB;QAC5B,QAAQ,EAAE,cAAc,CAAC,QAAS;QAClC,SAAS,EAAE,SAAS;KACrB,CAAC;IAEF,IAAI,iBAAiB,EAAE;QACrB,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;KAC5C;SAAM;QACL,IAAI,CAAC,YAAY,GAAG,cAAc,CAAC,YAAY,CAAC;KACjD;IAED,OAAO,IAAI,6BAA6B,CAAC;QACvC,IAAI;KACL,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { ConfidentialClientApplication, NodeAuthOptions } from \"@azure/msal-node\";\nimport { AuthenticationConfiguration } from \"../models/configuration\";\nimport { ClientCertificate, getAuthority, parseCertificate } from \"./utils\";\n\n/**\n * @internal\n */\nexport function createConfidentialClientApplication(\n authentication: AuthenticationConfiguration\n): ConfidentialClientApplication {\n const authority = getAuthority(authentication.authorityHost!, authentication.tenantId!);\n const clientCertificate: ClientCertificate | undefined = parseCertificate(\n authentication.certificateContent\n );\n\n const auth: NodeAuthOptions = {\n clientId: authentication.clientId!,\n authority: authority,\n };\n\n if (clientCertificate) {\n auth.clientCertificate = clientCertificate;\n } else {\n auth.clientSecret = authentication.clientSecret;\n }\n\n return new ConfidentialClientApplication({\n auth,\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/teamsfx",
3
- "version": "0.2.8-alpha.9ef5081b.0",
3
+ "version": "0.2.8-alpha.b4c7969d.0",
4
4
  "description": "Microsoft Teams Framework for Node.js and browser.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist-esm/src/index.js",
@@ -111,6 +111,7 @@
111
111
  "cross-env": "^7.0.2",
112
112
  "dotenv": "^8.2.0",
113
113
  "eslint": "^7.15.0",
114
+ "eslint-plugin-prettier": "^4.0.0",
114
115
  "fs-extra": "^9.1.0",
115
116
  "got": "^11.8.2",
116
117
  "isomorphic-fetch": "^3.0.0",
@@ -131,6 +132,7 @@
131
132
  "mocked-env": "^1.3.2",
132
133
  "nyc": "^15.1.0",
133
134
  "playwright-chromium": "^1.11.1",
135
+ "prettier": "^2.4.1",
134
136
  "puppeteer": "^9.1.1",
135
137
  "rimraf": "^3.0.0",
136
138
  "rollup": "^2.41.0",
@@ -144,16 +146,13 @@
144
146
  "url-join": "^4.0.1",
145
147
  "util": "^0.12.1"
146
148
  },
147
- "gitHead": "a99874ab6e77627c7cfcea37d3d48a2d959651a3",
149
+ "gitHead": "8caa6b472ca4f023fdffa0d5100df07f2e6698ef",
148
150
  "publishConfig": {
149
151
  "access": "public"
150
152
  },
151
153
  "lint-staged": {
152
154
  "*.{js,jsx,css,ts,tsx}": [
153
155
  "npx eslint --cache --fix --quiet"
154
- ],
155
- "*": [
156
- "npx prettier --config .prettierrc.js --ignore-unknown --write --ignore-path .prettierignore "
157
156
  ]
158
157
  }
159
158
  }
@@ -38,6 +38,12 @@ export declare interface AuthenticationConfiguration {
38
38
  * @readonly
39
39
  */
40
40
  readonly clientSecret?: string;
41
+ /**
42
+ * The content of a PEM-encoded public/private key certificate.
43
+ *
44
+ * @readonly
45
+ */
46
+ readonly certificateContent?: string;
41
47
  /**
42
48
  * Endpoint of auth service provisioned by Teams Framework. Default value comes from SIMPLE_AUTH_ENDPOINT environment variable.
43
49
  *
@@ -203,6 +209,10 @@ export declare enum ErrorCode {
203
209
  * Invalid configuration error.
204
210
  */
205
211
  InvalidConfiguration = "InvalidConfiguration",
212
+ /**
213
+ * Invalid certificate error.
214
+ */
215
+ InvalidCertificate = "InvalidCertificate",
206
216
  /**
207
217
  * Internal error.
208
218
  */
@@ -376,7 +386,7 @@ export declare enum LogLevel {
376
386
  * @beta
377
387
  */
378
388
  export declare class M365TenantCredential implements TokenCredential {
379
- private readonly clientSecretCredential;
389
+ private readonly msalClient;
380
390
  /**
381
391
  * Constructor of M365TenantCredential.
382
392
  *
@@ -484,7 +494,7 @@ export declare class OnBehalfOfUserCredential implements TokenCredential {
484
494
  *
485
495
  * @param {string} ssoToken - User token provided by Teams SSO feature.
486
496
  *
487
- * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, authority host or tenant id is not found in config.
497
+ * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
488
498
  * @throws {@link ErrorCode|InternalError} when SSO token is not valid.
489
499
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
490
500
  *