@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
@@ -4,13 +4,29 @@
4
4
  */
5
5
 
6
6
  import { debug } from '@microsoft/agents-telemetry'
7
- import type { ConnectionMapItem } from './msalConnectionManager'
7
+ import { ExceptionHelper } from '@microsoft/agents-activity'
8
+ import { Errors } from '../errorHelper'
9
+ import type { MsalConnectionSettings } from './msal/msalConnectionSettings'
10
+ import type { SidecarConnectionSettings } from './sidecar/sidecarConnectionSettings'
11
+
12
+ export type { MsalConnectionSettings } from './msal/msalConnectionSettings'
13
+ export type { SidecarConnectionSettings } from './sidecar/sidecarConnectionSettings'
8
14
 
9
15
  const logger = debug('agents:authConfiguration')
10
16
 
11
17
  const DEFAULT_CONNECTION = 'serviceConnection'
12
18
  const AUTHORITY_DEFAULT = 'https://login.microsoftonline.com'
13
19
 
20
+ /**
21
+ * A single entry in the connections map used to route an inbound activity
22
+ * (matched by audience and/or serviceUrl) to a named connection.
23
+ */
24
+ export interface ConnectionMapItem {
25
+ serviceUrl: string
26
+ audience?: string
27
+ connection: string
28
+ }
29
+
14
30
  export const DEFAULT_CONNECTION_MAP: ConnectionMapItem = { serviceUrl: '*', connection: DEFAULT_CONNECTION }
15
31
 
16
32
  export type ConnectionKeys = keyof Omit<AuthConfiguration, 'connections' | 'connectionsMap'>
@@ -53,8 +69,14 @@ export function applyDefaultSettings (config: AuthConfiguration) {
53
69
  settings.authority = settings.authorityEndpoint
54
70
  }
55
71
 
72
+ // .NET parity alias: keep altBlueprintConnectionName and alternateBlueprintConnectionName in sync.
73
+ settings.altBlueprintConnectionName ??= settings.alternateBlueprintConnectionName
74
+ if (settings.altBlueprintConnectionName) {
75
+ settings.alternateBlueprintConnectionName = settings.altBlueprintConnectionName
76
+ }
77
+
56
78
  if (!settings.clientId && process.env.NODE_ENV?.toLowerCase() === 'production') {
57
- throw new Error('ClientId required in production')
79
+ throw ExceptionHelper.generateException(Error, Errors.ClientIdRequiredInProduction)
58
80
  }
59
81
 
60
82
  const defaultConnections = settings.connections?.size
@@ -171,9 +193,16 @@ function getDefaultIssuers (tenantId: string, authority: string) : string[] {
171
193
  }
172
194
 
173
195
  /**
174
- * Represents the authentication configuration.
196
+ * Connection-level settings common to every authentication provider.
197
+ *
198
+ * @remarks
199
+ * Mirrors the .NET `Microsoft.Agents.Authentication.ConnectionSettingsBase` abstract class: these are
200
+ * the credential-agnostic properties shared by all connection types. Provider-specific settings
201
+ * extend this base (see {@link MsalConnectionSettings} for MSAL and {@link SidecarConnectionSettings}
202
+ * for the Entra sidecar). The active provider for a connection is selected
203
+ * by {@link ConnectionSettingsBase.authType | authType}; see `AuthProviderFactory`.
175
204
  */
