@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.
- package/dist/package.json +3 -3
- package/dist/src/app/streaming/streamingResponse.d.ts +11 -0
- package/dist/src/app/streaming/streamingResponse.js +21 -0
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/auth/MemoryCache.d.ts +2 -1
- package/dist/src/auth/MemoryCache.js +7 -1
- package/dist/src/auth/MemoryCache.js.map +1 -1
- package/dist/src/auth/authConfiguration.d.ts +1 -1
- package/dist/src/auth/authConfiguration.js +36 -1
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/authConstants.d.ts +7 -7
- package/dist/src/auth/authConstants.js.map +1 -1
- package/dist/src/auth/connectionManager.d.ts +92 -0
- package/dist/src/auth/{msalConnectionManager.js → connectionManager.js} +50 -48
- package/dist/src/auth/connectionManager.js.map +1 -0
- package/dist/src/auth/index.d.ts +5 -3
- package/dist/src/auth/index.js +5 -3
- package/dist/src/auth/index.js.map +1 -1
- package/dist/src/auth/jwt-middleware.d.ts +6 -0
- package/dist/src/auth/jwt-middleware.js +32 -1
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msal/msalConnectionManager.d.ts +17 -0
- package/dist/src/auth/msal/msalConnectionManager.js +50 -0
- package/dist/src/auth/msal/msalConnectionManager.js.map +1 -0
- package/dist/src/auth/msal/msalConnectionSettings.d.ts +68 -0
- package/dist/src/auth/msal/msalConnectionSettings.js +7 -0
- package/dist/src/auth/msal/msalConnectionSettings.js.map +1 -0
- package/dist/src/auth/{msalTokenCredential.d.ts → msal/msalTokenCredential.d.ts} +1 -1
- package/dist/src/auth/{msalTokenCredential.js → msal/msalTokenCredential.js} +2 -2
- package/dist/src/auth/msal/msalTokenCredential.js.map +1 -0
- package/dist/src/auth/{msalTokenProvider.d.ts → msal/msalTokenProvider.d.ts} +23 -3
- package/dist/src/auth/{msalTokenProvider.js → msal/msalTokenProvider.js} +264 -160
- package/dist/src/auth/msal/msalTokenProvider.js.map +1 -0
- package/dist/src/auth/settings.d.ts +142 -133
- package/dist/src/auth/settings.js +49 -5
- package/dist/src/auth/settings.js.map +1 -1
- package/dist/src/auth/sidecar/sidecarAuthProvider.d.ts +74 -0
- package/dist/src/auth/sidecar/sidecarAuthProvider.js +181 -0
- package/dist/src/auth/sidecar/sidecarAuthProvider.js.map +1 -0
- package/dist/src/auth/sidecar/sidecarConnectionSettings.d.ts +65 -0
- package/dist/src/auth/sidecar/sidecarConnectionSettings.js +7 -0
- package/dist/src/auth/sidecar/sidecarConnectionSettings.js.map +1 -0
- package/dist/src/auth/sidecar/sidecarHttpClient.d.ts +95 -0
- package/dist/src/auth/sidecar/sidecarHttpClient.js +406 -0
- package/dist/src/auth/sidecar/sidecarHttpClient.js.map +1 -0
- package/dist/src/auth/sidecar/sidecarModels.d.ts +110 -0
- package/dist/src/auth/sidecar/sidecarModels.js +48 -0
- package/dist/src/auth/sidecar/sidecarModels.js.map +1 -0
- package/dist/src/auth/sidecar/sidecarTokenExpiry.d.ts +13 -0
- package/dist/src/auth/sidecar/sidecarTokenExpiry.js +39 -0
- package/dist/src/auth/sidecar/sidecarTokenExpiry.js.map +1 -0
- package/dist/src/cloudAdapter.js +1 -1
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/errorHelper.js +96 -0
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/oauth/userTokenClient.d.ts +12 -0
- package/dist/src/oauth/userTokenClient.js +28 -8
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/package.json +3 -3
- package/src/app/streaming/streamingResponse.ts +24 -1
- package/src/auth/MemoryCache.ts +7 -1
- package/src/auth/authConfiguration.ts +36 -3
- package/src/auth/authConstants.ts +7 -7
- package/src/auth/{msalConnectionManager.ts → connectionManager.ts} +65 -61
- package/src/auth/index.ts +5 -3
- package/src/auth/jwt-middleware.ts +31 -1
- package/src/auth/msal/msalConnectionManager.ts +55 -0
- package/src/auth/msal/msalConnectionSettings.ts +79 -0
- package/src/auth/{msalTokenCredential.ts → msal/msalTokenCredential.ts} +2 -1
- package/src/auth/{msalTokenProvider.ts → msal/msalTokenProvider.ts} +337 -156
- package/src/auth/settings.ts +131 -76
- package/src/auth/sidecar/sidecarAuthProvider.ts +234 -0
- package/src/auth/sidecar/sidecarConnectionSettings.ts +72 -0
- package/src/auth/sidecar/sidecarHttpClient.ts +443 -0
- package/src/auth/sidecar/sidecarModels.ts +153 -0
- package/src/auth/sidecar/sidecarTokenExpiry.ts +35 -0
- package/src/cloudAdapter.ts +1 -1
- package/src/errorHelper.ts +110 -0
- package/src/index.ts +1 -1
- package/src/oauth/userTokenClient.ts +43 -9
- package/dist/src/auth/msalConnectionManager.d.ts +0 -64
- package/dist/src/auth/msalConnectionManager.js.map +0 -1
- package/dist/src/auth/msalTokenCredential.js.map +0 -1
- package/dist/src/auth/msalTokenProvider.js.map +0 -1
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ConfidentialClientApplication, LogLevel, ManagedIdentityApplication, NodeSystemOptions } from '@azure/msal-node'
|
|
7
|
-
import { AuthConfiguration, AuthType, resolveAuthority as resolveAuthorityUtil } from '
|
|
8
|
-
import { AuthProvider } from '
|
|
6
|
+
import { AuthenticationResult, ConfidentialClientApplication, LogLevel, ManagedIdentityApplication, NodeSystemOptions } from '@azure/msal-node'
|
|
7
|
+
import { AuthConfiguration, AuthType, resolveAuthority, resolveAuthority as resolveAuthorityUtil, resolveAuthType } from '../authConfiguration'
|
|
8
|
+
import { AuthProvider } from '../authProvider'
|
|
9
9
|
import { debug, trace } from '@microsoft/agents-telemetry'
|
|
10
10
|
import { randomUUID } from 'crypto'
|
|
11
|
-
import { MemoryCache } from '
|
|
12
|
-
import jwt from 'jsonwebtoken'
|
|
13
|
-
|
|
11
|
+
import { MemoryCache } from '../MemoryCache'
|
|
12
|
+
import jwt, { JwtPayload } from 'jsonwebtoken'
|
|
14
13
|
import fs from 'fs'
|
|
15
14
|
import crypto from 'crypto'
|
|
16
|
-
import { AuthenticationTraceDefinitions } from '
|
|
15
|
+
import { AuthenticationTraceDefinitions } from '../../observability'
|
|
17
16
|
import { ExceptionHelper } from '@microsoft/agents-activity'
|
|
18
|
-
import { Errors } from '
|
|
17
|
+
import { Errors } from '../../errorHelper'
|
|
19
18
|
|
|
20
19
|
const audience = 'api://AzureADTokenExchange'
|
|
21
20
|
const logger = debug('agents:msal')
|
|
@@ -33,14 +32,237 @@ function createTokenRequestTimeoutError (timeoutMs: number): Error {
|
|
|
33
32
|
* Provides tokens using MSAL.
|
|
34
33
|
*/
|
|
35
34
|
export class MsalTokenProvider implements AuthProvider {
|
|
36
|
-
private readonly
|
|
35
|
+
private static readonly _accessTokenCache = new MemoryCache<string>()
|
|
36
|
+
private static readonly _agenticTokenCache = new MemoryCache<string>()
|
|
37
|
+
private static readonly _confidentialClients = new Map<string, ConfidentialClientApplication>()
|
|
38
|
+
private static readonly _maxConfidentialClients = 100
|
|
37
39
|
public readonly connectionSettings?: AuthConfiguration
|
|
38
40
|
|
|
39
41
|
constructor (connectionSettings?: AuthConfiguration) {
|
|
40
|
-
this._agenticTokenCache = new MemoryCache<string>()
|
|
41
42
|
this.connectionSettings = connectionSettings
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Clears process-wide auth caches.
|
|
47
|
+
*/
|
|
48
|
+
public static clearSharedCaches (): void {
|
|
49
|
+
MsalTokenProvider._accessTokenCache.clear()
|
|
50
|
+
MsalTokenProvider._agenticTokenCache.clear()
|
|
51
|
+
MsalTokenProvider._confidentialClients.clear()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private static cacheKey (...parts: Array<string | number | boolean | undefined | null>): string {
|
|
55
|
+
return JSON.stringify(parts.map(part => part ?? ''))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private static digest (value: string | Buffer | undefined): string {
|
|
59
|
+
return value ? crypto.createHash('sha256').update(value).digest('base64url') : ''
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private getOrCreateConfidentialClient (
|
|
63
|
+
cacheKey: string,
|
|
64
|
+
createClient: () => ConfidentialClientApplication
|
|
65
|
+
): ConfidentialClientApplication {
|
|
66
|
+
const existing = MsalTokenProvider._confidentialClients.get(cacheKey)
|
|
67
|
+
if (existing) {
|
|
68
|
+
MsalTokenProvider._confidentialClients.delete(cacheKey)
|
|
69
|
+
MsalTokenProvider._confidentialClients.set(cacheKey, existing)
|
|
70
|
+
return existing
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const created = createClient()
|
|
74
|
+
MsalTokenProvider._confidentialClients.set(cacheKey, created)
|
|
75
|
+
while (MsalTokenProvider._confidentialClients.size > MsalTokenProvider._maxConfidentialClients) {
|
|
76
|
+
const oldestKey = MsalTokenProvider._confidentialClients.keys().next().value
|
|
77
|
+
if (oldestKey === undefined) {
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
MsalTokenProvider._confidentialClients.delete(oldestKey)
|
|
81
|
+
}
|
|
82
|
+
return created
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private getFileCacheIdentity (path?: string): string {
|
|
86
|
+
if (!path) {
|
|
87
|
+
return ''
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const stat = fs.statSync(path)
|
|
92
|
+
return `${path}:${stat.size}:${stat.mtimeMs}`
|
|
93
|
+
} catch {
|
|
94
|
+
return path
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private getAccessTokenCacheKey (authConfig: AuthConfiguration, scope: string): string {
|
|
99
|
+
const authType = resolveAuthType(authConfig)
|
|
100
|
+
let authority = resolveAuthorityUtil(authConfig.authorityEndpoint ?? authConfig.authority, authConfig.tenantId)
|
|
101
|
+
let credentialIdentity = ''
|
|
102
|
+
|
|
103
|
+
switch (authType) {
|
|
104
|
+
case AuthType.ClientSecret:
|
|
105
|
+
credentialIdentity = MsalTokenProvider.digest(authConfig.clientSecret)
|
|
106
|
+
break
|
|
107
|
+
case AuthType.Certificate:
|
|
108
|
+
case AuthType.CertificateSubjectName:
|
|
109
|
+
credentialIdentity = MsalTokenProvider.cacheKey(
|
|
110
|
+
this.getFileCacheIdentity(authConfig.certPemFile),
|
|
111
|
+
this.getFileCacheIdentity(authConfig.certKeyFile),
|
|
112
|
+
authConfig.sendX5C
|
|
113
|
+
)
|
|
114
|
+
break
|
|
115
|
+
case AuthType.WorkloadIdentity:
|
|
116
|
+
authority = `https://login.microsoftonline.com/${authConfig.tenantId}`
|
|
117
|
+
credentialIdentity = this.getFileCacheIdentity(authConfig.federatedTokenFile ?? authConfig.WIDAssertionFile)
|
|
118
|
+
break
|
|
119
|
+
case AuthType.FederatedCredentials:
|
|
120
|
+
credentialIdentity = authConfig.federatedClientId ?? authConfig.FICClientId ?? ''
|
|
121
|
+
break
|
|
122
|
+
case AuthType.UserManagedIdentity:
|
|
123
|
+
authority = 'managed-identity'
|
|
124
|
+
credentialIdentity = authConfig.clientId ?? ''
|
|
125
|
+
break
|
|
126
|
+
case AuthType.SystemManagedIdentity:
|
|
127
|
+
authority = 'managed-identity'
|
|
128
|
+
credentialIdentity = 'system'
|
|
129
|
+
break
|
|
130
|
+
default:
|
|
131
|
+
credentialIdentity = 'unknown'
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return MsalTokenProvider.cacheKey(
|
|
135
|
+
'access-token',
|
|
136
|
+
authType,
|
|
137
|
+
authority,
|
|
138
|
+
authConfig.clientId,
|
|
139
|
+
scope,
|
|
140
|
+
authConfig.azureRegion,
|
|
141
|
+
credentialIdentity
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private cacheAccessToken (cacheKey: string, token: string, expiresOn?: Date | null): void {
|
|
146
|
+
const ttlSeconds = this.getTokenCacheTtlSeconds(token, expiresOn)
|
|
147
|
+
if (ttlSeconds > 0) {
|
|
148
|
+
MsalTokenProvider._accessTokenCache.set(cacheKey, token, ttlSeconds)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private cacheAgenticToken (cacheKey: string, token: string, ttlSeconds: number): void {
|
|
153
|
+
const safeTtlSeconds = Math.floor(ttlSeconds) - 300
|
|
154
|
+
if (safeTtlSeconds > 0) {
|
|
155
|
+
MsalTokenProvider._agenticTokenCache.set(cacheKey, token, safeTtlSeconds)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private cacheAgenticAuthenticationResult (cacheKey: string, token: AuthenticationResult): void {
|
|
160
|
+
const ttlSeconds = this.getTokenCacheTtlSeconds(token.accessToken, token.expiresOn)
|
|
161
|
+
if (ttlSeconds > 0) {
|
|
162
|
+
MsalTokenProvider._agenticTokenCache.set(cacheKey, token.accessToken, ttlSeconds)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private getAgenticTokenCacheKey (
|
|
167
|
+
tenantId: string,
|
|
168
|
+
clientId: string,
|
|
169
|
+
scopes: string[],
|
|
170
|
+
tokenBodyParameters: { [key: string]: any }
|
|
171
|
+
): string {
|
|
172
|
+
const bodyKey = Object.keys(tokenBodyParameters)
|
|
173
|
+
.sort()
|
|
174
|
+
.filter(key => key !== 'user_federated_identity_credential')
|
|
175
|
+
.map(key => `${key}=${MsalTokenProvider.digest(String(tokenBodyParameters[key]))}`)
|
|
176
|
+
.join('&')
|
|
177
|
+
|
|
178
|
+
return MsalTokenProvider.cacheKey(
|
|
179
|
+
'agentic-token',
|
|
180
|
+
this.resolveAuthority(tenantId),
|
|
181
|
+
clientId,
|
|
182
|
+
scopes.join(' '),
|
|
183
|
+
bodyKey
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private getTokenCacheTtlSeconds (token: string, expiresOn?: Date | null): number {
|
|
188
|
+
const expiresAtMs = expiresOn?.getTime() ?? this.getJwtExpiresAtMs(token)
|
|
189
|
+
if (!expiresAtMs) {
|
|
190
|
+
return 0
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return Math.floor((expiresAtMs - Date.now()) / 1000) - 300
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private getJwtExpiresAtMs (token: string): number | undefined {
|
|
197
|
+
const payload = jwt.decode(token) as JwtPayload | string | null
|
|
198
|
+
if (!payload || typeof payload === 'string' || typeof payload.exp !== 'number') {
|
|
199
|
+
return undefined
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return payload.exp * 1000
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private getClientSecretClient (authConfig: AuthConfiguration): ConfidentialClientApplication {
|
|
206
|
+
const cacheKey = MsalTokenProvider.cacheKey(
|
|
207
|
+
'confidential-client',
|
|
208
|
+
AuthType.ClientSecret,
|
|
209
|
+
authConfig.clientId,
|
|
210
|
+
resolveAuthorityUtil(authConfig.authorityEndpoint ?? authConfig.authority, authConfig.tenantId),
|
|
211
|
+
MsalTokenProvider.digest(authConfig.clientSecret)
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return this.getOrCreateConfidentialClient(cacheKey, () => new ConfidentialClientApplication({
|
|
215
|
+
auth: {
|
|
216
|
+
clientId: authConfig.clientId as string,
|
|
217
|
+
authority: resolveAuthorityUtil(authConfig.authorityEndpoint ?? authConfig.authority, authConfig.tenantId),
|
|
218
|
+
clientSecret: authConfig.clientSecret
|
|
219
|
+
},
|
|
220
|
+
system: this.sysOptions
|
|
221
|
+
}))
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private getCertificateClient (authConfig: AuthConfiguration): ConfidentialClientApplication {
|
|
225
|
+
const cacheKey = MsalTokenProvider.cacheKey(
|
|
226
|
+
'confidential-client',
|
|
227
|
+
authConfig.authType ?? AuthType.Certificate,
|
|
228
|
+
authConfig.clientId,
|
|
229
|
+
resolveAuthorityUtil(authConfig.authorityEndpoint ?? authConfig.authority, authConfig.tenantId),
|
|
230
|
+
this.getFileCacheIdentity(authConfig.certPemFile),
|
|
231
|
+
this.getFileCacheIdentity(authConfig.certKeyFile),
|
|
232
|
+
authConfig.sendX5C
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
return this.getOrCreateConfidentialClient(cacheKey, () => {
|
|
236
|
+
const privateKeySource = fs.readFileSync(authConfig.certKeyFile as string)
|
|
237
|
+
|
|
238
|
+
const privateKeyObject = crypto.createPrivateKey({
|
|
239
|
+
key: privateKeySource,
|
|
240
|
+
format: 'pem'
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
const privateKey = privateKeyObject.export({
|
|
244
|
+
format: 'pem',
|
|
245
|
+
type: 'pkcs8'
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const pemFile = fs.readFileSync(authConfig.certPemFile as string)
|
|
249
|
+
const pubKeyObject = new crypto.X509Certificate(pemFile)
|
|
250
|
+
|
|
251
|
+
return new ConfidentialClientApplication({
|
|
252
|
+
auth: {
|
|
253
|
+
clientId: authConfig.clientId || '',
|
|
254
|
+
authority: resolveAuthority(authConfig.authorityEndpoint ?? authConfig.authority, authConfig.tenantId),
|
|
255
|
+
clientCertificate: {
|
|
256
|
+
privateKey: privateKey as string,
|
|
257
|
+
thumbprint: pubKeyObject.fingerprint.replaceAll(':', ''),
|
|
258
|
+
x5c: pemFile.toString()
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
system: this.sysOptions
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
44
266
|
/**
|
|
45
267
|
* Gets an access token using the auth configuration from the MsalTokenProvider instance and the provided scope.
|
|
46
268
|
* @param scope The scope for the token.
|
|
@@ -79,80 +301,64 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
79
301
|
return ''
|
|
80
302
|
}
|
|
81
303
|
|
|
304
|
+
const accessTokenCacheKey = this.getAccessTokenCacheKey(authConfig, actualScope)
|
|
305
|
+
const cachedAccessToken = MsalTokenProvider._accessTokenCache.get(accessTokenCacheKey)
|
|
306
|
+
if (cachedAccessToken) {
|
|
307
|
+
logger.debug('getAccessToken cache hit clientId=%s scope=%s', authConfig.clientId, actualScope)
|
|
308
|
+
return cachedAccessToken
|
|
309
|
+
}
|
|
310
|
+
|
|
82
311
|
let token
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
token = await this.acquireAccessTokenViaWID(authConfig, actualScope)
|
|
93
|
-
break
|
|
312
|
+
const authType = resolveAuthType(authConfig)
|
|
313
|
+
record({ method: authType })
|
|
314
|
+
logger.debug('getAccessToken via method=%s clientId=%s scope=%s', authType, authConfig.clientId, actualScope)
|
|
315
|
+
|
|
316
|
+
switch (authType) {
|
|
317
|
+
case AuthType.WorkloadIdentity: {
|
|
318
|
+
const tokenFilePath = authConfig.federatedTokenFile ?? authConfig.WIDAssertionFile
|
|
319
|
+
if (!tokenFilePath) {
|
|
320
|
+
throw ExceptionHelper.generateException(Error, Errors.WorkloadIdentityTokenFileRequired)
|
|
94
321
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
throw ExceptionHelper.generateException(Error, Errors.FICClientIdRequired)
|
|
98
|
-
}
|
|
99
|
-
token = await this.acquireAccessTokenViaFIC(authConfig, actualScope)
|
|
100
|
-
break
|
|
101
|
-
case AuthType.ClientSecret:
|
|
102
|
-
if (!authConfig.clientSecret) {
|
|
103
|
-
throw ExceptionHelper.generateException(Error, Errors.ClientSecretRequired)
|
|
104
|
-
}
|
|
105
|
-
token = await this.acquireAccessTokenViaSecret(authConfig, actualScope)
|
|
106
|
-
break
|
|
107
|
-
case AuthType.Certificate:
|
|
108
|
-
case AuthType.CertificateSubjectName:
|
|
109
|
-
if (!authConfig.certPemFile || !authConfig.certKeyFile) {
|
|
110
|
-
throw ExceptionHelper.generateException(Error, Errors.CertificateFilesRequired)
|
|
111
|
-
}
|
|
112
|
-
token = await this.acquireTokenWithCertificate(authConfig, actualScope)
|
|
113
|
-
break
|
|
114
|
-
case AuthType.UserManagedIdentity:
|
|
115
|
-
if (!authConfig.clientId) {
|
|
116
|
-
throw ExceptionHelper.generateException(Error, Errors.ClientIdRequiredForUserManagedIdentity)
|
|
117
|
-
}
|
|
118
|
-
token = await this.acquireTokenWithUserAssignedIdentity(authConfig, actualScope)
|
|
119
|
-
break
|
|
120
|
-
case AuthType.SystemManagedIdentity:
|
|
121
|
-
token = await this.acquireTokenWithSystemAssignedIdentity(authConfig, actualScope)
|
|
122
|
-
break
|
|
123
|
-
default:
|
|
124
|
-
throw ExceptionHelper.generateException(Error, Errors.UnsupportedAuthType, undefined, { authType: authConfig.authType })
|
|
322
|
+
token = await this.acquireAccessTokenViaWID(authConfig, actualScope)
|
|
323
|
+
break
|
|
125
324
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
325
|
+
case AuthType.FederatedCredentials:
|
|
326
|
+
if (!authConfig.federatedClientId && !authConfig.FICClientId) {
|
|
327
|
+
throw ExceptionHelper.generateException(Error, Errors.FICClientIdRequired)
|
|
328
|
+
}
|
|
329
|
+
token = await this.acquireAccessTokenViaFIC(authConfig, actualScope)
|
|
330
|
+
break
|
|
331
|
+
case AuthType.ClientSecret:
|
|
332
|
+
if (!authConfig.clientSecret) {
|
|
333
|
+
throw ExceptionHelper.generateException(Error, Errors.ClientSecretRequired)
|
|
334
|
+
}
|
|
335
|
+
token = await this.acquireAccessTokenViaSecret(authConfig, actualScope)
|
|
336
|
+
break
|
|
337
|
+
case AuthType.Certificate:
|
|
338
|
+
case AuthType.CertificateSubjectName:
|
|
339
|
+
if (!authConfig.certPemFile || !authConfig.certKeyFile) {
|
|
340
|
+
throw ExceptionHelper.generateException(Error, Errors.CertificateFilesRequired)
|
|
341
|
+
}
|
|
342
|
+
token = await this.acquireTokenWithCertificate(authConfig, actualScope)
|
|
343
|
+
break
|
|
344
|
+
case AuthType.UserManagedIdentity:
|
|
345
|
+
if (!authConfig.clientId) {
|
|
346
|
+
throw ExceptionHelper.generateException(Error, Errors.ClientIdRequiredForUserManagedIdentity)
|
|
347
|
+
}
|
|
348
|
+
token = await this.acquireTokenWithUserAssignedIdentity(authConfig, actualScope)
|
|
349
|
+
break
|
|
350
|
+
case AuthType.SystemManagedIdentity:
|
|
351
|
+
token = await this.acquireTokenWithSystemAssignedIdentity(authConfig, actualScope)
|
|
352
|
+
break
|
|
353
|
+
default:
|
|
354
|
+
throw ExceptionHelper.generateException(Error, Errors.UnsupportedAuthType, undefined, { authType })
|
|
151
355
|
}
|
|
356
|
+
|
|
152
357
|
if (token === undefined) {
|
|
153
358
|
throw ExceptionHelper.generateException(Error, Errors.FailedToAcquireToken)
|
|
154
359
|
}
|
|
155
360
|
|
|
361
|
+
this.cacheAccessToken(accessTokenCacheKey, token)
|
|
156
362
|
return token
|
|
157
363
|
})
|
|
158
364
|
}
|
|
@@ -187,14 +393,7 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
187
393
|
record({ scopes: actualScopes })
|
|
188
394
|
logger.debug('acquireTokenOnBehalfOf clientId=%s scopes=%o', authConfig.clientId, actualScopes)
|
|
189
395
|
|
|
190
|
-
const cca =
|
|
191
|
-
auth: {
|
|
192
|
-
clientId: authConfig.clientId as string,
|
|
193
|
-
authority: `${authConfig.authorityEndpoint ?? authConfig.authority}/${authConfig.tenantId || 'botframework.com'}`,
|
|
194
|
-
clientSecret: authConfig.clientSecret
|
|
195
|
-
},
|
|
196
|
-
system: this.sysOptions
|
|
197
|
-
})
|
|
396
|
+
const cca = this.getClientSecretClient(authConfig)
|
|
198
397
|
const token = await cca.acquireTokenOnBehalfOf({
|
|
199
398
|
oboAssertion: actualOboAssertion,
|
|
200
399
|
scopes: actualScopes
|
|
@@ -215,6 +414,19 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
215
414
|
if (!this.connectionSettings) {
|
|
216
415
|
throw new Error('Connection settings must be provided when calling getAgenticInstanceToken')
|
|
217
416
|
}
|
|
417
|
+
|
|
418
|
+
const instanceTokenCacheKey = MsalTokenProvider.cacheKey(
|
|
419
|
+
'agentic-instance-token',
|
|
420
|
+
this.resolveAuthority(tenantId),
|
|
421
|
+
agentAppInstanceId,
|
|
422
|
+
this.connectionSettings.clientId,
|
|
423
|
+
this.connectionSettings.azureRegion
|
|
424
|
+
)
|
|
425
|
+
const cachedInstanceToken = MsalTokenProvider._agenticTokenCache.get(instanceTokenCacheKey)
|
|
426
|
+
if (cachedInstanceToken) {
|
|
427
|
+
return cachedInstanceToken
|
|
428
|
+
}
|
|
429
|
+
|
|
218
430
|
const appToken = await this.getAgenticApplicationToken(tenantId, agentAppInstanceId)
|
|
219
431
|
const cca = new ConfidentialClientApplication({
|
|
220
432
|
auth: {
|
|
@@ -235,6 +447,7 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
235
447
|
throw new Error(`Failed to acquire instance token for agent instance: ${agentAppInstanceId}`)
|
|
236
448
|
}
|
|
237
449
|
|
|
450
|
+
this.cacheAgenticAuthenticationResult(instanceTokenCacheKey, token)
|
|
238
451
|
return token.accessToken
|
|
239
452
|
})
|
|
240
453
|
}
|
|
@@ -286,9 +499,9 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
286
499
|
|
|
287
500
|
logger.debug('acquireTokenForAgenticScenarios clientId=%s tenantId=%s scopes=%o grant_type=%s', clientId, tenantId, scopes, tokenBodyParameters.grant_type)
|
|
288
501
|
// Check cache first
|
|
289
|
-
const cacheKey =
|
|
290
|
-
if (
|
|
291
|
-
return
|
|
502
|
+
const cacheKey = this.getAgenticTokenCacheKey(tenantId, clientId, scopes, tokenBodyParameters)
|
|
503
|
+
if (MsalTokenProvider._agenticTokenCache.get(cacheKey)) {
|
|
504
|
+
return MsalTokenProvider._agenticTokenCache.get(cacheKey) as string
|
|
292
505
|
}
|
|
293
506
|
|
|
294
507
|
const url = `${this.resolveAuthority(tenantId)}/oauth2/v2.0/token`
|
|
@@ -344,7 +557,7 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
344
557
|
})
|
|
345
558
|
|
|
346
559
|
// capture token, expire local cache 5 minutes early
|
|
347
|
-
this.
|
|
560
|
+
this.cacheAgenticToken(cacheKey, token.access_token, token.expires_in)
|
|
348
561
|
return token.access_token
|
|
349
562
|
}
|
|
350
563
|
|
|
@@ -353,13 +566,22 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
353
566
|
logger.debug('getAgenticUserToken tenantId=%s agentAppInstanceId=%s scopes=%o', tenantId, agentAppInstanceId, scopes)
|
|
354
567
|
record({ agenticInstanceId: agentAppInstanceId, agenticUserId, scopes })
|
|
355
568
|
|
|
569
|
+
const userTokenParameters = {
|
|
570
|
+
user_id: agenticUserId,
|
|
571
|
+
grant_type: 'user_fic',
|
|
572
|
+
}
|
|
573
|
+
const userTokenCacheKey = this.getAgenticTokenCacheKey(tenantId, agentAppInstanceId, scopes, userTokenParameters)
|
|
574
|
+
const cachedUserToken = MsalTokenProvider._agenticTokenCache.get(userTokenCacheKey)
|
|
575
|
+
if (cachedUserToken) {
|
|
576
|
+
return cachedUserToken
|
|
577
|
+
}
|
|
578
|
+
|
|
356
579
|
const agentToken = await this.getAgenticApplicationToken(tenantId, agentAppInstanceId)
|
|
357
580
|
const instanceToken = await this.getAgenticInstanceToken(tenantId, agentAppInstanceId)
|
|
358
581
|
|
|
359
582
|
const token = await this.acquireTokenForAgenticScenarios(tenantId, agentAppInstanceId, agentToken, scopes, {
|
|
360
|
-
|
|
583
|
+
...userTokenParameters,
|
|
361
584
|
user_federated_identity_credential: instanceToken,
|
|
362
|
-
grant_type: 'user_fic',
|
|
363
585
|
})
|
|
364
586
|
|
|
365
587
|
if (!token) {
|
|
@@ -376,7 +598,9 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
376
598
|
}
|
|
377
599
|
logger.debug('getAgenticApplicationToken clientId=%s tenantId=%s agentAppInstanceId=%s', this.connectionSettings.clientId, tenantId, agentAppInstanceId)
|
|
378
600
|
|
|
379
|
-
|
|
601
|
+
const authType = resolveAuthType(this.connectionSettings)
|
|
602
|
+
|
|
603
|
+
if (authType === AuthType.IdentityProxyManager) {
|
|
380
604
|
let resource: string
|
|
381
605
|
if (!this.connectionSettings.idpmResource) {
|
|
382
606
|
resource = 'api://AzureAdTokenExchange/.default'
|
|
@@ -401,38 +625,28 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
401
625
|
|
|
402
626
|
let clientAssertion
|
|
403
627
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
throw ExceptionHelper.generateException(Error, Errors.WorkloadIdentityTokenFileRequired)
|
|
410
|
-
}
|
|
411
|
-
clientAssertion = fs.readFileSync(tokenFilePath as string, 'utf8')
|
|
412
|
-
break
|
|
628
|
+
switch (authType) {
|
|
629
|
+
case AuthType.WorkloadIdentity: {
|
|
630
|
+
const tokenFilePath = this.connectionSettings.federatedTokenFile ?? this.connectionSettings.WIDAssertionFile
|
|
631
|
+
if (tokenFilePath === undefined) {
|
|
632
|
+
throw ExceptionHelper.generateException(Error, Errors.WorkloadIdentityTokenFileRequired)
|
|
413
633
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
throw ExceptionHelper.generateException(Error, Errors.FICClientIdRequired)
|
|
417
|
-
}
|
|
418
|
-
clientAssertion = await this.fetchExternalToken(this.connectionSettings.federatedClientId as string || this.connectionSettings.FICClientId as string)
|
|
419
|
-
break
|
|
420
|
-
case AuthType.Certificate:
|
|
421
|
-
case AuthType.CertificateSubjectName:
|
|
422
|
-
if (!this.connectionSettings.certPemFile || !this.connectionSettings.certKeyFile) {
|
|
423
|
-
throw ExceptionHelper.generateException(Error, Errors.CertificateFilesRequired)
|
|
424
|
-
}
|
|
425
|
-
clientAssertion = this.getAssertionFromCert(this.connectionSettings)
|
|
426
|
-
break
|
|
634
|
+
clientAssertion = fs.readFileSync(tokenFilePath as string, 'utf8')
|
|
635
|
+
break
|
|
427
636
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
637
|
+
case AuthType.FederatedCredentials:
|
|
638
|
+
if (!this.connectionSettings.federatedClientId && !this.connectionSettings.FICClientId) {
|
|
639
|
+
throw ExceptionHelper.generateException(Error, Errors.FICClientIdRequired)
|
|
640
|
+
}
|
|
641
|
+
clientAssertion = await this.fetchExternalToken(this.connectionSettings.federatedClientId as string || this.connectionSettings.FICClientId as string)
|
|
642
|
+
break
|
|
643
|
+
case AuthType.Certificate:
|
|
644
|
+
case AuthType.CertificateSubjectName:
|
|
645
|
+
if (!this.connectionSettings.certPemFile || !this.connectionSettings.certKeyFile) {
|
|
646
|
+
throw ExceptionHelper.generateException(Error, Errors.CertificateFilesRequired)
|
|
647
|
+
}
|
|
648
|
+
clientAssertion = this.getAssertionFromCert(this.connectionSettings)
|
|
649
|
+
break
|
|
436
650
|
}
|
|
437
651
|
|
|
438
652
|
const token = await this.acquireTokenForAgenticScenarios(tenantId, this.connectionSettings.clientId, clientAssertion, ['api://AzureAdTokenExchange/.default'], {
|
|
@@ -559,33 +773,7 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
559
773
|
* @returns A promise that resolves to the access token.
|
|
560
774
|
*/
|
|
561
775
|
private async acquireTokenWithCertificate (authConfig: AuthConfiguration, scope: string) {
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
const privateKeyObject = crypto.createPrivateKey({
|
|
565
|
-
key: privateKeySource,
|
|
566
|
-
format: 'pem'
|
|
567
|
-
})
|
|
568
|
-
|
|
569
|
-
const privateKey = privateKeyObject.export({
|
|
570
|
-
format: 'pem',
|
|
571
|
-
type: 'pkcs8'
|
|
572
|
-
})
|
|
573
|
-
|
|
574
|
-
const pemFile = fs.readFileSync(authConfig.certPemFile as string)
|
|
575
|
-
const pubKeyObject = new crypto.X509Certificate(pemFile)
|
|
576
|
-
|
|
577
|
-
const cca = new ConfidentialClientApplication({
|
|
578
|
-
auth: {
|
|
579
|
-
clientId: authConfig.clientId || '',
|
|
580
|
-
authority: `${authConfig.authorityEndpoint ?? authConfig.authority}/${authConfig.tenantId || 'botframework.com'}`,
|
|
581
|
-
clientCertificate: {
|
|
582
|
-
privateKey: privateKey as string,
|
|
583
|
-
thumbprint: pubKeyObject.fingerprint.replaceAll(':', ''),
|
|
584
|
-
x5c: pemFile.toString()
|
|
585
|
-
}
|
|
586
|
-
},
|
|
587
|
-
system: this.sysOptions
|
|
588
|
-
})
|
|
776
|
+
const cca = this.getCertificateClient(authConfig)
|
|
589
777
|
const token = await cca.acquireTokenByClientCredential({
|
|
590
778
|
scopes: [`${scope}/.default`],
|
|
591
779
|
correlationId: randomUUID(),
|
|
@@ -604,14 +792,7 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
604
792
|
* @returns A promise that resolves to the access token.
|
|
605
793
|
*/
|
|
606
794
|
private async acquireAccessTokenViaSecret (authConfig: AuthConfiguration, scope: string) {
|
|
607
|
-
const cca =
|
|
608
|
-
auth: {
|
|
609
|
-
clientId: authConfig.clientId as string,
|
|
610
|
-
authority: `${authConfig.authorityEndpoint ?? authConfig.authority}/${authConfig.tenantId || 'botframework.com'}`,
|
|
611
|
-
clientSecret: authConfig.clientSecret
|
|
612
|
-
},
|
|
613
|
-
system: this.sysOptions
|
|
614
|
-
})
|
|
795
|
+
const cca = this.getClientSecretClient(authConfig)
|
|
615
796
|
const token = await cca.acquireTokenByClientCredential({
|
|
616
797
|
scopes: [`${scope}/.default`],
|
|
617
798
|
correlationId: randomUUID(),
|