@chat-adapter/teams 4.16.1 → 4.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -76,17 +76,37 @@ declare class TeamsFormatConverter extends BaseFormatConverter {
76
76
  private tableToGfm;
77
77
  }
78
78
 
79
+ /** Certificate-based authentication config */
80
+ interface TeamsAuthCertificate {
81
+ /** PEM-encoded certificate private key */
82
+ certificatePrivateKey: string;
83
+ /** Hex-encoded certificate thumbprint (optional when x5c is provided) */
84
+ certificateThumbprint?: string;
85
+ /** Public certificate for subject-name validation (optional) */
86
+ x5c?: string;
87
+ }
88
+ /** Federated (workload identity) authentication config */
89
+ interface TeamsAuthFederated {
90
+ /** Audience for the federated credential (defaults to api://AzureADTokenExchange) */
91
+ clientAudience?: string;
92
+ /** Client ID for the managed identity assigned to the bot */
93
+ clientId: string;
94
+ }
79
95
  interface TeamsAdapterConfig {
80
- /** Microsoft App ID */
81
- appId: string;
82
- /** Microsoft App Password */
83
- appPassword: string;
84
- /** Microsoft App Tenant ID */
96
+ /** Microsoft App ID. Defaults to TEAMS_APP_ID env var. */
97
+ appId?: string;
98
+ /** Microsoft App Password. Defaults to TEAMS_APP_PASSWORD env var. */
99
+ appPassword?: string;
100
+ /** Microsoft App Tenant ID. Defaults to TEAMS_APP_TENANT_ID env var. */
85
101
  appTenantId?: string;
86
102
  /** Microsoft App Type */
87
103
  appType?: "MultiTenant" | "SingleTenant";
88
- /** Logger instance for error reporting */
89
- logger: Logger;
104
+ /** Certificate-based authentication */
105
+ certificate?: TeamsAuthCertificate;
106
+ /** Federated (workload identity) authentication */
107
+ federated?: TeamsAuthFederated;
108
+ /** Logger instance for error reporting. Defaults to ConsoleLogger. */
109
+ logger?: Logger;
90
110
  /** Override bot username (optional) */
91
111
  userName?: string;
92
112
  }