176
- export interface AuthConfiguration {
205
+ export interface ConnectionSettingsBase {
177
206
  /**
178
207
  * The tenant ID for the authentication configuration.
179
208
  */
@@ -185,24 +214,9 @@ export interface AuthConfiguration {
185
214
  clientId?: string
186
215
 
187
216
  /**
188
- * The client secret for the authentication configuration.
189
- */
190
- clientSecret?: string
191
-
192
- /**
193
- * The path to the certificate PEM file.
194
- */
195
- certPemFile?: string
196
-
197
- /**
198
- * The path to the certificate key file.
199
- */
200
- certKeyFile?: string
201
-
202
- /**
203
- * Indicates whether to send the X5C param or not (for SNI authentication).
217
+ * The authentication type for the connection.
204
218
  */
205
- sendX5C?: boolean
219
+ authType?: AuthType | string
206
220
 
207
221
  /**
208
222
  * A list of valid issuers for the authentication configuration.
@@ -214,13 +228,6 @@ export interface AuthConfiguration {
214
228
  */
215
229
  connectionName?: string
216
230
 
217
- /**
218
- * @deprecated Use federatedClientId instead.
219
- *
220
- * The FIC (First-Party Integration Channel) client ID.
221
- */
222
- FICClientId?: string,
223
-
224
231
  /**
225
232
  * @deprecated Use authorityEndpoint instead.
226
233
  *
@@ -234,78 +241,89 @@ export interface AuthConfiguration {
234
241
  authority?: string
235
242
 
236
243
  /**
237
- * @deprecated Use scopes instead.
244
+ * Entra Authentication Endpoint to use.
245
+ *
246
+ * @remarks
247
+ * If not populated the Entra Public Cloud endpoint is assumed.
248
+ * This example of Public Cloud Endpoint is https://login.microsoftonline.com
249
+ * see also https://learn.microsoft.com/entra/identity-platform/authentication-national-cloud
238
250
  */
239
- scope?: string
251
+ authorityEndpoint?: string
240
252
 
241
253
  /**
242
- * A map of connection names to their respective authentication configurations.
254
+ * @deprecated Use scopes instead.
243
255
  */
244
- connections?: Map<string, AuthConfiguration>
256
+ scope?: string
245
257
 
246
258
  /**
247
- * A list of connection map items to map service URLs to connection names.
259
+ * The scopes for the authentication configuration.
248
260
  */
249
- connectionsMap?: ConnectionMapItem[],
261
+ scopes?: string[]
250
262
 
251
263
  /**
252
264
  * An optional alternative blueprint Connection name used when constructing a connector client.
253
- */
254
- altBlueprintConnectionName?: string
255
-
256
- /**
257
- * @deprecated Use `authType` set to `'WorkloadIdentity'` and `federatedTokenFile` instead.
258
265
  *
259
- * The path to K8s provided token.
260
- */
261
- WIDAssertionFile?: string
262
-
263
- /**
264
- * The Azure region for ESTS-R regional token acquisition (e.g. 'westus', 'eastus').
265
- * When set, MSAL routes token requests to the specified regional endpoint.
266
- * See https://learn.microsoft.com/en-us/entra/msal/javascript/node/regional-authorities for details.
267
- */
268
- azureRegion?: string
269
-
270
- /**
271
- * The authentication type for the connection.
272
- */
273
- authType?: AuthType | string
274
-
275
- /**
276
- * The path to the federated token file used for Workload Identity authentication.
266
+ * @remarks
267
+ * Equivalent to the .NET `AlternateBlueprintConnectionName` connection setting. {@link alternateBlueprintConnectionName}
268
+ * is an alias of this property that matches the .NET name exactly; when both are provided this property takes precedence.
277
269
  */
278
- federatedTokenFile?: string
270
+ altBlueprintConnectionName?: string
279
271
 
280
272
  /**
281
- * Sets the resource URL for Identity Proxy Manager (IDPM).
273
+ * Alias of {@link altBlueprintConnectionName} named to match the .NET `AlternateBlueprintConnectionName`
274
+ * connection setting exactly.
282
275
  *
283
276
  * @remarks
284
- * Set this to the appropriate resource identifier when the application is running in an environment,
285
- * such as a Foundry container, that exposes Managed Identity through a container-specific IMDS endpoint.
286
- * This setting is only meaningful when using Identity Proxy Manager (AuthType.IdentityProxyManager) for authentication.
277
+ * Provided for stricter .NET parity. The two properties are kept in sync during configuration normalization;
278
+ * {@link altBlueprintConnectionName} takes precedence when both are set.
287
279
  */
288
- idpmResource?: string
280
+ alternateBlueprintConnectionName?: string
281
+ }
289
282
 
283
+ /**
284
+ * The complete settings for a single connection, across every provider.
285
+ *
286
+ * @remarks
287
+ * This is the per-connection unit that an `AuthProvider` consumes: it combines the common base
288
+ * ({@link ConnectionSettingsBase}) with the MSAL settings ({@link MsalConnectionSettings}) and the
289
+ * Entra sidecar settings ({@link SidecarConnectionSettings}). The active provider for a connection is
290
+ * selected by `authType`, so only the subset of these properties relevant to that provider is used.
291
+ *
292
+ * Conceptually this is the consumer-facing counterpart to the {@link AuthConfiguration} *container*:
293
+ * each value in {@link AuthConfiguration.connections} is the settings for one connection. It parallels
294
+ * the .NET `IConnectionSettings` (a single connection's settings) as distinct from the connection
295
+ * registry. {@link AuthConfiguration} extends this type for backward compatibility (it doubles as the
296
+ * settings for the legacy single connection).
297
+ */
298
+ export interface ConnectionSettings extends MsalConnectionSettings, SidecarConnectionSettings {}
299
+
300
+ /**
301
+ * Represents the authentication configuration.
302
+ *
303
+ * @remarks
304
+ * The agent-level authentication container. It owns the connection registry
305
+ * (`connections`/`connectionsMap`) and, for backward compatibility, also extends
306
+ * {@link ConnectionSettings} so the legacy single-connection shape (a flat config with `clientId`,
307
+ * `clientSecret`, etc.) keeps working — in that mode the top-level object *is* the one connection's
308
+ * settings. The active provider for each connection is dispatched by `authType` via
309
+ * `AuthProviderFactory`, so a single connection only uses the subset of these properties relevant to
310
+ * its provider.
311
+ */
312
+ export interface AuthConfiguration extends ConnectionSettings {
290
313
  /**
291
- * Entra Authentication Endpoint to use.
314
+ * The connection registry: a map of connection name to that connection's settings.
292
315
  *
293
316
  * @remarks
294
- * If not populated the Entra Public Cloud endpoint is assumed.
295
- * This example of Public Cloud Endpoint is https://login.microsoftonline.com
296
- * see also https://learn.microsoft.com/entra/identity-platform/authentication-national-cloud
297
- */
298
- authorityEndpoint?: string
299
-
300
- /**
301
- * The federated client ID for the authentication configuration, used for workload identity federation scenarios.
317
+ * Each value is the {@link ConnectionSettings} for one connection (typed as {@link AuthConfiguration}
318
+ * for backward compatibility; only the connection-settings subset is consumed). An `AuthProvider` is
319
+ * created per entry by `AuthProviderFactory`, dispatched by `authType`.
302
320
  */
303
- federatedClientId?: string
321
+ connections?: Map<string, AuthConfiguration>
304
322
 
305
323
  /**
306
- * The scopes for the authentication configuration.
324
+ * A list of connection map items to map service URLs to connection names.
307
325
  */
308
- scopes?: string[]
326
+ connectionsMap?: ConnectionMapItem[]
309
327
  }
310
328
 
311
329
  /**
@@ -319,5 +337,42 @@ export enum AuthType {
319
337
  SystemManagedIdentity = 'SystemManagedIdentity',
320
338
  FederatedCredentials = 'FederatedCredentials',
321
339
  WorkloadIdentity = 'WorkloadIdentity',
322
- IdentityProxyManager = 'IdentityProxyManager'
340
+ IdentityProxyManager = 'IdentityProxyManager',
341
+ EntraAuthSideCar = 'EntraAuthSideCar'
342
+ }
343
+
344
+ /**
345
+ * Resolves the authentication type for a given authentication configuration.
346
+ * @remarks
347
+ * The function checks various properties of the `authConfig` object to determine the appropriate authentication type.
348
+ * It returns a string representing the resolved authentication type or 'unknown' if it cannot be determined.
349
+ * @param authConfig The authentication configuration object.
350
+ * @returns The resolved authentication type as a string or 'unknown' if it cannot be determined.
351
+ */
352
+ export function resolveAuthType (authConfig?: AuthConfiguration): AuthType | string {
353
+ if (!authConfig) {
354
+ return 'none'
355
+ }
356
+ if (authConfig.authType) {
357
+ return authConfig.authType
358
+ }
359
+ if (authConfig.WIDAssertionFile !== undefined) {
360
+ return AuthType.WorkloadIdentity
361
+ }
362
+ if (authConfig.federatedClientId !== undefined || authConfig.FICClientId !== undefined) {
363
+ return AuthType.FederatedCredentials
364
+ }
365
+ if (authConfig.clientSecret !== undefined) {
366
+ return AuthType.ClientSecret
367
+ }
368
+ if (authConfig.certPemFile !== undefined && authConfig.certKeyFile !== undefined) {
369
+ return AuthType.Certificate
370
+ }
371
+ if (authConfig.clientSecret === undefined && authConfig.certPemFile === undefined && authConfig.certKeyFile === undefined) {
372
+ return AuthType.UserManagedIdentity
373
+ }
374
+ if (authConfig.idpmResource !== undefined) {
375
+ return AuthType.IdentityProxyManager
376
+ }
377
+ return 'unknown'
323
378
  }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { debug } from '@microsoft/agents-telemetry'
7
+ import { ExceptionHelper } from '@microsoft/agents-activity'
8
+ import { Errors } from '../../errorHelper'
9
+ import { AuthConfiguration } from '../authConfiguration'
10
+ import { AuthProvider } from '../authProvider'
11
+ import { SidecarHttpClient } from './sidecarHttpClient'
12
+ import { resolveTokenExpiry } from './sidecarTokenExpiry'
13
+ import {
14
+ ResolvedSidecarConnectionSettings,
15
+ SidecarRequestOptions,
16
+ toSidecarConnectionSettings
17
+ } from './sidecarModels'
18
+
19
+ const logger = debug('agents:sidecar')
20
+
21
+ const GUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
22
+
23
+ // Refresh slightly ahead of the real expiry so callers never receive a token that expires mid-flight.
24
+ const EXPIRY_BUFFER_MS = 30 * 1000
25
+
26
+ // Hard upper bound on cached entries; protects memory when many distinct identities are served.
27
+ const MAX_CACHE_ENTRIES = 500
28
+
29
+ interface CachedToken {
30
+ token: string
31
+ expiresOn: number
32
+ }
33
+
34
+ /**
35
+ * Authentication provider that delegates token acquisition to the Microsoft Entra Agent ID
36
+ * sidecar (agent container). This replaces MSAL at the connection layer, using the sidecar's
37
+ * `/AuthorizationHeaderUnauthenticated/{serviceName}` endpoint for app-only and agentic identity
38
+ * flows. The sidecar performs the full Blueprint→Instance→User chain internally; no MSAL exchange is
39
+ * performed in-process.
40
+ */
41
+ export class SidecarAuthProvider implements AuthProvider {
42
+ public readonly connectionSettings?: AuthConfiguration
43
+ private readonly _settings: ResolvedSidecarConnectionSettings
44
+ private readonly _httpClient: SidecarHttpClient
45
+ private readonly _tokenCache: Map<string, CachedToken> = new Map()
46
+
47
+ /**
48
+ * Creates a new {@link SidecarAuthProvider}.
49
+ * @param connectionSettings The connection authentication configuration.
50
+ */
51
+ constructor (connectionSettings?: AuthConfiguration) {
52
+ this.connectionSettings = connectionSettings
53
+ this._settings = toSidecarConnectionSettings(connectionSettings)
54
+
55
+ const resolvedUrl = SidecarHttpClient.resolveBaseUrl(this._settings.sidecarBaseUrl)
56
+ SidecarHttpClient.validateBaseUrl(resolvedUrl, this._settings.bypassLocalNetworkRestriction)
57
+ this._httpClient = new SidecarHttpClient(resolvedUrl, this._settings.requestTimeout, this._settings.retryCount)
58
+ logger.debug('SidecarAuthProvider initialized serviceName=%s blueprintServiceName=%s', this._settings.serviceName, this._settings.blueprintServiceName)
59
+ }
60
+
61
+ /**
62
+ * Acquires an app-only access token from the sidecar.
63
+ * @param scope The scope for the token.
64
+ */
65
+ async getAccessToken (scope: string): Promise<string>
66
+ /**
67
+ * Acquires an app-only access token from the sidecar.
68
+ * @param authConfig The authentication configuration. Ignored by the sidecar provider, which
69
+ * owns the credential and derives the token from its configured service name; accepted only to
70
+ * satisfy the {@link AuthProvider} overload.
71
+ * @param scope The scope for the token.
72
+ */
73
+ async getAccessToken (authConfig: AuthConfiguration, scope: string): Promise<string>
74
+ async getAccessToken (authConfigOrScope: AuthConfiguration | string, scope?: string): Promise<string> {
75
+ const actualScope = typeof authConfigOrScope === 'string' ? authConfigOrScope : scope
76
+ const options: SidecarRequestOptions = {
77
+ scopes: actualScope ? [actualScope] : this._settings.scopes,
78
+ requestAppToken: true
79
+ }
80
+ return this.getCachedToken(this._settings.serviceName, options)
81
+ }
82
+
83
+ /**
84
+ * Acquires the Blueprint (agent application) token from the sidecar.
85
+ * @param tenantId The tenant ID.
86
+ * @param agentAppInstanceId The agent instance ID (from the inbound activity); maps to `AgentIdentity`.
87
+ */
88
+ async getAgenticApplicationToken (tenantId: string, agentAppInstanceId: string): Promise<string> {
89
+ this.ensureConnectionSettings()
90
+ const options: SidecarRequestOptions = {
91
+ agentIdentity: agentAppInstanceId,
92
+ tenant: tenantId
93
+ }
94
+ return this.getCachedToken(this._settings.blueprintServiceName, options)
95
+ }
96
+
97
+ /**
98
+ * Acquires the autonomous agent (instance) token from the sidecar for the configured resource.
99
+ * @param tenantId The tenant ID.
100
+ * @param agentAppInstanceId The agent instance ID (from the inbound activity); maps to `AgentIdentity`.
101
+ */
102
+ async getAgenticInstanceToken (tenantId: string, agentAppInstanceId: string): Promise<string> {
103
+ this.ensureConnectionSettings()
104
+ const options: SidecarRequestOptions = {
105
+ agentIdentity: agentAppInstanceId,
106
+ requestAppToken: true,
107
+ tenant: tenantId,
108
+ scopes: this._settings.scopes
109
+ }
110
+ return this.getCachedToken(this._settings.serviceName, options)
111
+ }
112
+
113
+ /**
114
+ * Acquires the agentic user token from the sidecar for the configured resource.
115
+ * @param tenantId The tenant ID.
116
+ * @param agentAppInstanceId The agent instance ID (from the inbound activity); maps to `AgentIdentity`.
117
+ * @param upn The agentic user identifier. A GUID is sent as `AgentUserId`; otherwise as `AgentUsername`.
118
+ * @param scopes The OAuth scopes to request.
119
+ */
120
+ async getAgenticUserToken (tenantId: string, agentAppInstanceId: string, upn: string, scopes: string[]): Promise<string> {
121
+ this.ensureConnectionSettings()
122
+ const isObjectId = GUID_REGEX.test(upn ?? '')
123
+ const options: SidecarRequestOptions = {
124
+ agentIdentity: agentAppInstanceId,
125
+ agentUsername: isObjectId ? undefined : upn,
126
+ agentUserId: isObjectId ? upn : undefined,
127
+ tenant: tenantId,
128
+ scopes: scopes ?? this._settings.scopes
129
+ }
130
+ return this.getCachedToken(this._settings.serviceName, options)
131
+ }
132
+
133
+ /**
134
+ * On-behalf-of token exchange — not supported by the sidecar provider in Phase 1.
135
+ */
136
+ async acquireTokenOnBehalfOf (scopes: string[], oboAssertion: string): Promise<string>
137
+ async acquireTokenOnBehalfOf (authConfig: AuthConfiguration, scopes: string[], oboAssertion: string): Promise<string>
138
+ async acquireTokenOnBehalfOf (
139
+ _authConfigOrScopes: AuthConfiguration | string[],
140
+ _scopesOrOboAssertion?: string[] | string,
141
+ _oboAssertion?: string
142
+ ): Promise<string> {
143
+ throw ExceptionHelper.generateException(Error, Errors.OnBehalfOfNotSupportedBySidecar)
144
+ }
145
+
146
+ /**
147
+ * Checks sidecar availability via the `/healthz` endpoint.
148
+ * @returns `true` when the sidecar is reachable and healthy.
149
+ */
150
+ async isHealthy (): Promise<boolean> {
151
+ return this._httpClient.isHealthy()
152
+ }
153
+
154
+ private ensureConnectionSettings (): void {
155
+ if (!this.connectionSettings) {
156
+ throw ExceptionHelper.generateException(Error, Errors.SidecarConnectionSettingsRequired)
157
+ }
158
+ }
159
+
160
+ private async getCachedToken (serviceName: string, options: SidecarRequestOptions): Promise<string> {
161
+ const forceRefresh = options.forceRefresh === true
162
+ const cacheKey = SidecarAuthProvider.buildCacheKey(serviceName, options)
163
+
164
+ const cached = this.cacheGet(cacheKey, forceRefresh)
165
+ if (cached) {
166
+ return cached
167
+ }
168
+
169
+ const result = await this._httpClient.getAuthorizationHeaderUnauthenticated(serviceName, options)
170
+ // The hosting stack always transmits the token as `Bearer {token}`. Reject any other scheme
171
+ // (e.g. PoP) rather than silently emitting an invalid Authorization header.
172
+ if (result.scheme && result.scheme.toLowerCase() !== 'bearer') {
173
+ throw ExceptionHelper.generateException(Error, Errors.SidecarUnsupportedAuthScheme, undefined, { scheme: result.scheme })
174
+ }
175
+ this.cacheSet(cacheKey, { token: result.token, expiresOn: resolveTokenExpiry(result.token) })
176
+ return result.token
177
+ }
178
+
179
+ private cacheGet (cacheKey: string, forceRefresh: boolean): string | undefined {
180
+ const cached = this._tokenCache.get(cacheKey)
181
+ if (cached) {
182
+ if (!forceRefresh && cached.expiresOn >= Date.now() + EXPIRY_BUFFER_MS) {
183
+ return cached.token
184
+ }
185
+ this._tokenCache.delete(cacheKey)
186
+ }
187
+ return undefined
188
+ }
189
+
190
+ private cacheSet (cacheKey: string, token: CachedToken): void {
191
+ this._tokenCache.set(cacheKey, token)
192
+ if (this._tokenCache.size > MAX_CACHE_ENTRIES) {
193
+ this.pruneExpiredEntries()
194
+ if (this._tokenCache.size > MAX_CACHE_ENTRIES) {
195
+ this.evictNearestExpiry()
196
+ }
197
+ }
198
+ }
199
+
200
+ private pruneExpiredEntries (): void {
201
+ const now = Date.now()
202
+ for (const [key, value] of this._tokenCache) {
203
+ if (value.expiresOn <= now) {
204
+ this._tokenCache.delete(key)
205
+ }
206
+ }
207
+ }
208
+
209
+ private evictNearestExpiry (): void {
210
+ const overflow = this._tokenCache.size - MAX_CACHE_ENTRIES
211
+ if (overflow <= 0) {
212
+ return
213
+ }
214
+ const sorted = [...this._tokenCache.entries()].sort((a, b) => a[1].expiresOn - b[1].expiresOn)
215
+ for (let i = 0; i < overflow; i++) {
216
+ this._tokenCache.delete(sorted[i][0])
217
+ }
218
+ }
219
+
220
+ private static buildCacheKey (serviceName: string, options: SidecarRequestOptions): string {
221
+ const scopes = options.scopes
222
+ ? [...new Set(options.scopes.filter((s) => s && s.trim()))].sort().join(' ')
223
+ : ''
224
+ return [
225
+ serviceName,
226
+ options.agentIdentity ?? '',
227
+ options.agentUsername ?? '',
228
+ options.agentUserId ?? '',
229
+ options.tenant ?? '',
230
+ options.requestAppToken === true ? 'app' : 'user',
231
+ scopes
232
+ ].join('|')
233
+ }
234
+ }
@@ -0,0 +1,72 @@
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 Entra Agent ID sidecar (agent container) authentication provider, used when a
10
+ * connection's `authType` is `'EntraAuthSideCar'`.
11
+ *
12
+ * @remarks
13
+ * Mirrors the .NET `Microsoft.Agents.Authentication.EntraAuthSidecar.Model.SidecarConnectionSettings`
14
+ * class, which likewise derives from `ConnectionSettingsBase`. These are the configuration-level
15
+ * properties; they are normalized (with defaults applied) before use by the sidecar token provider.
16
+ */
17
+ export interface SidecarConnectionSettings extends ConnectionSettingsBase {
18
+ /**
19
+ * Optional base URL of the Entra Agent ID sidecar (agent container).
20
+ *
21
+ * @remarks
22
+ * Only used when `authType` is `'EntraAuthSideCar'`. Resolution order:
23
+ * `SIDECAR_URL` environment variable > this setting > `http://localhost:5178`.
24
+ * Regardless of how it is resolved, the host must be a loopback/private address
25
+ * unless `bypassLocalNetworkRestriction` is set.
26
+ */
27
+ sidecarBaseUrl?: string
28
+
29
+ /**
30
+ * The configured downstream API service name in the sidecar's DownstreamApis configuration.
31
+ *
32
+ * @remarks
33
+ * Only used when `authType` is `'EntraAuthSideCar'`. Defaults to `'default'`.
34
+ */
35
+ serviceName?: string
36
+
37
+ /**
38
+ * The sidecar downstream API name used to acquire the Blueprint (agent application) token for the
39
+ * agentic FIC chain.
40
+ *
41
+ * @remarks
42
+ * Only used when `authType` is `'EntraAuthSideCar'`. Defaults to `'agenticblueprint'`. This
43
+ * downstream API must be configured app-only with the `api://AzureAdTokenExchange/.default` scope.
44
+ */
45
+ blueprintServiceName?: string
46
+
47
+ /**
48
+ * When `true`, disables the loopback/private-address safety check on the resolved sidecar base URL.
49
+ *
50
+ * @remarks
51
+ * UNSAFE. Leave this `false` in all normal deployments. Only enable it for a carefully validated
52
+ * private-network configuration where the sidecar is reachable at a non-private address that the
53
+ * operator explicitly trusts. Only used when `authType` is `'EntraAuthSideCar'`.
54
+ */
55
+ bypassLocalNetworkRestriction?: boolean
56
+
57
+ /**
58
+ * HTTP request timeout (in milliseconds) for sidecar calls.
59
+ *
60
+ * @remarks
61
+ * Only used when `authType` is `'EntraAuthSideCar'`. Defaults to 30000 (30 seconds).
62
+ */
63
+ requestTimeout?: number
64
+
65
+ /**
66
+ * Number of retry attempts for transient sidecar failures (5xx, 408, 429, network/timeout).
67
+ *
68
+ * @remarks
69
+ * Only used when `authType` is `'EntraAuthSideCar'`. Defaults to 3.
70
+ */
71
+ retryCount?: number
72
+ }