@microsoft/agents-hosting 1.6.1 → 1.7.0-beta.1.g8bcd4f11a7

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 (87) hide show
  1. package/dist/package.json +3 -3
  2. package/dist/src/app/streaming/streamingResponse.d.ts +11 -0
  3. package/dist/src/app/streaming/streamingResponse.js +21 -0
  4. package/dist/src/app/streaming/streamingResponse.js.map +1 -1
  5. package/dist/src/auth/MemoryCache.d.ts +2 -1
  6. package/dist/src/auth/MemoryCache.js +7 -1
  7. package/dist/src/auth/MemoryCache.js.map +1 -1
  8. package/dist/src/auth/authConfiguration.d.ts +1 -1
  9. package/dist/src/auth/authConfiguration.js +36 -1
  10. package/dist/src/auth/authConfiguration.js.map +1 -1
  11. package/dist/src/auth/authConstants.d.ts +7 -7
  12. package/dist/src/auth/authConstants.js.map +1 -1
  13. package/dist/src/auth/connectionManager.d.ts +92 -0
  14. package/dist/src/auth/{msalConnectionManager.js → connectionManager.js} +50 -48
  15. package/dist/src/auth/connectionManager.js.map +1 -0
  16. package/dist/src/auth/index.d.ts +5 -3
  17. package/dist/src/auth/index.js +5 -3
  18. package/dist/src/auth/index.js.map +1 -1
  19. package/dist/src/auth/jwt-middleware.d.ts +6 -0
  20. package/dist/src/auth/jwt-middleware.js +32 -1
  21. package/dist/src/auth/jwt-middleware.js.map +1 -1
  22. package/dist/src/auth/msal/msalConnectionManager.d.ts +17 -0
  23. package/dist/src/auth/msal/msalConnectionManager.js +50 -0
  24. package/dist/src/auth/msal/msalConnectionManager.js.map +1 -0
  25. package/dist/src/auth/msal/msalConnectionSettings.d.ts +68 -0
  26. package/dist/src/auth/msal/msalConnectionSettings.js +7 -0
  27. package/dist/src/auth/msal/msalConnectionSettings.js.map +1 -0
  28. package/dist/src/auth/{msalTokenCredential.d.ts → msal/msalTokenCredential.d.ts} +1 -1
  29. package/dist/src/auth/{msalTokenCredential.js → msal/msalTokenCredential.js} +2 -2
  30. package/dist/src/auth/msal/msalTokenCredential.js.map +1 -0
  31. package/dist/src/auth/{msalTokenProvider.d.ts → msal/msalTokenProvider.d.ts} +23 -3
  32. package/dist/src/auth/{msalTokenProvider.js → msal/msalTokenProvider.js} +264 -160
  33. package/dist/src/auth/msal/msalTokenProvider.js.map +1 -0
  34. package/dist/src/auth/settings.d.ts +142 -133
  35. package/dist/src/auth/settings.js +49 -5
  36. package/dist/src/auth/settings.js.map +1 -1
  37. package/dist/src/auth/sidecar/sidecarAuthProvider.d.ts +74 -0
  38. package/dist/src/auth/sidecar/sidecarAuthProvider.js +181 -0
  39. package/dist/src/auth/sidecar/sidecarAuthProvider.js.map +1 -0
  40. package/dist/src/auth/sidecar/sidecarConnectionSettings.d.ts +65 -0
  41. package/dist/src/auth/sidecar/sidecarConnectionSettings.js +7 -0
  42. package/dist/src/auth/sidecar/sidecarConnectionSettings.js.map +1 -0
  43. package/dist/src/auth/sidecar/sidecarHttpClient.d.ts +95 -0
  44. package/dist/src/auth/sidecar/sidecarHttpClient.js +406 -0
  45. package/dist/src/auth/sidecar/sidecarHttpClient.js.map +1 -0
  46. package/dist/src/auth/sidecar/sidecarModels.d.ts +110 -0
  47. package/dist/src/auth/sidecar/sidecarModels.js +48 -0
  48. package/dist/src/auth/sidecar/sidecarModels.js.map +1 -0
  49. package/dist/src/auth/sidecar/sidecarTokenExpiry.d.ts +13 -0
  50. package/dist/src/auth/sidecar/sidecarTokenExpiry.js +39 -0
  51. package/dist/src/auth/sidecar/sidecarTokenExpiry.js.map +1 -0
  52. package/dist/src/cloudAdapter.js +1 -1
  53. package/dist/src/cloudAdapter.js.map +1 -1
  54. package/dist/src/errorHelper.js +96 -0
  55. package/dist/src/errorHelper.js.map +1 -1
  56. package/dist/src/index.d.ts +1 -1
  57. package/dist/src/index.js +2 -1
  58. package/dist/src/index.js.map +1 -1
  59. package/dist/src/oauth/userTokenClient.d.ts +12 -0
  60. package/dist/src/oauth/userTokenClient.js +28 -8
  61. package/dist/src/oauth/userTokenClient.js.map +1 -1
  62. package/package.json +3 -3
  63. package/src/app/streaming/streamingResponse.ts +24 -1
  64. package/src/auth/MemoryCache.ts +7 -1
  65. package/src/auth/authConfiguration.ts +36 -3
  66. package/src/auth/authConstants.ts +7 -7
  67. package/src/auth/{msalConnectionManager.ts → connectionManager.ts} +65 -61
  68. package/src/auth/index.ts +5 -3
  69. package/src/auth/jwt-middleware.ts +31 -1
  70. package/src/auth/msal/msalConnectionManager.ts +55 -0
  71. package/src/auth/msal/msalConnectionSettings.ts +79 -0
  72. package/src/auth/{msalTokenCredential.ts → msal/msalTokenCredential.ts} +2 -1
  73. package/src/auth/{msalTokenProvider.ts → msal/msalTokenProvider.ts} +337 -156
  74. package/src/auth/settings.ts +131 -76
  75. package/src/auth/sidecar/sidecarAuthProvider.ts +234 -0
  76. package/src/auth/sidecar/sidecarConnectionSettings.ts +72 -0
  77. package/src/auth/sidecar/sidecarHttpClient.ts +443 -0
  78. package/src/auth/sidecar/sidecarModels.ts +153 -0
  79. package/src/auth/sidecar/sidecarTokenExpiry.ts +35 -0
  80. package/src/cloudAdapter.ts +1 -1
  81. package/src/errorHelper.ts +110 -0
  82. package/src/index.ts +1 -1
  83. package/src/oauth/userTokenClient.ts +43 -9
  84. package/dist/src/auth/msalConnectionManager.d.ts +0 -64
  85. package/dist/src/auth/msalConnectionManager.js.map +0 -1
  86. package/dist/src/auth/msalTokenCredential.js.map +0 -1
  87. package/dist/src/auth/msalTokenProvider.js.map +0 -1
