@microsoft/agents-hosting 1.10.0-beta.8.g158a2c07ad → 1.10.1

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.
Files changed (169) hide show
  1. package/README.md +41 -2
  2. package/dist/package.json +3 -3
  3. package/dist/src/activityHandler.js +6 -3
  4. package/dist/src/activityHandler.js.map +1 -1
  5. package/dist/src/agent-client/agentResponseHandler.d.ts +7 -0
  6. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  7. package/dist/src/agent-client/createAgentResponseHandler.d.ts +2 -0
  8. package/dist/src/agent-client/createAgentResponseHandler.js.map +1 -1
  9. package/dist/src/app/adaptiveCards/adaptiveCardsActions.d.ts +1 -0
  10. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +1 -0
  11. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
  12. package/dist/src/app/attachmentDownloadHttpClient.d.ts +13 -0
  13. package/dist/src/app/attachmentDownloadHttpClient.js +80 -0
  14. package/dist/src/app/attachmentDownloadHttpClient.js.map +1 -0
  15. package/dist/src/app/attachmentDownloader.d.ts +2 -0
  16. package/dist/src/app/attachmentDownloader.js +6 -11
  17. package/dist/src/app/attachmentDownloader.js.map +1 -1
  18. package/dist/src/app/auth/authorization.d.ts +58 -0
  19. package/dist/src/app/auth/authorization.js.map +1 -1
  20. package/dist/src/app/proactive/conversation.d.ts +18 -0
  21. package/dist/src/app/proactive/conversation.js +6 -0
  22. package/dist/src/app/proactive/conversation.js.map +1 -1
  23. package/dist/src/app/proactive/conversationBuilder.js +2 -1
  24. package/dist/src/app/proactive/conversationBuilder.js.map +1 -1
  25. package/dist/src/app/proactive/conversationReferenceBuilder.js +8 -4
  26. package/dist/src/app/proactive/conversationReferenceBuilder.js.map +1 -1
  27. package/dist/src/app/proactive/createConversationOptionsBuilder.d.ts +1 -0
  28. package/dist/src/app/proactive/createConversationOptionsBuilder.js +2 -1
  29. package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -1
  30. package/dist/src/app/proactive/proactive.d.ts +6 -0
  31. package/dist/src/app/proactive/proactive.js +32 -1
  32. package/dist/src/app/proactive/proactive.js.map +1 -1
  33. package/dist/src/app/routeList.d.ts +21 -0
  34. package/dist/src/app/routeList.js +27 -3
  35. package/dist/src/app/routeList.js.map +1 -1
  36. package/dist/src/app/teamsAttachmentDownloader.d.ts +8 -0
  37. package/dist/src/app/teamsAttachmentDownloader.js +17 -12
  38. package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
  39. package/dist/src/auth/authConfiguration.d.ts +1 -0
  40. package/dist/src/auth/authConfiguration.js.map +1 -1
  41. package/dist/src/auth/authConstants.d.ts +7 -0
  42. package/dist/src/auth/authConstants.js +7 -0
  43. package/dist/src/auth/authConstants.js.map +1 -1
  44. package/dist/src/auth/authProvider.d.ts +36 -0
  45. package/dist/src/auth/connectionManager.js +2 -1
  46. package/dist/src/auth/connectionManager.js.map +1 -1
  47. package/dist/src/auth/connections.d.ts +1 -0
  48. package/dist/src/auth/msal/msalConnectionManager.d.ts +7 -0
  49. package/dist/src/auth/msal/msalConnectionManager.js +7 -0
  50. package/dist/src/auth/msal/msalConnectionManager.js.map +1 -1
  51. package/dist/src/auth/msal/msalTokenProvider.d.ts +44 -0
  52. package/dist/src/auth/msal/msalTokenProvider.js +43 -0
  53. package/dist/src/auth/msal/msalTokenProvider.js.map +1 -1
  54. package/dist/src/auth/settings.d.ts +28 -0
  55. package/dist/src/auth/settings.js +23 -0
  56. package/dist/src/auth/settings.js.map +1 -1
  57. package/dist/src/auth/sidecar/sidecarAuthProvider.d.ts +3 -0
  58. package/dist/src/auth/sidecar/sidecarAuthProvider.js.map +1 -1
  59. package/dist/src/auth/sidecar/sidecarHttpClient.d.ts +0 -8
  60. package/dist/src/auth/sidecar/sidecarHttpClient.js +2 -68
  61. package/dist/src/auth/sidecar/sidecarHttpClient.js.map +1 -1
  62. package/dist/src/cards/taskModuleAction.d.ts +6 -0
  63. package/dist/src/cards/taskModuleAction.js +6 -0
  64. package/dist/src/cards/taskModuleAction.js.map +1 -1
  65. package/dist/src/cloudAdapter.js +16 -8
  66. package/dist/src/cloudAdapter.js.map +1 -1
  67. package/dist/src/configuration/configuration.d.ts +1 -0
  68. package/dist/src/configuration/configuration.js +2 -1
  69. package/dist/src/configuration/configuration.js.map +1 -1
  70. package/dist/src/configuration/environmentConfiguration.js +1 -1
  71. package/dist/src/configuration/environmentConfiguration.js.map +1 -1
  72. package/dist/src/connector-client/connectorClient.d.ts +8 -0
  73. package/dist/src/connector-client/connectorClient.js +8 -0
  74. package/dist/src/connector-client/connectorClient.js.map +1 -1
  75. package/dist/src/createCloudAdapter.d.ts +2 -0
  76. package/dist/src/createCloudAdapter.js.map +1 -1
  77. package/dist/src/errorHelper.d.ts +1 -0
  78. package/dist/src/errorHelper.js +9 -0
  79. package/dist/src/errorHelper.js.map +1 -1
  80. package/dist/src/headerPropagation.d.ts +38 -0
  81. package/dist/src/headerPropagation.js +33 -0
  82. package/dist/src/headerPropagation.js.map +1 -1
  83. package/dist/src/httpClient.d.ts +58 -0
  84. package/dist/src/httpClient.js +40 -0
  85. package/dist/src/httpClient.js.map +1 -1
  86. package/dist/src/interfaces/webResponse.d.ts +7 -0
  87. package/dist/src/middlewareSet.d.ts +6 -0
  88. package/dist/src/middlewareSet.js.map +1 -1
  89. package/dist/src/oauth/userTokenClient.d.ts +6 -0
  90. package/dist/src/oauth/userTokenClient.js +12 -0
  91. package/dist/src/oauth/userTokenClient.js.map +1 -1
  92. package/dist/src/outboundHostValidator.d.ts +33 -1
  93. package/dist/src/outboundHostValidator.js +60 -25
  94. package/dist/src/outboundHostValidator.js.map +1 -1
  95. package/dist/src/storage/fileStorage.d.ts +26 -0
  96. package/dist/src/storage/fileStorage.js +35 -6
  97. package/dist/src/storage/fileStorage.js.map +1 -1
  98. package/dist/src/storage/memoryStorage.d.ts +34 -0
  99. package/dist/src/storage/memoryStorage.js +41 -4
  100. package/dist/src/storage/memoryStorage.js.map +1 -1
  101. package/dist/src/storage/storage.d.ts +20 -0
  102. package/dist/src/storage/storage.js +7 -0
  103. package/dist/src/storage/storage.js.map +1 -1
  104. package/dist/src/storage/storageCompatibility.js +12 -6
  105. package/dist/src/storage/storageCompatibility.js.map +1 -1
  106. package/dist/src/storage/storageExpiry.d.ts +7 -0
  107. package/dist/src/storage/storageExpiry.js +7 -0
  108. package/dist/src/storage/storageExpiry.js.map +1 -1
  109. package/dist/src/turnContext.d.ts +10 -0
  110. package/dist/src/turnContext.js +5 -0
  111. package/dist/src/turnContext.js.map +1 -1
  112. package/dist/src/utils/env.js +22 -11
  113. package/dist/src/utils/env.js.map +1 -1
  114. package/dist/src/utils/ipAddress.d.ts +6 -0
  115. package/dist/src/utils/ipAddress.js +64 -0
  116. package/dist/src/utils/ipAddress.js.map +1 -0
  117. package/dist/src/utils/outboundUrl.d.ts +7 -0
  118. package/dist/src/utils/outboundUrl.js +52 -0
  119. package/dist/src/utils/outboundUrl.js.map +1 -0
  120. package/dist/src/utils.js +2 -1
  121. package/dist/src/utils.js.map +1 -1
  122. package/package.json +3 -3
  123. package/src/activityHandler.ts +9 -3
  124. package/src/agent-client/agentResponseHandler.ts +7 -0
  125. package/src/agent-client/createAgentResponseHandler.ts +2 -0
  126. package/src/app/adaptiveCards/adaptiveCardsActions.ts +1 -0
  127. package/src/app/attachmentDownloadHttpClient.ts +80 -0
  128. package/src/app/attachmentDownloader.ts +6 -9
  129. package/src/app/auth/authorization.ts +58 -0
  130. package/src/app/proactive/conversation.ts +24 -0
  131. package/src/app/proactive/conversationBuilder.ts +3 -1
  132. package/src/app/proactive/conversationReferenceBuilder.ts +12 -4
  133. package/src/app/proactive/createConversationOptionsBuilder.ts +5 -1
  134. package/src/app/proactive/proactive.ts +33 -1
  135. package/src/app/routeList.ts +30 -3
  136. package/src/app/teamsAttachmentDownloader.ts +17 -10
  137. package/src/auth/authConfiguration.ts +1 -0
  138. package/src/auth/authConstants.ts +7 -0
  139. package/src/auth/authProvider.ts +36 -0
  140. package/src/auth/connectionManager.ts +3 -1
  141. package/src/auth/connections.ts +1 -0
  142. package/src/auth/msal/msalConnectionManager.ts +7 -0
  143. package/src/auth/msal/msalTokenProvider.ts +59 -0
  144. package/src/auth/settings.ts +28 -0
  145. package/src/auth/sidecar/sidecarAuthProvider.ts +4 -0
  146. package/src/auth/sidecar/sidecarHttpClient.ts +2 -62
  147. package/src/cards/taskModuleAction.ts +6 -0
  148. package/src/cloudAdapter.ts +30 -10
  149. package/src/configuration/configuration.ts +4 -2
  150. package/src/configuration/environmentConfiguration.ts +7 -1
  151. package/src/connector-client/connectorClient.ts +8 -0
  152. package/src/createCloudAdapter.ts +2 -0
  153. package/src/errorHelper.ts +11 -0
  154. package/src/headerPropagation.ts +38 -0
  155. package/src/httpClient.ts +58 -0
  156. package/src/interfaces/webResponse.ts +7 -0
  157. package/src/middlewareSet.ts +6 -0
  158. package/src/oauth/userTokenClient.ts +13 -0
  159. package/src/outboundHostValidator.ts +84 -23
  160. package/src/storage/fileStorage.ts +46 -6
  161. package/src/storage/memoryStorage.ts +45 -4
  162. package/src/storage/storage.ts +20 -0
  163. package/src/storage/storageCompatibility.ts +18 -6
  164. package/src/storage/storageExpiry.ts +7 -0
  165. package/src/turnContext.ts +11 -0
  166. package/src/utils/env.ts +33 -11
  167. package/src/utils/ipAddress.ts +63 -0
  168. package/src/utils/outboundUrl.ts +50 -0
  169. package/src/utils.ts +3 -1