@@ -106,7 +126,7 @@ declare class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {
106
126
  private readonly logger;
107
127
  private readonly formatConverter;
108
128
  private readonly config;
109
- constructor(config: TeamsAdapterConfig);
129
+ constructor(config?: TeamsAdapterConfig);
110
130
  initialize(chat: ChatInstance): Promise<void>;
111
131
  handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;
112
132
  private handleTurn;
@@ -225,6 +245,6 @@ declare class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {
225
245
  */
226
246
  private handleTeamsError;
227
247
  }
228
- declare function createTeamsAdapter(config?: Partial<TeamsAdapterConfig>): TeamsAdapter;
248
+ declare function createTeamsAdapter(config?: TeamsAdapterConfig): TeamsAdapter;
229
249
 
230
- export { TeamsAdapter, type TeamsAdapterConfig, TeamsFormatConverter, type TeamsThreadId, cardToAdaptiveCard, cardToFallbackText, createTeamsAdapter };
250
+ export { TeamsAdapter, type TeamsAdapterConfig, type TeamsAuthCertificate, type TeamsAuthFederated, TeamsFormatConverter, type TeamsThreadId, cardToAdaptiveCard, cardToFallbackText, createTeamsAdapter };
package/dist/index.js CHANGED
@@ -671,7 +671,11 @@ var require_azureTokenCredentials = __commonJS({
671
671
  });
672
672
 
673
673
  // src/index.ts
674
- import { ClientSecretCredential } from "@azure/identity";
674
+ import {
675
+ ClientCertificateCredential,
676
+ ClientSecretCredential,
677
+ DefaultAzureCredential
678
+ } from "@azure/identity";
675
679
 
676
680
  // ../../node_modules/.pnpm/@microsoft+microsoft-graph-client@3.0.7_@azure+identity@4.13.0/node_modules/@microsoft/microsoft-graph-client/lib/es/src/content/BatchRequestContent.js
677
681
  init_tslib_es6();
@@ -3563,6 +3567,10 @@ import {
3563
3567
  ConfigurationBotFrameworkAuthentication,
3564
3568
  TeamsInfo
3565
3569
  } from "botbuilder";
3570
+ import {
3571
+ CertificateServiceClientCredentialsFactory,
3572
+ FederatedServiceClientCredentialsFactory
3573
+ } from "botframework-connector";
3566
3574
  import {
3567
3575
  AdapterRateLimitError,
3568
3576
  AuthenticationError,
@@ -3882,8 +3890,22 @@ var TeamsFormatConverter = class extends BaseFormatConverter {
3882
3890
  );
3883
3891
  markdown = markdown.replace(/<code>([^<]+)<\/code>/gi, "`$1`");
3884
3892
  markdown = markdown.replace(/<pre>([^<]+)<\/pre>/gi, "```\n$1\n```");
3885
- markdown = markdown.replace(/<[^>]+>/g, "");
3886
- markdown = markdown.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
3893
+ let prev;
3894
+ do {
3895
+ prev = markdown;
3896
+ markdown = markdown.replace(/<[^>]+>/g, "");
3897
+ } while (markdown !== prev);
3898
+ const entityMap = {
3899
+ "&lt;": "<",
3900
+ "&gt;": ">",
3901
+ "&amp;": "&",
3902
+ "&quot;": '"',
3903
+ "&#39;": "'"
3904
+ };
3905
+ markdown = markdown.replace(
3906
+ /&(?:lt|gt|amp|quot|#39);/g,
3907
+ (match) => entityMap[match] ?? match
3908
+ );
3887
3909
  return parseMarkdown(markdown);
3888
3910
  }
3889
3911
  nodeToTeams(node) {
@@ -3981,31 +4003,110 @@ var TeamsAdapter = class {
3981
4003
  logger;
3982
4004
  formatConverter = new TeamsFormatConverter();
3983
4005
  config;
3984
- constructor(config) {
3985
- this.config = config;
3986
- this.logger = config.logger;
4006
+ constructor(config = {}) {
4007
+ const appId = config.appId ?? process.env.TEAMS_APP_ID;
4008
+ if (!appId) {
4009
+ throw new ValidationError(
4010
+ "teams",
4011
+ "appId is required. Set TEAMS_APP_ID or provide it in config."
4012
+ );
4013
+ }
4014
+ const hasExplicitAuth = config.appPassword || config.certificate || config.federated;
4015
+ const appPassword = hasExplicitAuth ? config.appPassword : config.appPassword ?? process.env.TEAMS_APP_PASSWORD;
4016
+ const appTenantId = config.appTenantId ?? process.env.TEAMS_APP_TENANT_ID;
4017
+ this.config = {
4018
+ ...config,
4019
+ appId,
4020
+ appPassword,
4021
+ appTenantId
4022
+ };
4023
+ this.logger = config.logger ?? new ConsoleLogger("info").child("teams");
3987
4024
  this.userName = config.userName || "bot";
3988
- if (config.appType === "SingleTenant" && !config.appTenantId) {
4025
+ const authMethodCount = [
4026
+ appPassword,
4027
+ config.certificate,
4028
+ config.federated
4029
+ ].filter(Boolean).length;
4030
+ if (authMethodCount === 0) {
4031
+ throw new ValidationError(
4032
+ "teams",
4033
+ "One of appPassword, certificate, or federated must be provided"
4034
+ );
4035
+ }
4036
+ if (authMethodCount > 1) {
4037
+ throw new ValidationError(
4038
+ "teams",
4039
+ "Only one of appPassword, certificate, or federated can be provided"
4040
+ );
4041
+ }
4042
+ if (config.appType === "SingleTenant" && !appTenantId) {
3989
4043
  throw new ValidationError(
3990
4044
  "teams",
3991
4045
  "appTenantId is required for SingleTenant app type"
3992
4046
  );
3993
4047
  }
3994
- const auth = new ConfigurationBotFrameworkAuthentication({
3995
- MicrosoftAppId: config.appId,
3996
- MicrosoftAppPassword: config.appPassword,
4048
+ const botFrameworkConfig = {
4049
+ MicrosoftAppId: appId,
3997
4050
  MicrosoftAppType: config.appType || "MultiTenant",
3998
- MicrosoftAppTenantId: config.appType === "SingleTenant" ? config.appTenantId : void 0
3999
- });
4000
- this.botAdapter = new ServerlessCloudAdapter(auth);
4001
- if (config.appTenantId) {
4002
- const credential = new ClientSecretCredential(
4003
- config.appTenantId,
4004
- config.appId,
4005
- config.appPassword
4051
+ MicrosoftAppTenantId: config.appType === "SingleTenant" ? appTenantId : void 0
4052
+ };
4053
+ let credentialsFactory;
4054
+ let graphCredential;
4055
+ if (config.certificate) {
4056
+ const { certificatePrivateKey, certificateThumbprint, x5c } = config.certificate;
4057
+ if (x5c) {
4058
+ credentialsFactory = new CertificateServiceClientCredentialsFactory(
4059
+ appId,
4060
+ x5c,
4061
+ certificatePrivateKey,
4062
+ appTenantId
4063
+ );
4064
+ } else if (certificateThumbprint) {
4065
+ credentialsFactory = new CertificateServiceClientCredentialsFactory(
4066
+ appId,
4067
+ certificateThumbprint,
4068
+ certificatePrivateKey,
4069
+ appTenantId
4070
+ );
4071
+ } else {
4072
+ throw new ValidationError(
4073
+ "teams",
4074
+ "Certificate auth requires either certificateThumbprint or x5c"
4075
+ );
4076
+ }
4077
+ if (appTenantId) {
4078
+ graphCredential = new ClientCertificateCredential(appTenantId, appId, {
4079
+ certificate: certificatePrivateKey
4080
+ });
4081
+ }
4082
+ } else if (config.federated) {
4083
+ credentialsFactory = new FederatedServiceClientCredentialsFactory(
4084
+ appId,
4085
+ config.federated.clientId,
4086
+ appTenantId,
4087
+ config.federated.clientAudience
4088
+ );
4089
+ if (appTenantId) {
4090
+ graphCredential = new DefaultAzureCredential();
4091
+ }
4092
+ } else if (appPassword && appTenantId) {
4093
+ graphCredential = new ClientSecretCredential(
4094
+ appTenantId,
4095
+ appId,
4096
+ appPassword
4006
4097
  );
4098
+ }
4099
+ const auth = new ConfigurationBotFrameworkAuthentication(
4100
+ {
4101
+ ...botFrameworkConfig,
4102
+ ...appPassword ? { MicrosoftAppPassword: appPassword } : {}
4103
+ },
4104
+ credentialsFactory
4105
+ );
4106
+ this.botAdapter = new ServerlessCloudAdapter(auth);
4107
+ if (graphCredential) {
4007
4108
  const authProvider = new import_azureTokenCredentials.TokenCredentialAuthenticationProvider(
4008
- credential,
4109
+ graphCredential,
4009
4110
  {
4010
4111
  scopes: ["https://graph.microsoft.com/.default"]
4011
4112
  }
@@ -5000,7 +5101,13 @@ var TeamsAdapter = class {
5000
5101
  }
5001
5102
  let text = "";
5002
5103
  if (msg.body?.content) {
5003
- text = msg.body.content.replace(/<[^>]*>/g, "").trim();
5104
+ let stripped = msg.body.content;
5105
+ let prev;
5106
+ do {
5107
+ prev = stripped;
5108
+ stripped = stripped.replace(/<[^>]*>/g, "");
5109
+ } while (stripped !== prev);
5110
+ text = stripped.trim();
5004
5111
  }
5005
5112
  if (!text && msg.attachments?.length) {
5006
5113
  for (const att of msg.attachments) {
@@ -5589,29 +5696,7 @@ var TeamsAdapter = class {
5589
5696
  }
5590
5697
  };
5591
5698
  function createTeamsAdapter(config) {
5592
- const appId = config?.appId ?? process.env.TEAMS_APP_ID;
5593
- if (!appId) {
5594
- throw new ValidationError(
5595
- "teams",
5596
- "appId is required. Set TEAMS_APP_ID or provide it in config."
5597
- );
5598
- }
5599
- const appPassword = config?.appPassword ?? process.env.TEAMS_APP_PASSWORD;
5600
- if (!appPassword) {
5601
- throw new ValidationError(
5602
- "teams",
5603
- "appPassword is required. Set TEAMS_APP_PASSWORD or provide it in config."
5604
- );
5605
- }
5606
- const resolved = {
5607
- appId,
5608
- appPassword,
5609
- appTenantId: config?.appTenantId ?? process.env.TEAMS_APP_TENANT_ID,
5610
- appType: config?.appType,
5611
- logger: config?.logger ?? new ConsoleLogger("info").child("teams"),
5612
- userName: config?.userName
5613
- };
5614
- return new TeamsAdapter(resolved);
5699
+ return new TeamsAdapter(config ?? {});
5615
5700
  }
5616
5701
  export {
5617
5702
  TeamsAdapter,