@@ -3,11 +3,10 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import type { ConnectionMapItem } from './msalConnectionManager'
7
6
  import { debug, redactString, redactScopes, redactUrl } from '@microsoft/agents-telemetry'
8
- import { loadEnvSettings, AuthConfiguration, envParser, envParserUtils, LoadEnv, applyDefaultSettings, DEFAULT_CONNECTION_MAP, ConnectionKeys, ConnectionMapKeys } from './settings'
7
+ import { loadEnvSettings, AuthConfiguration, envParser, envParserUtils, LoadEnv, applyDefaultSettings, DEFAULT_CONNECTION_MAP, ConnectionKeys, ConnectionMapKeys, ConnectionMapItem } from './settings'
9
8
 
10
- export { type AuthConfiguration, AuthType, resolveAuthority } from './settings'
9
+ export { type AuthConfiguration, type ConnectionSettings, type ConnectionSettingsBase, type MsalConnectionSettings, type SidecarConnectionSettings, AuthType, resolveAuthority, type ConnectionMapItem, resolveAuthType } from './settings'
11
10
  import { prune } from '../utils'
12
11
 
13
12
  const logger = debug('agents:authConfiguration')
@@ -36,8 +35,15 @@ function summarizeAuthConfiguration (authConfig: AuthConfiguration) {
36
35
  idpmResource: config.idpmResource ? redactUrl(config.idpmResource) : undefined,
37
36
  connectionName: config.connectionName,
38
37
  altBlueprintConnectionName: config.altBlueprintConnectionName,
38
+ alternateBlueprintConnectionName: undefined, // Alias of altBlueprintConnectionName, avoid logging duplicate info
39
39
  azureRegion: config.azureRegion,
40
40
  sendX5C: config.sendX5C,
41
+ sidecarBaseUrl: config.sidecarBaseUrl ? redactUrl(config.sidecarBaseUrl) : undefined,
42
+ serviceName: config.serviceName,
43
+ blueprintServiceName: config.blueprintServiceName,
44
+ bypassLocalNetworkRestriction: config.bypassLocalNetworkRestriction,
45
+ requestTimeout: config.requestTimeout,
46
+ retryCount: config.retryCount,
41
47
  // Don't log the following properties
42
48
  authority: undefined, // Deprecated, same as authorityEndpoint, avoid logging duplicate info
43
49
  FICClientId: undefined, // Deprecated, same as federatedClientId, avoid logging duplicate info
@@ -82,11 +88,24 @@ const connectionsEnv = {
82
88
  return { key: 'scopes', value: this.scopes(value)?.value } // redirect with single scope
83
89
  },
84
90
  altBlueprintConnectionName: envParserUtils.bypass,
91
+ alternateBlueprintConnectionName: (value) => ({ key: 'altBlueprintConnectionName', value }), // .NET parity alias
85
92
  WIDAssertionFile: envParserUtils.bypass,
86
93
  federatedTokenFile: envParserUtils.bypass,
87
94
  idpmResource: envParserUtils.bypass,
88
95
  azureRegion: envParserUtils.bypass,
89
96
  sendX5C: (value) => ({ value: value === 'true' }),
97
+ sidecarBaseUrl: envParserUtils.bypass,
98
+ serviceName: envParserUtils.bypass,
99
+ blueprintServiceName: envParserUtils.bypass,
100
+ bypassLocalNetworkRestriction: (value) => ({ value: value === 'true' }),
101
+ requestTimeout: (value) => {
102
+ const n = parseInt(value, 10)
103
+ return { value: Number.isFinite(n) && n > 0 ? n : undefined }
104
+ },
105
+ retryCount: (value) => {
106
+ const n = parseInt(value, 10)
107
+ return { value: Number.isFinite(n) && n >= 0 ? n : undefined }
108
+ },
90
109
  issuers (value) {
91
110
  if (value.includes(',')) {
92
111
  return { value: value.split(',').map(s => s.trim()).filter(Boolean) }
@@ -205,12 +224,19 @@ const legacyBotFrameworkEnv = {
205
224
  authorityEndpoint: envParserUtils.redirect(connectionsEnv.parser, 'authorityEndpoint'),
206
225
  scope: envParserUtils.redirect(connectionsEnv.parser, 'scopes'),
207
226
  altBlueprintConnectionName: envParserUtils.redirect(connectionsEnv.parser, 'altBlueprintConnectionName'),
227
+ alternateBlueprintConnectionName: envParserUtils.redirect(connectionsEnv.parser, 'altBlueprintConnectionName'),
208
228
  WIDAssertionFile: envParserUtils.redirect(connectionsEnv.parser, 'WIDAssertionFile'),
209
229
  azureRegion: envParserUtils.redirect(connectionsEnv.parser, 'azureRegion'),
210
230
  sendX5C: envParserUtils.redirect(connectionsEnv.parser, 'sendX5C'),
211
231
  authType: envParserUtils.redirect(connectionsEnv.parser, 'authType'),
212
232
  federatedTokenFile: envParserUtils.redirect(connectionsEnv.parser, 'federatedTokenFile'),
213
233
  idpmResource: envParserUtils.redirect(connectionsEnv.parser, 'idpmResource'),
234
+ sidecarBaseUrl: envParserUtils.redirect(connectionsEnv.parser, 'sidecarBaseUrl'),
235
+ serviceName: envParserUtils.redirect(connectionsEnv.parser, 'serviceName'),
236
+ blueprintServiceName: envParserUtils.redirect(connectionsEnv.parser, 'blueprintServiceName'),
237
+ bypassLocalNetworkRestriction: envParserUtils.redirect(connectionsEnv.parser, 'bypassLocalNetworkRestriction'),
238
+ requestTimeout: envParserUtils.redirect(connectionsEnv.parser, 'requestTimeout'),
239
+ retryCount: envParserUtils.redirect(connectionsEnv.parser, 'retryCount'),
214
240
  }),
215
241
  process (env: LoadEnv) {
216
242
  return legacyPrefixEnv.process.call(this, env)
@@ -233,12 +259,19 @@ const legacyPrefixEnv = {
233
259
  authorityEndpoint: envParserUtils.redirect(connectionsEnv.parser, 'authorityEndpoint'),
234
260
  scope: envParserUtils.redirect(connectionsEnv.parser, 'scopes'),
235
261
  altBlueprintConnectionName: envParserUtils.redirect(connectionsEnv.parser, 'altBlueprintConnectionName'),
262
+ alternateBlueprintConnectionName: envParserUtils.redirect(connectionsEnv.parser, 'altBlueprintConnectionName'),
236
263
  WIDAssertionFile: envParserUtils.redirect(connectionsEnv.parser, 'WIDAssertionFile'),
237
264
  azureRegion: envParserUtils.redirect(connectionsEnv.parser, 'azureRegion'),
238
265
  sendX5C: envParserUtils.redirect(connectionsEnv.parser, 'sendX5C'),
239
266
  authType: envParserUtils.redirect(connectionsEnv.parser, 'authType'),
240
267
  federatedTokenFile: envParserUtils.redirect(connectionsEnv.parser, 'federatedTokenFile'),
241
268
  idpmResource: envParserUtils.redirect(connectionsEnv.parser, 'idpmResource'),
269
+ sidecarBaseUrl: envParserUtils.redirect(connectionsEnv.parser, 'sidecarBaseUrl'),
270
+ serviceName: envParserUtils.redirect(connectionsEnv.parser, 'serviceName'),
271
+ blueprintServiceName: envParserUtils.redirect(connectionsEnv.parser, 'blueprintServiceName'),
272
+ bypassLocalNetworkRestriction: envParserUtils.redirect(connectionsEnv.parser, 'bypassLocalNetworkRestriction'),
273
+ requestTimeout: envParserUtils.redirect(connectionsEnv.parser, 'requestTimeout'),
274
+ retryCount: envParserUtils.redirect(connectionsEnv.parser, 'retryCount'),
242
275
  }),
243
276
  process (env: LoadEnv, prefix?: string) {
244
277
  const settings: Partial<AuthConfiguration> = {}
@@ -2,10 +2,10 @@
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- export const ApxLocalScope = 'c16e153d-5d2b-4c21-b7f4-b05ee5d516f1/.default'
6
- export const ApxDevScope = '0d94caae-b412-4943-8a68-83135ad6d35f/.default'
7
- export const ApxProductionScope = '5a807f24-c9de-44ee-a3a7-329e88a00ffc/.default'
8
- export const ApxGCCScope = 'c9475445-9789-4fef-9ec5-cde4a9bcd446/.default'
9
- export const ApxGCCHScope = '6f669b9e-7701-4e2b-b624-82c9207fde26/.default'
10
- export const ApxDoDScope = '0a069c81-8c7c-4712-886b-9c542d673ffb/.default'
11
- export const ApxGallatinScope = 'bd004c8e-5acf-4c48-8570-4e7d46b2f63b/.default'
5
+ export const ApxLocalScope: string = 'c16e153d-5d2b-4c21-b7f4-b05ee5d516f1/.default'
6
+ export const ApxDevScope: string = '0d94caae-b412-4943-8a68-83135ad6d35f/.default'
7
+ export const ApxProductionScope: string = '5a807f24-c9de-44ee-a3a7-329e88a00ffc/.default'
8
+ export const ApxGCCScope: string = 'c9475445-9789-4fef-9ec5-cde4a9bcd446/.default'
9
+ export const ApxGCCHScope: string = '6f669b9e-7701-4e2b-b624-82c9207fde26/.default'
10
+ export const ApxDoDScope: string = '0a069c81-8c7c-4712-886b-9c542d673ffb/.default'
11
+ export const ApxGallatinScope: string = 'bd004c8e-5acf-4c48-8570-4e7d46b2f63b/.default'
@@ -3,28 +3,58 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { Activity, RoleTypes } from '@microsoft/agents-activity'
7
- import { debug } from '@microsoft/agents-telemetry'
8
- import { AuthConfiguration, AuthType, resolveAuthority } from './authConfiguration'
6
+ import { Activity, ExceptionHelper, RoleTypes } from '@microsoft/agents-activity'
7
+ import { debug, redactString } from '@microsoft/agents-telemetry'
8
+ import { AuthConfiguration, AuthType, resolveAuthType } from './authConfiguration'
9
9
  import { Connections } from './connections'
10
- import { MsalTokenProvider } from './msalTokenProvider'
10
+ import { AuthProvider } from './authProvider'
11
+ import { MsalTokenProvider } from './msal/msalTokenProvider'
12
+ import { SidecarAuthProvider } from './sidecar/sidecarAuthProvider'
11
13
  import { JwtPayload } from 'jsonwebtoken'
14
+ import { Errors } from '../errorHelper'
15
+ import type { ConnectionMapItem } from './settings'
12
16
 
13
17
  const logger = debug('agents:authorization:connections')
14
18
 
15
- export interface ConnectionMapItem {
16
- audience?: string
17
- serviceUrl: string
18
- connection: string
19
- }
19
+ /**
20
+ * Factory function that creates an {@link AuthProvider} instance from a connection's
21
+ * {@link AuthConfiguration}.
22
+ */
23
+ export type AuthProviderFactory = (config: AuthConfiguration) => AuthProvider
24
+
25
+ /**
26
+ * Default {@link AuthProviderFactory} that dispatches per-connection by `authType`: connections with
27
+ * `authType` set to `EntraAuthSideCar` use {@link SidecarAuthProvider}; all others use
28
+ * {@link MsalTokenProvider}.
29
+ * @param config The connection authentication configuration.
30
+ * @returns The auth provider for the connection.
31
+ */
32
+ export const defaultAuthProviderFactory: AuthProviderFactory = (config: AuthConfiguration): AuthProvider =>
33
+ config?.authType === AuthType.EntraAuthSideCar
34
+ ? new SidecarAuthProvider(config)
35
+ : new MsalTokenProvider(config)
20
36
 
21
- export class MsalConnectionManager implements Connections {
22
- private _connections: Map<string, MsalTokenProvider>
23
- private _connectionsMap: ConnectionMapItem[]
24
- private _serviceConnectionConfiguration: AuthConfiguration
25
- private static readonly DEFAULT_CONNECTION = 'serviceConnection'
37
+ /**
38
+ * Generic, provider-agnostic connection manager. Dispatches connections to any {@link AuthProvider}
39
+ * implementation produced by the supplied {@link AuthProviderFactory}, while owning the
40
+ * provider-independent connection routing logic (audience matching, service URL dispatch,
41
+ * `altBlueprintConnectionName` handling, default connection resolution).
42
+ */
43
+ export class ConnectionManager implements Connections {
44
+ protected _connections: Map<string, AuthProvider>
45
+ protected _connectionsMap: ConnectionMapItem[]
46
+ protected _serviceConnectionConfiguration: AuthConfiguration
47
+ protected static readonly DEFAULT_CONNECTION = 'serviceConnection'
26
48
 
49
+ /**
50
+ * Creates a new {@link ConnectionManager}.
51
+ * @param providerFactory Factory used to instantiate an {@link AuthProvider} for each connection.
52
+ * @param connectionsConfigurations Map of connection names to their authentication configurations.
53
+ * @param connectionsMap Map items used to route activities to connections.
54
+ * @param configuration Fallback authentication configuration (used when the above are empty).
55
+ */
27
56
  constructor (
57
+ providerFactory: AuthProviderFactory = defaultAuthProviderFactory,
28
58
  connectionsConfigurations: Map<string, AuthConfiguration> = new Map(),
29
59
  connectionsMap: ConnectionMapItem[] = [],
30
60
  configuration: AuthConfiguration = {}) {
@@ -35,26 +65,16 @@ export class MsalConnectionManager implements Connections {
35
65
  const providedConnections = connectionsConfigurations.size > 0 ? connectionsConfigurations : (configuration.connections || new Map())
36
66
 
37
67
  for (const [name, config] of providedConnections) {
38
- // Instantiate MsalTokenProvider for each connection
39
- this._connections.set(name, new MsalTokenProvider(config))
40
- if (name === MsalConnectionManager.DEFAULT_CONNECTION) {
68
+ this._connections.set(name, providerFactory(config))
69
+ if (name === ConnectionManager.DEFAULT_CONNECTION) {
41
70
  this._serviceConnectionConfiguration = config
42
71
  }
43
72
  }
44
73
 
45
74
  for (const [name, provider] of this._connections.entries()) {
46
75
  const cfg = provider.connectionSettings
47
- const authType = cfg?.authType ??
48
- (cfg?.certPemFile
49
- ? AuthType.Certificate
50
- : cfg?.clientSecret
51
- ? AuthType.ClientSecret
52
- : cfg?.WIDAssertionFile
53
- ? AuthType.WorkloadIdentity
54
- : cfg?.federatedClientId || cfg?.FICClientId
55
- ? AuthType.FederatedCredentials
56
- : 'none')
57
- logger.debug('connection "%s" clientId=%s tenantId=%s authType=%s', name, cfg?.clientId ?? '<none>', cfg?.tenantId ?? '<none>', authType)
76
+ const authType = resolveAuthType(cfg)
77
+ logger.debug('connection "%s" clientId=%s tenantId=%s authType=%s', name, redactString(cfg?.clientId, true) ?? '<none>', redactString(cfg?.tenantId, true) ?? '<none>', authType)
58
78
  }
59
79
 
60
80
  for (const item of this._connectionsMap) {
@@ -67,10 +87,10 @@ export class MsalConnectionManager implements Connections {
67
87
  * @param connectionName The name of the connection.
68
88
  * @returns The OAuth connection for the agent.
69
89
  */
70
- getConnection (connectionName: string): MsalTokenProvider {
90
+ getConnection (connectionName: string): AuthProvider {
71
91
  const conn = this._connections.get(connectionName)
72
92
  if (!conn) {
73
- throw new Error(`Connection not found: ${connectionName}`)
93
+ throw ExceptionHelper.generateException(Error, Errors.ConnectionNotFound, undefined, { connectionName })
74
94
  }
75
95
  return this.applyConnectionDefaults(conn)
76
96
  }
@@ -79,9 +99,9 @@ export class MsalConnectionManager implements Connections {
79
99
  * Get the default OAuth connection for the agent.
80
100
  * @returns The default OAuth connection for the agent.
81
101
  */
82
- getDefaultConnection (): MsalTokenProvider {
102
+ getDefaultConnection (): AuthProvider {
83
103
  if (this._connections.size === 0) {
84
- throw new Error('No connections found for this Agent in the Connections Configuration.')
104
+ throw ExceptionHelper.generateException(Error, Errors.NoConnectionsFoundInConfiguration)
85
105
  }
86
106
 
87
107
  // Return the wildcard map item instance.
@@ -91,7 +111,7 @@ export class MsalConnectionManager implements Connections {
91
111
  }
92
112
  }
93
113
 
94
- const conn = this._connections.values().next().value as MsalTokenProvider
114
+ const conn = this._connections.values().next().value as AuthProvider
95
115
 
96
116
  return this.applyConnectionDefaults(conn)
97
117
  }
@@ -114,9 +134,9 @@ export class MsalConnectionManager implements Connections {
114
134
  * ServiceUrl is: A regex to match with, or "*" for any serviceUrl value.
115
135
  * Connection is: A name in the 'Connections' list.
116
136
  */
117
- getTokenProvider (identity: JwtPayload, serviceUrl: string): MsalTokenProvider {
137
+ getTokenProvider (identity: JwtPayload, serviceUrl: string): AuthProvider {
118
138
  if (!identity) {
119
- throw new Error('Identity is required to get the token provider.')
139
+ throw ExceptionHelper.generateException(Error, Errors.IdentityRequiredForTokenProvider)
120
140
  }
121
141
 
122
142
  let audience
@@ -126,7 +146,7 @@ export class MsalConnectionManager implements Connections {
126
146
  audience = identity.aud
127
147
  }
128
148
 
129
- if (!audience || !serviceUrl) throw new Error('Audience and Service URL are required to get the token provider.')
149
+ if (!audience || !serviceUrl) throw ExceptionHelper.generateException(Error, Errors.AudienceAndServiceUrlRequiredForTokenProvider)
130
150
 
131
151
  if (this._connectionsMap.length === 0) {
132
152
  logger.debug('no connectionsMap, using default connection for serviceUrl=%s', serviceUrl)
@@ -154,7 +174,7 @@ export class MsalConnectionManager implements Connections {
154
174
  }
155
175
  }
156
176
  }
157
- throw new Error(`No connection found for audience: ${audience} and serviceUrl: ${serviceUrl}`)
177
+ throw ExceptionHelper.generateException(Error, Errors.NoConnectionForAudienceAndServiceUrl, undefined, { audience: String(audience), serviceUrl })
158
178
  }
159
179
 
160
180
  /**
@@ -163,7 +183,7 @@ export class MsalConnectionManager implements Connections {
163
183
  * @param activity The activity.
164
184
  * @returns The TokenProvider for the connection.
165
185
  */
166
- getTokenProviderFromActivity (identity: JwtPayload, activity: Activity): MsalTokenProvider {
186
+ getTokenProviderFromActivity (identity: JwtPayload, activity: Activity): AuthProvider {
167
187
  let connection = this.getTokenProvider(identity, activity.serviceUrl || '')
168
188
 
169
189
  // This is for the case where the Agentic BlueprintId is not the same as the AppId
@@ -186,29 +206,13 @@ export class MsalConnectionManager implements Connections {
186
206
  return this._serviceConnectionConfiguration
187
207
  }
188
208
 
189
- private applyConnectionDefaults (conn: MsalTokenProvider): MsalTokenProvider {
190
- if (conn.connectionSettings) {
191
- conn.connectionSettings.authorityEndpoint ??= conn.connectionSettings.authority || 'https://login.microsoftonline.com'
192
- conn.connectionSettings.authority ??= conn.connectionSettings.authorityEndpoint
193
- conn.connectionSettings.issuers ??= [
194
- 'https://api.botframework.com',
195
- `${resolveAuthority('https://sts.windows.net', conn.connectionSettings.tenantId)}/`,
196
- `${resolveAuthority(conn.connectionSettings.authorityEndpoint, conn.connectionSettings.tenantId)}/v2.0`
197
- ]
198
- // For backward compatibility
199
- if (conn.connectionSettings.federatedClientId) {
200
- conn.connectionSettings.FICClientId = conn.connectionSettings.federatedClientId
201
- }
202
-
203
- if (conn.connectionSettings.scopes?.length) {
204
- conn.connectionSettings.scope = conn.connectionSettings.scopes?.[0]
205
- }
206
-
207
- if (conn.connectionSettings.authorityEndpoint) {
208
- conn.connectionSettings.authority = conn.connectionSettings.authorityEndpoint
209
- }
210
- }
211
-
209
+ /**
210
+ * Applies provider-specific defaults to a resolved connection before returning it. The generic
211
+ * base performs no mutation; provider-specific managers may override this.
212
+ * @param conn The resolved auth provider.
213
+ * @returns The auth provider, possibly with defaults applied.
214
+ */
215
+ protected applyConnectionDefaults (conn: AuthProvider): AuthProvider {
212
216
  return conn
213
217
  }
214
218
  }
package/src/auth/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export * from './authConfiguration'
2
2
  export * from './authConstants'
3
3
  export * from './authProvider'
4
- export * from './msalTokenProvider'
4
+ export * from './msal/msalTokenProvider'
5
5
  export * from './request'
6
- export * from './msalTokenCredential'
7
- export * from './msalConnectionManager'
6
+ export * from './msal/msalTokenCredential'
7
+ export * from './connectionManager'
8
+ export * from './msal/msalConnectionManager'
9
+ export * from './sidecar/sidecarAuthProvider'
@@ -11,6 +11,15 @@ import jwt, { JwtHeader, JwtPayload, SignCallback, GetPublicKeyOrSecret } from '
11
11
  import { debug } from '@microsoft/agents-telemetry'
12
12
 
13
13
  const logger = debug('agents:jwt-middleware')
14
+ const jwksClients = new Map<string, JwksClient>()
15
+ const maxJwksClients = 100
16
+
17
+ /**
18
+ * Clears process-wide JWKS clients.
19
+ */
20
+ export function clearJwksClients (): void {
21
+ jwksClients.clear()
22
+ }
14
23
 
15
24
  /**
16
25
  * Builds the JWKS URI for the given token issuer and auth configuration.
@@ -24,6 +33,26 @@ export function buildJwksUri (iss: string, authConfig: AuthConfiguration): strin
24
33
  : `${resolveAuthority(authConfig.authorityEndpoint ?? authConfig.authority, authConfig.tenantId)}/discovery/v2.0/keys`
25
34
  }
26
35
 
36
+ export function getJwksClient (jwksUri: string): JwksClient {
37
+ // Check if a client for this JWKS URI already exists in the cache.
38
+ let client = jwksClients.get(jwksUri)
39
+ if (!client) {
40
+ client = jwksRsa({ jwksUri })
41
+ jwksClients.set(jwksUri, client)
42
+ while (jwksClients.size > maxJwksClients) {
43
+ const oldestKey = jwksClients.keys().next().value
44
+ if (oldestKey === undefined) {
45
+ break
46
+ }
47
+ jwksClients.delete(oldestKey)
48
+ }
49
+ } else {
50
+ jwksClients.delete(jwksUri)
51
+ jwksClients.set(jwksUri, client)
52
+ }
53
+ return client
54
+ }
55
+
27
56
  /**
28
57
  * Verifies the JWT token.
29
58
  * @param raw The raw JWT token.
@@ -55,9 +84,10 @@ const verifyToken = async (raw: string, config: AuthConfiguration): Promise<JwtP
55
84
  const jwksUri = buildJwksUri(payload.iss as string, authConfig)
56
85
 
57
86
  logger.debug(`fetching keys from ${jwksUri}`)
58
- const jwksClient: JwksClient = jwksRsa({ jwksUri })
87
+ const jwksClient = getJwksClient(jwksUri)
59
88
 
60
89
  const getKey: GetPublicKeyOrSecret = (header: JwtHeader, callback: SignCallback) => {
90
+ // Retrieve the public, issuer-wide signing key from the JWKS endpoint using the kid from the token header.
61
91
  jwksClient.getSigningKey(header.kid, (err: Error | null, key: SigningKey | undefined): void => {
62
92
  if (err) {
63
93
  logger.error('jwksClient.getSigningKey ', JSON.stringify(err))
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { AuthConfiguration, resolveAuthority } from '../authConfiguration'
7
+ import type { ConnectionMapItem } from '../settings'
8
+ import { AuthProvider } from '../authProvider'
9
+ import { ConnectionManager, defaultAuthProviderFactory } from '../connectionManager'
10
+
11
+ /**
12
+ * Connection manager backed by MSAL (and, for `EntraAuthSideCar` connections, the Entra sidecar
13
+ * provider). A thin convenience subclass of {@link ConnectionManager} that supplies the default
14
+ * provider factory and applies MSAL-specific connection defaults.
15
+ */
16
+ export class MsalConnectionManager extends ConnectionManager {
17
+ constructor (
18
+ connectionsConfigurations: Map<string, AuthConfiguration> = new Map(),
19
+ connectionsMap: ConnectionMapItem[] = [],
20
+ configuration: AuthConfiguration = {}) {
21
+ super(defaultAuthProviderFactory, connectionsConfigurations, connectionsMap, configuration)
22
+ }
23
+
24
+ protected applyConnectionDefaults (conn: AuthProvider): AuthProvider {
25
+ if (conn.connectionSettings) {
26
+ conn.connectionSettings.authorityEndpoint ??= conn.connectionSettings.authority || 'https://login.microsoftonline.com'
27
+ conn.connectionSettings.authority ??= conn.connectionSettings.authorityEndpoint
28
+ conn.connectionSettings.issuers ??= [
29
+ 'https://api.botframework.com',
30
+ `${resolveAuthority('https://sts.windows.net', conn.connectionSettings.tenantId)}/`,
31
+ `${resolveAuthority(conn.connectionSettings.authorityEndpoint, conn.connectionSettings.tenantId)}/v2.0`
32
+ ]
33
+ // For backward compatibility
34
+ if (conn.connectionSettings.federatedClientId) {
35
+ conn.connectionSettings.FICClientId = conn.connectionSettings.federatedClientId
36
+ }
37
+
38
+ if (conn.connectionSettings.scopes?.length) {
39
+ conn.connectionSettings.scope = conn.connectionSettings.scopes?.[0]
40
+ }
41
+
42
+ if (conn.connectionSettings.authorityEndpoint) {
43
+ conn.connectionSettings.authority = conn.connectionSettings.authorityEndpoint
44
+ }
45
+
46
+ // .NET parity alias: keep altBlueprintConnectionName and alternateBlueprintConnectionName in sync.
47
+ conn.connectionSettings.altBlueprintConnectionName ??= conn.connectionSettings.alternateBlueprintConnectionName
48
+ if (conn.connectionSettings.altBlueprintConnectionName) {
49
+ conn.connectionSettings.alternateBlueprintConnectionName = conn.connectionSettings.altBlueprintConnectionName
50
+ }
51
+ }
52
+
53
+ return conn
54
+ }
55
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type { ConnectionSettingsBase } from '../settings'
7
+
8
+ /**
9
+ * Connection settings for the MSAL-backed authentication providers (the default providers used for
10
+ * every {@link AuthType} except `EntraAuthSideCar`).
11
+ *
12
+ * @remarks
13
+ * Mirrors the .NET `Microsoft.Agents.Authentication.Msal.Model.ConnectionSettings` class. Node.js uses
14
+ * PEM certificate files (`certPemFile`/`certKeyFile`) rather than the Windows certificate store, so the
15
+ * .NET `CertThumbprint`/`CertSubjectName`/`CertStoreName`/`ValidCertificateOnly` settings have no JS
16
+ * equivalent; all other MSAL properties map one-to-one.
17
+ */
18
+ export interface MsalConnectionSettings extends ConnectionSettingsBase {
19
+ /**
20
+ * The client secret for the authentication configuration.
21
+ */
22
+ clientSecret?: string
23
+
24
+ /**
25
+ * The path to the certificate PEM file.
26
+ */
27
+ certPemFile?: string
28
+
29
+ /**
30
+ * The path to the certificate key file.
31
+ */
32
+ certKeyFile?: string
33
+
34
+ /**
35
+ * Indicates whether to send the X5C param or not (for SNI authentication).
36
+ */
37
+ sendX5C?: boolean
38
+
39
+ /**
40
+ * @deprecated Use federatedClientId instead.
41
+ *
42
+ * The FIC (First-Party Integration Channel) client ID.
43
+ */
44
+ FICClientId?: string
45
+
46
+ /**
47
+ * @deprecated Use `authType` set to `'WorkloadIdentity'` and `federatedTokenFile` instead.
48
+ *
49
+ * The path to K8s provided token.
50
+ */
51
+ WIDAssertionFile?: string
52
+
53
+ /**
54
+ * The Azure region for ESTS-R regional token acquisition (e.g. 'westus', 'eastus').
55
+ * When set, MSAL routes token requests to the specified regional endpoint.
56
+ * See https://learn.microsoft.com/en-us/entra/msal/javascript/node/regional-authorities for details.
57
+ */
58
+ azureRegion?: string
59
+
60
+ /**
61
+ * The path to the federated token file used for Workload Identity authentication.
62
+ */
63
+ federatedTokenFile?: string
64
+
65
+ /**
66
+ * Sets the resource URL for Identity Proxy Manager (IDPM).
67
+ *
68
+ * @remarks
69
+ * Set this to the appropriate resource identifier when the application is running in an environment,
70
+ * such as a Foundry container, that exposes Managed Identity through a container-specific IMDS endpoint.
71
+ * This setting is only meaningful when using Identity Proxy Manager (AuthType.IdentityProxyManager) for authentication.
72
+ */
73
+ idpmResource?: string
74
+
75
+ /**
76
+ * The federated client ID for the authentication configuration, used for workload identity federation scenarios.
77
+ */
78
+ federatedClientId?: string
79
+ }
@@ -1,5 +1,6 @@
1
1
  import { GetTokenOptions, TokenCredential } from '@azure/core-auth'
2
- import { AuthConfiguration, MsalTokenProvider } from './'
2
+ import { AuthConfiguration } from '../authConfiguration'
3
+ import { MsalTokenProvider } from './msalTokenProvider'
3
4
  import { debug } from '@microsoft/agents-telemetry'
4
5
 
5
6
  const logger = debug('agents:msal')