@@ -12,17 +12,26 @@ import { TurnState } from './turnState'
12
12
  import { HttpClient } from '../httpClient'
13
13
  import { z } from 'zod'
14
14
  import { createOutboundHostValidator, OutboundUrlPolicy } from '../outboundHostValidator'
15
+ import { AttachmentDownloadHttpClient } from './attachmentDownloadHttpClient'
15
16
 
16
17
  const logger = debug('agents:M365AttachmentDownloader')
17
18
 
18
19
  /**
19
20
  * Downloads attachments from Teams and M365 using the bots access token.
21
+ * Enabled outbound validation requires HTTPS and validates addresses at every redirect.
22
+ * Initial request credentials are not forwarded to redirects when validation is enabled.
20
23
  */
21
24
  export class M365AttachmentDownloader<TState extends TurnState = TurnState> implements InputFileDownloader<TState> {
22
25
  private _httpClient: HttpClient
23
26
  private _stateKey: string
24
27
  private readonly _hostValidator: OutboundUrlPolicy
25
28
 
29
+ /**
30
+ * Creates an attachment downloader.
31
+ *
32
+ * @param stateKey - Turn-state key used by {@link downloadAndStoreFiles}.
33
+ * @param outboundHostValidator - Policy used to validate attachment download URLs.
34
+ */
26
35
  public constructor (stateKey: string = 'inputFiles', outboundHostValidator?: OutboundUrlPolicy) {
27
36
  this._httpClient = new HttpClient()
28
37
  this._stateKey = stateKey
@@ -46,11 +55,11 @@ export class M365AttachmentDownloader<TState extends TurnState = TurnState> impl
46
55
  }
47
56
 
48
57
  const connectorClient : ConnectorClient = context.turnState.get<ConnectorClient>(context.adapter.ConnectorClientKey)
49
- this._httpClient.defaultHeaders = { ...connectorClient.httpClient.defaultHeaders }
58
+ const headers = { ...connectorClient.httpClient.defaultHeaders }
50
59
 
51
60
  const files: InputFile[] = []
52
61
  for (const attachment of attachments) {
53
- const file = await this.downloadFile(attachment)
62
+ const file = await this.downloadFile(attachment, headers)
54
63
  if (file) {
55
64
  files.push(file)
56
65
  }
@@ -64,7 +73,7 @@ export class M365AttachmentDownloader<TState extends TurnState = TurnState> impl
64
73
  * @param {Attachment} attachment - Attachment to download.
65
74
  * @returns {Promise<InputFile>} - Promise that resolves to the downloaded input file.
66
75
  */
67
- private async downloadFile (attachment: Attachment): Promise<InputFile | undefined> {
76
+ private async downloadFile (attachment: Attachment, headers: Record<string, string> = {}): Promise<InputFile | undefined> {
68
77
  let inputFile: InputFile | undefined
69
78
 
70
79
  if (attachment.contentUrl && attachment.contentUrl.startsWith('https://')) {
@@ -72,21 +81,19 @@ export class M365AttachmentDownloader<TState extends TurnState = TurnState> impl
72
81
  const contentSchema = z.object({ downloadUrl: z.string().url() })
73
82
  const parsed = contentSchema.safeParse(attachment.content)
74
83
  const downloadUrl = parsed.success ? parsed.data.downloadUrl : attachment.contentUrl
75
- if (!this._hostValidator.isAllowed(downloadUrl)) {
76
- let host = '[invalid-url]'
77
- try { host = new URL(downloadUrl).hostname } catch { /* invalid */ }
78
- logger.warn(`Attachment downloadUrl host is not in the configured allowed hosts. Host='${host}'`)
84
+ const response = await new AttachmentDownloadHttpClient(this._httpClient, this._hostValidator)
85
+ .get(downloadUrl, headers)
86
+ if (!response) {
79
87
  return undefined
80
88
  }
81
89
 
82
- const response = await this._httpClient.get(downloadUrl, { responseType: 'arraybuffer' })
83
-
84
90
  const content = Buffer.from(response.data as ArrayBuffer)
85
91
  const contentTypeHeader = response.headers.get('content-type')
86
92
  const contentType = typeof contentTypeHeader === 'string' ? contentTypeHeader : 'application/octet-stream'
87
93
  inputFile = { content, contentType, contentUrl: attachment.contentUrl }
88
94
  } catch (error) {
89
- logger.error(`Failed to download Teams attachment: ${error}`)
95
+ const code = error instanceof Error && 'code' in error && typeof error.code === 'number' ? error.code : 'unavailable'
96
+ logger.error(`Failed to download Teams attachment. Error type='${error instanceof Error ? error.name : 'unknown'}', Code='${code}'`)
90
97
  return undefined
91
98
  }
92
99
  } else {
@@ -441,6 +441,7 @@ let globalEnv = loadEnv()
441
441
  * Optional host-scoped inputs used while resolving authentication settings.
442
442
  */
443
443
  export interface AuthConfigurationResolutionOptions {
444
+ /** Host-scoped configuration layers to include during resolution. */
444
445
  configurationContext?: ConfigurationContext
445
446
  }
446
447
 
@@ -2,10 +2,17 @@
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
+ /** APX OAuth scope for local development. */
5
6
  export const ApxLocalScope: string = 'c16e153d-5d2b-4c21-b7f4-b05ee5d516f1/.default'
7
+ /** APX OAuth scope for the development environment. */
6
8
  export const ApxDevScope: string = '0d94caae-b412-4943-8a68-83135ad6d35f/.default'
9
+ /** APX OAuth scope for the production environment. */
7
10
  export const ApxProductionScope: string = '5a807f24-c9de-44ee-a3a7-329e88a00ffc/.default'
11
+ /** APX OAuth scope for the US Government Community Cloud environment. */
8
12
  export const ApxGCCScope: string = 'c9475445-9789-4fef-9ec5-cde4a9bcd446/.default'
13
+ /** APX OAuth scope for the US Government Community Cloud High environment. */
9
14
  export const ApxGCCHScope: string = '6f669b9e-7701-4e2b-b624-82c9207fde26/.default'
15
+ /** APX OAuth scope for the US Department of Defense environment. */
10
16
  export const ApxDoDScope: string = '0a069c81-8c7c-4712-886b-9c542d673ffb/.default'
17
+ /** APX OAuth scope for the Microsoft Cloud operated by 21Vianet. */
11
18
  export const ApxGallatinScope: string = 'bd004c8e-5acf-4c48-8570-4e7d46b2f63b/.default'
@@ -21,7 +21,20 @@ export interface AuthProvider {
21
21
  * @returns A promise that resolves to the access token.
22
22
  */
23
23
  getAccessToken (authConfig: AuthConfiguration, scope: string): Promise<string>
24
+ /**
25
+ * Gets an access token using this provider's connection settings.
26
+ *
27
+ * @param scope - Scope for which the access token is requested.
28
+ * @returns A promise that resolves to the access token.
29
+ */
24
30
  getAccessToken (scope: string): Promise<string>
31
+ /**
32
+ * Gets an access token using explicit or provider-level settings.
33
+ *
34
+ * @param authConfigOrScope - Authentication configuration, or the scope when using provider settings.
35
+ * @param scope - Scope used with an explicit authentication configuration.
36
+ * @returns A promise that resolves to the access token.
37
+ */
25
38
  getAccessToken (authConfigOrScope: AuthConfiguration | string, scope?: string): Promise<string>
26
39
 
27
40
  /**
@@ -50,8 +63,31 @@ export interface AuthProvider {
50
63
  */
51
64
  getAgenticUserToken: (tenantId: string, agentAppInstanceId: string, upn: string, scopes: string[]) => Promise<string>
52
65
 
66
+ /**
67
+ * Acquires a token on behalf of a user using this provider's connection settings.
68
+ *
69
+ * @param scopes - Scopes requested for the delegated token.
70
+ * @param oboAssertion - User assertion exchanged for the token.
71
+ * @returns A promise that resolves to the delegated access token.
72
+ */
53
73
  acquireTokenOnBehalfOf (scopes: string[], oboAssertion: string): Promise<string>
74
+ /**
75
+ * Acquires a token on behalf of a user using explicit authentication settings.
76
+ *
77
+ * @param authConfig - Authentication configuration.
78
+ * @param scopes - Scopes requested for the delegated token.
79
+ * @param oboAssertion - User assertion exchanged for the token.
80
+ * @returns A promise that resolves to the delegated access token.
81
+ */
54
82
  acquireTokenOnBehalfOf (authConfig: AuthConfiguration, scopes: string[], oboAssertion: string): Promise<string>
83
+ /**
84
+ * Acquires a delegated token using explicit or provider-level settings.
85
+ *
86
+ * @param authConfigOrScopes - Authentication configuration, or requested scopes when using provider settings.
87
+ * @param scopesOrOboAssertion - Requested scopes or the user assertion, depending on the overload.
88
+ * @param oboAssertion - User assertion used with explicit authentication settings.
89
+ * @returns A promise that resolves to the delegated access token.
90
+ */
55
91
  acquireTokenOnBehalfOf (
56
92
  authConfigOrScopes: AuthConfiguration | string[],
57
93
  scopesOrOboAssertion?: string[] | string,
@@ -146,7 +146,9 @@ export class ConnectionManager implements Connections {
146
146
  audience = identity.aud
147
147
  }
148
148
 
149
- if (!audience || !serviceUrl) throw ExceptionHelper.generateException(Error, Errors.AudienceAndServiceUrlRequiredForTokenProvider)
149
+ if (!audience || !serviceUrl) {
150
+ throw ExceptionHelper.generateException(Error, Errors.AudienceAndServiceUrlRequiredForTokenProvider)
151
+ }
150
152
 
151
153
  if (this._connectionsMap.length === 0) {
152
154
  logger.debug('no connectionsMap, using default connection for serviceUrl=%s', serviceUrl)
@@ -8,6 +8,7 @@ import { AuthConfiguration } from './authConfiguration'
8
8
  import { AuthProvider } from './authProvider'
9
9
  import { JwtPayload } from 'jsonwebtoken'
10
10
 
11
+ /** Provides configured authentication connections and token providers. */
11
12
  export interface Connections {
12
13
  /**
13
14
  * Get the OAuth connection for the agent.
@@ -14,6 +14,13 @@ import { ConnectionManager, defaultAuthProviderFactory } from '../connectionMana
14
14
  * provider factory and applies MSAL-specific connection defaults.
15
15
  */
16
16
  export class MsalConnectionManager extends ConnectionManager {
17
+ /**
18
+ * Creates an MSAL connection manager.
19
+ *
20
+ * @param connectionsConfigurations - Authentication settings keyed by connection name.
21
+ * @param connectionsMap - Rules that map service URLs and audiences to connections.
22
+ * @param configuration - Default authentication configuration.
23
+ */
17
24
  constructor (
18
25
  connectionsConfigurations: Map<string, AuthConfiguration> = new Map(),
19
26
  connectionsMap: ConnectionMapItem[] = [],
@@ -37,8 +37,14 @@ export class MsalTokenProvider implements AuthProvider {
37
37
  private static readonly _agenticTokenCache = new MemoryCache<string>()
38
38
  private static readonly _confidentialClients = new Map<string, ConfidentialClientApplication>()
39
39
  private static readonly _maxConfidentialClients = 100
40
+ /** Authentication settings used by overloads that do not accept an explicit configuration. */
40
41
  public readonly connectionSettings?: AuthConfiguration
41
42
 
43
+ /**
44
+ * Creates an MSAL token provider.
45
+ *
46
+ * @param connectionSettings - Default authentication settings for token acquisition.
47
+ */
42
48
  constructor (connectionSettings?: AuthConfiguration) {
43
49
  this.connectionSettings = connectionSettings
44
50
  }
@@ -281,6 +287,13 @@ export class MsalTokenProvider implements AuthProvider {
281
287
  * @returns A promise that resolves to the access token.
282
288
  */
283
289
  public async getAccessToken (authConfig: AuthConfiguration, scope: string): Promise<string>
290
+ /**
291
+ * Gets an access token using explicit or provider-level settings.
292
+ *
293
+ * @param authConfigOrScope - Authentication configuration, or the scope when using provider settings.
294
+ * @param scope - Scope used with an explicit authentication configuration.
295
+ * @returns The access token.
296
+ */
284
297
  public async getAccessToken (authConfigOrScope: AuthConfiguration | string, scope?: string): Promise<string> {
285
298
  return trace(AuthenticationTraceDefinitions.getAccessToken, async ({ record }) => {
286
299
  let authConfig: AuthConfiguration
@@ -368,8 +381,31 @@ export class MsalTokenProvider implements AuthProvider {
368
381
  })
369
382
  }
370
383
 
384
+ /**
385
+ * Acquires a delegated token using this provider's connection settings.
386
+ *
387
+ * @param scopes - Scopes requested for the delegated token.
388
+ * @param oboAssertion - User assertion exchanged for the token.
389
+ * @returns The delegated access token.
390
+ */
371
391
  public async acquireTokenOnBehalfOf (scopes: string[], oboAssertion: string): Promise<string>
392
+ /**
393
+ * Acquires a delegated token using explicit authentication settings.
394
+ *
395
+ * @param authConfig - Authentication configuration.
396
+ * @param scopes - Scopes requested for the delegated token.
397
+ * @param oboAssertion - User assertion exchanged for the token.
398
+ * @returns The delegated access token.
399
+ */
372
400
  public async acquireTokenOnBehalfOf (authConfig: AuthConfiguration, scopes: string[], oboAssertion: string): Promise<string>
401
+ /**
402
+ * Acquires a delegated token using explicit or provider-level settings.
403
+ *
404
+ * @param authConfigOrScopes - Authentication configuration, or requested scopes when using provider settings.
405
+ * @param scopesOrOboAssertion - Requested scopes or the user assertion, depending on the overload.
406
+ * @param oboAssertion - User assertion used with explicit authentication settings.
407
+ * @returns The delegated access token.
408
+ */
373
409
  public async acquireTokenOnBehalfOf (
374
410
  authConfigOrScopes: AuthConfiguration | string[],
375
411
  scopesOrOboAssertion?: string[] | string,
@@ -411,6 +447,13 @@ export class MsalTokenProvider implements AuthProvider {
411
447
  })
412
448
  }
413
449
 
450
+ /**
451
+ * Gets a federated token for an agent application instance.
452
+ *
453
+ * @param tenantId - Microsoft Entra tenant ID.
454
+ * @param agentAppInstanceId - Agent application instance ID.
455
+ * @returns The agent instance token.
456
+ */
414
457
  public async getAgenticInstanceToken (tenantId: string, agentAppInstanceId: string): Promise<string> {
415
458
  return trace(AuthenticationTraceDefinitions.getAgenticInstanceToken, async ({ record }) => {
416
459
  logger.debug('getAgenticInstanceToken tenantId=%s agentAppInstanceId=%s', tenantId, agentAppInstanceId)
@@ -566,6 +609,15 @@ export class MsalTokenProvider implements AuthProvider {
566
609
  return token.access_token
567
610
  }
568
611
 
612
+ /**
613
+ * Gets a delegated token for an agentic user.
614
+ *
615
+ * @param tenantId - Microsoft Entra tenant ID.
616
+ * @param agentAppInstanceId - Agent application instance ID.
617
+ * @param agenticUserId - ID of the user represented by the token.
618
+ * @param scopes - Scopes requested for the token.
619
+ * @returns The agentic user token.
620
+ */
569
621
  public async getAgenticUserToken (tenantId: string, agentAppInstanceId: string, agenticUserId: string, scopes: string[]): Promise<string> {
570
622
  return trace(AuthenticationTraceDefinitions.getAgenticUserToken, async ({ record }) => {
571
623
  logger.debug('getAgenticUserToken tenantId=%s agentAppInstanceId=%s scopes=%o', tenantId, agentAppInstanceId, scopes)
@@ -597,6 +649,13 @@ export class MsalTokenProvider implements AuthProvider {
597
649
  })
598
650
  }
599
651
 
652
+ /**
653
+ * Gets the application token used to authenticate an agent application instance.
654
+ *
655
+ * @param tenantId - Microsoft Entra tenant ID.
656
+ * @param agentAppInstanceId - Agent application instance ID.
657
+ * @returns The agentic application token.
658
+ */
600
659
  public async getAgenticApplicationToken (tenantId: string, agentAppInstanceId: string): Promise<string> {
601
660
  if (!this.connectionSettings?.clientId) {
602
661
  throw ExceptionHelper.generateException(Error, Errors.ConnectionSettingsRequiredForGetAgenticApplicationToken)
@@ -24,16 +24,28 @@ const AUTHORITY_DEFAULT = 'https://login.microsoftonline.com'
24
24
  * (matched by audience and/or serviceUrl) to a named connection.
25
25
  */
26
26
  export interface ConnectionMapItem {
27
+ /** Service URL pattern matched by this route. */
27
28
  serviceUrl: string
29
+ /** Optional token audience matched by this route. */
28
30
  audience?: string
31
+ /** Name of the connection selected by this route. */
29
32
  connection: string
30
33
  }
31
34
 
35
+ /** Default route that selects the standard service connection. */
32
36
  export const DEFAULT_CONNECTION_MAP: ConnectionMapItem = { serviceUrl: '*', connection: DEFAULT_CONNECTION }
33
37
 
38
+ /** Property names accepted as connection-level settings. */
34
39
  export type ConnectionKeys = keyof Omit<AuthConfiguration, 'connections' | 'connectionsMap'>
40
+ /** Property names accepted in a connection-map entry. */
35
41
  export type ConnectionMapKeys = keyof ConnectionMapItem
36
42
 
43
+ /**
44
+ * Applies authority, issuer, alias, and connection-map defaults to a configuration.
45
+ *
46
+ * @param config - Authentication configuration to normalize.
47
+ * @returns A normalized copy of the configuration.
48
+ */
37
49
  export function applyDefaultSettings (config: AuthConfiguration) {
38
50
  const settings = { ...config }
39
51
  settings.authorityEndpoint ??= settings.authority ?? AUTHORITY_DEFAULT
@@ -106,6 +118,13 @@ function trimTrailingSlashes (value: string): string {
106
118
  return end === value.length ? value : value.slice(0, end)
107
119
  }
108
120
 
121
+ /**
122
+ * Builds the default trusted issuer list for a tenant and authority.
123
+ *
124
+ * @param tenantId - Microsoft Entra tenant ID.
125
+ * @param authority - Authority URL used to determine the cloud and effective tenant.
126
+ * @returns Trusted Bot Framework and Microsoft Entra issuer URLs.
127
+ */
109
128
  export function getDefaultIssuers (tenantId: string, authority: string) : string[] {
110
129
  // Convert empty string to undefined so resolveAuthority applies its 'botframework.com' default
111
130
  const t = getEffectiveTenant(tenantId || undefined, authority)
@@ -268,14 +287,23 @@ export interface AuthConfiguration extends ConnectionSettings {
268
287
  * Supported authentication types for agent connections.
269
288
  */
270
289
  export enum AuthType {
290
+ /** Client authentication using an X.509 certificate. */
271
291
  Certificate = 'Certificate',
292
+ /** Client authentication using a certificate subject name. */
272
293
  CertificateSubjectName = 'CertificateSubjectName',
294
+ /** Client authentication using a client secret. */
273
295
  ClientSecret = 'ClientSecret',
296
+ /** Authentication using a user-assigned managed identity. */
274
297
  UserManagedIdentity = 'UserManagedIdentity',
298
+ /** Authentication using a system-assigned managed identity. */
275
299
  SystemManagedIdentity = 'SystemManagedIdentity',
300
+ /** Authentication using federated identity credentials. */
276
301
  FederatedCredentials = 'FederatedCredentials',
302
+ /** Authentication using a workload identity token. */
277
303
  WorkloadIdentity = 'WorkloadIdentity',
304
+ /** Authentication through Identity Proxy Manager. */
278
305
  IdentityProxyManager = 'IdentityProxyManager',
306
+ /** Authentication through the Microsoft Entra authentication sidecar. */
279
307
  EntraAuthSideCar = 'EntraAuthSideCar'
280
308
  }
281
309
 
@@ -134,6 +134,10 @@ export class SidecarAuthProvider implements AuthProvider {
134
134
  * On-behalf-of token exchange — not supported by the sidecar provider in Phase 1.
135
135
  */
136
136
  async acquireTokenOnBehalfOf (scopes: string[], oboAssertion: string): Promise<string>
137
+
138
+ /**
139
+ * On-behalf-of token exchange with explicit authentication settings — not supported by the sidecar provider in Phase 1.
140
+ */
137
141
  async acquireTokenOnBehalfOf (authConfig: AuthConfiguration, scopes: string[], oboAssertion: string): Promise<string>
138
142
  async acquireTokenOnBehalfOf (
139
143
  _authConfigOrScopes: AuthConfiguration | string[],
@@ -3,10 +3,10 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { isIP } from 'node:net'
7
6
  import { debug } from '@microsoft/agents-telemetry'
8
7
  import { ExceptionHelper } from '@microsoft/agents-activity'
9
8
  import { Errors } from '../../errorHelper'
9
+ import { isPrivateOrLoopbackAddress } from '../../utils/ipAddress'
10
10
  import {
11
11
  DEFAULT_REQUEST_TIMEOUT_MS,
12
12
  DEFAULT_RETRY_COUNT,
@@ -139,68 +139,8 @@ export class SidecarHttpClient {
139
139
  }
140
140
 
141
141
  private static isLoopbackOrPrivateHost (host: string): boolean {
142
- // URL.hostname wraps IPv6 literals in brackets (e.g. "[::1]"); strip them before classification.
143
- const normalized = host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host
144
- const ipVersion = isIP(normalized)
145
- if (ipVersion === 0) {
146
- // DNS host: only "localhost" (and subdomains) is treated as safe.
147
- const lower = normalized.toLowerCase()
148
- return lower === 'localhost' || lower.endsWith('.localhost')
149
- }
150
- if (ipVersion === 4) {
151
- return SidecarHttpClient.isPrivateOrLoopbackIPv4(normalized)
152
- }
153
- return SidecarHttpClient.isPrivateOrLoopbackIPv6(normalized)
154
- }
155
-
156
- private static isPrivateOrLoopbackIPv4 (host: string): boolean {
157
- const b = host.split('.').map((p) => parseInt(p, 10))
158
- if (b.length !== 4 || b.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {
159
- return false
160
- }
161
- if (b[0] === 127) return true // 127.0.0.0/8 loopback
162
- if (b[0] === 10) return true // 10.0.0.0/8
163
- if (b[0] === 172 && b[1] >= 16 && b[1] <= 31) return true // 172.16.0.0/12
164
- if (b[0] === 192 && b[1] === 168) return true // 192.168.0.0/16
165
- if (b[0] === 169 && b[1] === 254) return true // 169.254.0.0/16 link-local
166
- return false
167
- }
168
-
169
- private static isPrivateOrLoopbackIPv6 (host: string): boolean {
170
142
  const lower = host.toLowerCase()
171
- if (lower === '::1' || lower === '0:0:0:0:0:0:0:1') return true // loopback (`::` is the unspecified address, not loopback)
172
- // IPv4-mapped/-translated addresses in either dotted (::ffff:127.0.0.1)
173
- // or hex-compressed (::ffff:7f00:1) form.
174
- const mappedIPv4 = SidecarHttpClient.extractMappedIPv4(lower)
175
- if (mappedIPv4) {
176
- return SidecarHttpClient.isPrivateOrLoopbackIPv4(mappedIPv4)
177
- }
178
- const firstHextet = parseInt(lower.split(':')[0], 16)
179
- if (!Number.isNaN(firstHextet)) {
180
- if ((firstHextet & 0xfe00) === 0xfc00) return true // fc00::/7 unique local
181
- if ((firstHextet & 0xffc0) === 0xfe80) return true // fe80::/10 link-local
182
- if ((firstHextet & 0xffc0) === 0xfec0) return true // fec0::/10 site-local (deprecated)
183
- }
184
- return false
185
- }
186
-
187
- /**
188
- * Extracts the embedded IPv4 address from an IPv4-mapped/-translated IPv6 literal, returning it
189
- * in dotted-quad form, or `undefined` if `host` is not such an address. Handles both the dotted
190
- * form (`::ffff:127.0.0.1`) and the hex-compressed form Node emits (`::ffff:7f00:1`).
191
- */
192
- private static extractMappedIPv4 (host: string): string | undefined {
193
- const dotted = host.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/)
194
- if (dotted) {
195
- return dotted[1]
196
- }
197
- const hex = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/)
198
- if (hex) {
199
- const high = parseInt(hex[1], 16)
200
- const low = parseInt(hex[2], 16)
201
- return `${high >> 8}.${high & 0xff}.${low >> 8}.${low & 0xff}`
202
- }
203
- return undefined
143
+ return lower === 'localhost' || lower.endsWith('.localhost') || isPrivateOrLoopbackAddress(host)
204
144
  }
205
145
 
206
146
  /**
@@ -9,6 +9,12 @@ import { CardAction } from '@microsoft/agents-activity'
9
9
  * Represents a task module action.
10
10
  */
11
11
  export class TaskModuleAction implements CardAction {
12
+ /**
13
+ * Creates an action that invokes a `task/fetch` task module.
14
+ *
15
+ * @param title The action title.
16
+ * @param value The action data as an object or JSON string.
17
+ */
12
18
  constructor (title: string, value: any) {
13
19
  this.type = 'invoke'
14
20
  this.title = title
@@ -152,7 +152,9 @@ function sanitizeForLog (value: string, max = 256): string {
152
152
  function truncateActivityForLog (activity: unknown, max = 1024): string {
153
153
  try {
154
154
  const json = JSON.stringify(activity)
155
- if (!json) return '<unserializable>'
155
+ if (!json) {
156
+ return '<unserializable>'
157
+ }
156
158
  // Strip C0/DEL/C1 controls + U+2028/U+2029 from the serialized form so
157
159
  // attacker-controlled fields (e.g. activity.text) can't forge log lines.
158
160
  // eslint-disable-next-line no-control-regex
@@ -651,18 +653,30 @@ export class CloudAdapter extends BaseAdapter {
651
653
  * should be rejected with a 400.
652
654
  */
653
655
  private validateServiceUrl (identity: JwtPayload | undefined, activity: Activity): boolean {
654
- if (!this.isOutboundServiceUrlAllowed(activity.serviceUrl)) return false
656
+ if (!this.isOutboundServiceUrlAllowed(activity.serviceUrl)) {
657
+ return false
658
+ }
655
659
 
656
- if (!identity) return true
657
- if (!activity.serviceUrl) return true
660
+ if (!identity) {
661
+ return true
662
+ }
663
+ if (!activity.serviceUrl) {
664
+ return true
665
+ }
658
666
 
659
667
  const claimValue = identity.serviceurl
660
- if (typeof claimValue !== 'string') return true
668
+ if (typeof claimValue !== 'string') {
669
+ return true
670
+ }
661
671
 
662
672
  let claimHost: string | undefined
663
673
  let activityHost: string | undefined
664
- try { claimHost = new URL(claimValue).hostname.toLowerCase() } catch { /* invalid */ }
665
- try { activityHost = new URL(activity.serviceUrl).hostname.toLowerCase() } catch { /* invalid */ }
674
+ try {
675
+ claimHost = new URL(claimValue).hostname.toLowerCase()
676
+ } catch { /* invalid */ }
677
+ try {
678
+ activityHost = new URL(activity.serviceUrl).hostname.toLowerCase()
679
+ } catch { /* invalid */ }
666
680
 
667
681
  if (claimHost && activityHost && claimHost === activityHost) {
668
682
  return true
@@ -682,7 +696,9 @@ export class CloudAdapter extends BaseAdapter {
682
696
  * Applies the shared outbound host policy.
683
697
  */
684
698
  private isOutboundServiceUrlAllowed (serviceUrl: string | undefined): boolean {
685
- if (!this._hostValidator.enabled || !serviceUrl || this._hostValidator.isAllowed(serviceUrl)) return true
699
+ if (!this._hostValidator.enabled || !serviceUrl || this._hostValidator.isAllowed(serviceUrl)) {
700
+ return true
701
+ }
686
702
 
687
703
  logger.warn(`ServiceUrl host is not in the configured allowed hosts. ServiceUrl='${sanitizeForLog(serviceUrl)}'`)
688
704
  return false
@@ -920,8 +936,12 @@ export class CloudAdapter extends BaseAdapter {
920
936
  if (typeof serviceUrl !== 'string' || !serviceUrl) {
921
937
  throw ExceptionHelper.generateException(TypeError, Errors.ServiceUrlRequired)
922
938
  }
923
- if (!conversationParameters) throw ExceptionHelper.generateException(TypeError, Errors.ConversationParametersRequired)
924
- if (!logic) throw ExceptionHelper.generateException(TypeError, Errors.LogicParameterRequired)
939
+ if (!conversationParameters) {
940
+ throw ExceptionHelper.generateException(TypeError, Errors.ConversationParametersRequired)
941
+ }
942
+ if (!logic) {
943
+ throw ExceptionHelper.generateException(TypeError, Errors.LogicParameterRequired)
944
+ }
925
945
 
926
946
  const identity = CloudAdapter.createIdentity(audience)
927
947
  const restClient = await this.createConnectorClient(serviceUrl, audience, identity)
@@ -27,6 +27,7 @@ export type CloudAdapterConfigurationPatch = Partial<{
27
27
  export type OutboundHostValidatorConfigurationPatch = Partial<{
28
28
  enabled: boolean
29
29
  includeDefaultMicrosoftHosts: boolean
30
+ allowPrivateNetworkAddresses: boolean
30
31
  hosts: readonly string[]
31
32
  }>
32
33
  export type AuthorizationHandlerPatch = Record<string, unknown>
@@ -105,6 +106,7 @@ const connectionMapParsers = {
105
106
  const outboundHostValidatorParsers = {
106
107
  enabled: asBoolean,
107
108
  includeDefaultMicrosoftHosts: asBoolean,
109
+ allowPrivateNetworkAddresses: asBoolean,
108
110
  hosts: asList
109
111
  } satisfies Record<string, (value: string) => unknown>
110
112
 
@@ -489,7 +491,7 @@ function parseValue (
489
491
  ? parseDocumentValue(parser, value)
490
492
  : typeof value === 'string' ? parser(value) : undefined
491
493
  if (parsed === undefined) {
492
- if (key === 'validateIssuer') {
494
+ if (key === 'validateIssuer' || key === 'allowPrivateNetworkAddresses') {
493
495
  throw ExceptionHelper.generateException(
494
496
  TypeError,
495
497
  Errors.InvalidBooleanConfigurationValue,
@@ -598,7 +600,7 @@ export function setConfigurationValue (
598
600
  if (parsed.key === 'hosts') {
599
601
  ;(target.outboundHostValidator as OutboundHostValidatorConfigurationPatch).hosts = parsed.value as string[]
600
602
  } else {
601
- ;(target.outboundHostValidator as OutboundHostValidatorConfigurationPatch)[parsed.key as 'enabled' | 'includeDefaultMicrosoftHosts'] = parsed.value as boolean
603
+ ;(target.outboundHostValidator as OutboundHostValidatorConfigurationPatch)[parsed.key as 'enabled' | 'includeDefaultMicrosoftHosts' | 'allowPrivateNetworkAddresses'] = parsed.value as boolean
602
604
  }
603
605
  return
604
606
  }
@@ -256,7 +256,13 @@ function bindOutboundHostValidator (
256
256
  emitHierarchySuggestion(sourceName, [parts[0], suggestedProperty].join('__'), true)
257
257
  }
258
258
  }
259
- trySet(layer, `outboundHostValidator.${parts[1]}`, value, sourceName)
259
+ trySet(
260
+ layer,
261
+ `outboundHostValidator.${parts[1]}`,
262
+ value,
263
+ sourceName,
264
+ parts[1].toLowerCase() === 'allowprivatenetworkaddresses'
265
+ )
260
266
  }
261
267
 
262
268
  function bindAuthorizationHandler (
@@ -53,6 +53,7 @@ export class ConnectorClient {
53
53
  this._httpClient = httpClient
54
54
  }
55
55
 
56
+ /** Gets the HTTP client used for Connector API requests. */
56
57
  public get httpClient (): HttpClient {
57
58
  return this._httpClient
58
59
  }
@@ -127,6 +128,13 @@ export class ConnectorClient {
127
128
  })
128
129
  }
129
130
 
131
+ /**
132
+ * Retrieves a member of a conversation.
133
+ *
134
+ * @param userId The member's channel user ID.
135
+ * @param conversationId The conversation ID.
136
+ * @returns The member's channel account.
137
+ */
130
138
  public async getConversationMember (userId: string, conversationId: string): Promise<ChannelAccount> {
131
139
  return trace(ConnectorClientTraceDefinitions.getConversationMember, async ({ record }) => {
132
140
  if (!userId || !conversationId) {
@@ -15,7 +15,9 @@ import { TurnState } from './app/turnState'
15
15
  * Result of creating a CloudAdapter from an agent.
16
16
  */
17
17
  export interface CloudAdapterResult {
18
+ /** The adapter to use for processing requests. */
18
19
  adapter: CloudAdapter
20
+ /** The application's outbound header propagation callback, when configured. */
19
21
  headerPropagation: HeaderPropagationDefinition | undefined
20
22
  }
21
23
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  import { AgentErrorDefinition } from '@microsoft/agents-activity'
5
5
 
6
+ /** Error definitions used by the hosting package. */
6
7
  export const Errors: { [key: string]: AgentErrorDefinition } = {
7
8
  // ============================================================================
8
9
  // TurnContext and Activity Errors (-120000 to -120090)
@@ -295,6 +296,16 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
295
296
  description: 'HttpClient requires baseURL when using a relative URL: {url}'
296
297
  },
297
298
 
299
+ AttachmentDownloadRedirectLimit: {
300
+ code: -120292,
301
+ description: 'Attachment download exceeded the maximum of 50 redirects.'
302
+ },
303
+
304
+ AttachmentDownloadInvalidRedirect: {
305
+ code: -120293,
306
+ description: 'Attachment download returned an invalid redirect URL.'
307
+ },
308
+
298
309
  // ============================================================================
299
310
  // Connection and Authentication Errors (-120300 to -120599)
300
311
  // ============================================================================