@microsoft/agents-hosting 1.6.0-beta.9.gce9d8facd2 → 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 +4 -9
- package/dist/src/agent-client/agentClient.js +2 -2
- package/dist/src/agent-client/agentClient.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.js +2 -2
- package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
- package/dist/src/app/agentApplication.js +28 -2
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +10 -0
- package/dist/src/app/attachmentDownloader.js +5 -10
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/auth/authorizationManager.d.ts +4 -0
- package/dist/src/app/auth/authorizationManager.js +94 -67
- package/dist/src/app/auth/authorizationManager.js.map +1 -1
- package/dist/src/app/auth/handlers/azureBotAuthorization.js +2 -2
- package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -1
- package/dist/src/app/proactive/createConversationOptionsBuilder.js +10 -6
- package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -1
- package/dist/src/app/proactive/proactive.d.ts +1 -0
- package/dist/src/app/proactive/proactive.js +30 -15
- package/dist/src/app/proactive/proactive.js.map +1 -1
- 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/app/teamsAttachmentDownloader.js +6 -14
- package/dist/src/app/teamsAttachmentDownloader.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 +2 -143
- package/dist/src/auth/authConfiguration.js +336 -257
- 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} +48 -33
- 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 +34 -2
- 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} +30 -3
- package/dist/src/auth/msal/msalTokenProvider.js +733 -0
- package/dist/src/auth/msal/msalTokenProvider.js.map +1 -0
- package/dist/src/auth/settings.d.ts +336 -0
- package/dist/src/auth/settings.js +202 -0
- package/dist/src/auth/settings.js.map +1 -0
- 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.d.ts +58 -1
- package/dist/src/cloudAdapter.js +233 -53
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/connector-client/connectorClient.d.ts +12 -8
- package/dist/src/connector-client/connectorClient.js +99 -82
- package/dist/src/connector-client/connectorClient.js.map +1 -1
- package/dist/src/errorHelper.js +181 -7
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/getProductInfo.d.ts +23 -0
- package/dist/src/getProductInfo.js +71 -1
- package/dist/src/getProductInfo.js.map +1 -1
- package/dist/src/headerPropagation.d.ts +17 -2
- package/dist/src/headerPropagation.js +46 -24
- package/dist/src/headerPropagation.js.map +1 -1
- package/dist/src/httpClient.d.ts +60 -0
- package/dist/src/httpClient.js +173 -0
- package/dist/src/httpClient.js.map +1 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +8 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/oauth/userTokenClient.d.ts +17 -4
- package/dist/src/oauth/userTokenClient.js +119 -80
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/observability/traces.d.ts +11 -4
- package/dist/src/observability/traces.js +39 -7
- package/dist/src/observability/traces.js.map +1 -1
- package/dist/src/utils/env.d.ts +50 -0
- package/dist/src/utils/env.js +113 -0
- package/dist/src/utils/env.js.map +1 -0
- package/dist/src/utils.d.ts +10 -0
- package/dist/src/utils.js +18 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +4 -9
- package/src/agent-client/agentClient.ts +2 -2
- package/src/agent-client/agentResponseHandler.ts +2 -2
- package/src/app/agentApplication.ts +30 -3
- package/src/app/agentApplicationOptions.ts +11 -0
- package/src/app/attachmentDownloader.ts +6 -8
- package/src/app/auth/authorizationManager.ts +84 -41
- package/src/app/auth/handlers/azureBotAuthorization.ts +2 -2
- package/src/app/proactive/createConversationOptionsBuilder.ts +8 -4
- package/src/app/proactive/proactive.ts +39 -23
- package/src/app/streaming/streamingResponse.ts +24 -1
- package/src/app/teamsAttachmentDownloader.ts +7 -11
- package/src/auth/MemoryCache.ts +7 -1
- package/src/auth/authConfiguration.ts +350 -356
- package/src/auth/authConstants.ts +7 -7
- package/src/auth/{msalConnectionManager.ts → connectionManager.ts} +65 -44
- package/src/auth/index.ts +5 -3
- package/src/auth/jwt-middleware.ts +32 -2
- 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/msal/msalTokenProvider.ts +881 -0
- package/src/auth/settings.ts +378 -0
- 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 +292 -13
- package/src/connector-client/connectorClient.ts +116 -94
- package/src/errorHelper.ts +206 -7
- package/src/getProductInfo.ts +79 -1
- package/src/headerPropagation.ts +55 -23
- package/src/httpClient.ts +223 -0
- package/src/index.ts +6 -2
- package/src/oauth/userTokenClient.ts +137 -84
- package/src/observability/traces.ts +39 -8
- package/src/utils/env.ts +105 -0
- package/src/utils.ts +14 -0
- 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 +0 -485
- package/dist/src/auth/msalTokenProvider.js.map +0 -1
- package/src/auth/msalTokenProvider.ts +0 -562
|
@@ -1,562 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ConfidentialClientApplication, LogLevel, ManagedIdentityApplication, NodeSystemOptions } from '@azure/msal-node'
|
|
7
|
-
import axios from 'axios'
|
|
8
|
-
import { AuthConfiguration, resolveAuthority as resolveAuthorityUtil } from './authConfiguration'
|
|
9
|
-
import { AuthProvider } from './authProvider'
|
|
10
|
-
import { debug, trace } from '@microsoft/agents-telemetry'
|
|
11
|
-
import { v4 } from 'uuid'
|
|
12
|
-
import { MemoryCache } from './MemoryCache'
|
|
13
|
-
import jwt from 'jsonwebtoken'
|
|
14
|
-
|
|
15
|
-
import fs from 'fs'
|
|
16
|
-
import crypto from 'crypto'
|
|
17
|
-
import { AuthenticationTraceDefinitions } from '../observability'
|
|
18
|
-
|
|
19
|
-
const audience = 'api://AzureADTokenExchange'
|
|
20
|
-
const logger = debug('agents:msal')
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Provides tokens using MSAL.
|
|
24
|
-
*/
|
|
25
|
-
export class MsalTokenProvider implements AuthProvider {
|
|
26
|
-
private readonly _agenticTokenCache: MemoryCache<string>
|
|
27
|
-
public readonly connectionSettings?: AuthConfiguration
|
|
28
|
-
|
|
29
|
-
constructor (connectionSettings?: AuthConfiguration) {
|
|
30
|
-
this._agenticTokenCache = new MemoryCache<string>()
|
|
31
|
-
this.connectionSettings = connectionSettings
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Gets an access token using the auth configuration from the MsalTokenProvider instance and the provided scope.
|
|
36
|
-
* @param scope The scope for the token.
|
|
37
|
-
* @returns A promise that resolves to the access token.
|
|
38
|
-
*/
|
|
39
|
-
public async getAccessToken (scope: string): Promise<string>
|
|
40
|
-
/**
|
|
41
|
-
* Gets an access token.
|
|
42
|
-
* @param authConfig The authentication configuration.
|
|
43
|
-
* @param scope The scope for the token.
|
|
44
|
-
* @returns A promise that resolves to the access token.
|
|
45
|
-
*/
|
|
46
|
-
public async getAccessToken (authConfig: AuthConfiguration, scope: string): Promise<string>
|
|
47
|
-
public async getAccessToken (authConfigOrScope: AuthConfiguration | string, scope?: string): Promise<string> {
|
|
48
|
-
return trace(AuthenticationTraceDefinitions.getAccessToken, async ({ record }) => {
|
|
49
|
-
let authConfig: AuthConfiguration
|
|
50
|
-
let actualScope: string
|
|
51
|
-
|
|
52
|
-
if (typeof authConfigOrScope === 'string') {
|
|
53
|
-
// Called as getAccessToken(scope)
|
|
54
|
-
if (!this.connectionSettings) {
|
|
55
|
-
throw new Error('Connection settings must be provided to constructor when calling getAccessToken(scope)')
|
|
56
|
-
}
|
|
57
|
-
authConfig = this.connectionSettings
|
|
58
|
-
actualScope = authConfigOrScope
|
|
59
|
-
} else {
|
|
60
|
-
// Called as getAccessToken(authConfig, scope)
|
|
61
|
-
authConfig = authConfigOrScope
|
|
62
|
-
actualScope = scope as string
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
record({ scope: actualScope })
|
|
66
|
-
|
|
67
|
-
if (!authConfig.clientId && process.env.NODE_ENV !== 'production') {
|
|
68
|
-
record({ method: 'unknown' })
|
|
69
|
-
return ''
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let token
|
|
73
|
-
if (authConfig.WIDAssertionFile !== undefined) {
|
|
74
|
-
record({ method: 'wid' })
|
|
75
|
-
logger.debug('getAccessToken via WID clientId=%s scope=%s', authConfig.clientId, actualScope)
|
|
76
|
-
token = await this.acquireAccessTokenViaWID(authConfig, actualScope)
|
|
77
|
-
} else if (authConfig.FICClientId !== undefined) {
|
|
78
|
-
record({ method: 'fic' })
|
|
79
|
-
logger.debug('getAccessToken via FIC clientId=%s scope=%s', authConfig.clientId, actualScope)
|
|
80
|
-
token = await this.acquireAccessTokenViaFIC(authConfig, actualScope)
|
|
81
|
-
} else if (authConfig.clientSecret !== undefined) {
|
|
82
|
-
record({ method: 'secret' })
|
|
83
|
-
logger.debug('getAccessToken via secret clientId=%s scope=%s', authConfig.clientId, actualScope)
|
|
84
|
-
token = await this.acquireAccessTokenViaSecret(authConfig, actualScope)
|
|
85
|
-
} else if (authConfig.certPemFile !== undefined &&
|
|
86
|
-
authConfig.certKeyFile !== undefined) {
|
|
87
|
-
record({ method: 'certificate' })
|
|
88
|
-
logger.debug('getAccessToken via certificate clientId=%s scope=%s', authConfig.clientId, actualScope)
|
|
89
|
-
token = await this.acquireTokenWithCertificate(authConfig, actualScope)
|
|
90
|
-
} else if (authConfig.clientSecret === undefined &&
|
|
91
|
-
authConfig.certPemFile === undefined &&
|
|
92
|
-
authConfig.certKeyFile === undefined) {
|
|
93
|
-
record({ method: 'managed_identity' })
|
|
94
|
-
logger.debug('getAccessToken via managed identity clientId=%s scope=%s', authConfig.clientId, actualScope)
|
|
95
|
-
token = await this.acquireTokenWithUserAssignedIdentity(authConfig, actualScope)
|
|
96
|
-
} else {
|
|
97
|
-
throw new Error('Invalid authConfig. ')
|
|
98
|
-
}
|
|
99
|
-
if (token === undefined) {
|
|
100
|
-
throw new Error('Failed to acquire token')
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return token
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
public async acquireTokenOnBehalfOf (scopes: string[], oboAssertion: string): Promise<string>
|
|
108
|
-
public async acquireTokenOnBehalfOf (authConfig: AuthConfiguration, scopes: string[], oboAssertion: string): Promise<string>
|
|
109
|
-
public async acquireTokenOnBehalfOf (
|
|
110
|
-
authConfigOrScopes: AuthConfiguration | string[],
|
|
111
|
-
scopesOrOboAssertion?: string[] | string,
|
|
112
|
-
oboAssertion?: string
|
|
113
|
-
): Promise<string> {
|
|
114
|
-
return trace(AuthenticationTraceDefinitions.acquireTokenOnBehalfOf, async ({ record }) => {
|
|
115
|
-
let authConfig: AuthConfiguration
|
|
116
|
-
let actualScopes: string[]
|
|
117
|
-
let actualOboAssertion: string
|
|
118
|
-
|
|
119
|
-
if (Array.isArray(authConfigOrScopes)) {
|
|
120
|
-
// Called as acquireTokenOnBehalfOf(scopes, oboAssertion)
|
|
121
|
-
if (!this.connectionSettings) {
|
|
122
|
-
throw new Error('Connection settings must be provided to constructor when calling acquireTokenOnBehalfOf(scopes, oboAssertion)')
|
|
123
|
-
}
|
|
124
|
-
authConfig = this.connectionSettings
|
|
125
|
-
actualScopes = authConfigOrScopes
|
|
126
|
-
actualOboAssertion = scopesOrOboAssertion as string
|
|
127
|
-
} else {
|
|
128
|
-
// Called as acquireTokenOnBehalfOf(authConfig, scopes, oboAssertion)
|
|
129
|
-
authConfig = authConfigOrScopes
|
|
130
|
-
actualScopes = scopesOrOboAssertion as string[]
|
|
131
|
-
actualOboAssertion = oboAssertion!
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
record({ scopes: actualScopes })
|
|
135
|
-
logger.debug('acquireTokenOnBehalfOf clientId=%s scopes=%o', authConfig.clientId, actualScopes)
|
|
136
|
-
|
|
137
|
-
const cca = new ConfidentialClientApplication({
|
|
138
|
-
auth: {
|
|
139
|
-
clientId: authConfig.clientId as string,
|
|
140
|
-
authority: `${authConfig.authority}/${authConfig.tenantId || 'botframework.com'}`,
|
|
141
|
-
clientSecret: authConfig.clientSecret
|
|
142
|
-
},
|
|
143
|
-
system: this.sysOptions
|
|
144
|
-
})
|
|
145
|
-
const token = await cca.acquireTokenOnBehalfOf({
|
|
146
|
-
oboAssertion: actualOboAssertion,
|
|
147
|
-
scopes: actualScopes
|
|
148
|
-
})
|
|
149
|
-
if (!token?.accessToken) {
|
|
150
|
-
throw new Error('Failed to acquire token on behalf of user')
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return token.accessToken
|
|
154
|
-
})
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
public async getAgenticInstanceToken (tenantId: string, agentAppInstanceId: string): Promise<string> {
|
|
158
|
-
return trace(AuthenticationTraceDefinitions.getAgenticInstanceToken, async ({ record }) => {
|
|
159
|
-
logger.debug('getAgenticInstanceToken tenantId=%s agentAppInstanceId=%s', tenantId, agentAppInstanceId)
|
|
160
|
-
record({ agenticInstanceId: agentAppInstanceId })
|
|
161
|
-
|
|
162
|
-
if (!this.connectionSettings) {
|
|
163
|
-
throw new Error('Connection settings must be provided when calling getAgenticInstanceToken')
|
|
164
|
-
}
|
|
165
|
-
const appToken = await this.getAgenticApplicationToken(tenantId, agentAppInstanceId)
|
|
166
|
-
const cca = new ConfidentialClientApplication({
|
|
167
|
-
auth: {
|
|
168
|
-
clientId: agentAppInstanceId,
|
|
169
|
-
clientAssertion: appToken,
|
|
170
|
-
authority: this.resolveAuthority(tenantId),
|
|
171
|
-
},
|
|
172
|
-
system: this.sysOptions
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
const token = await cca.acquireTokenByClientCredential({
|
|
176
|
-
scopes: ['api://AzureAdTokenExchange/.default'],
|
|
177
|
-
correlationId: v4(),
|
|
178
|
-
azureRegion: this.connectionSettings?.azureRegion
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
if (!token?.accessToken) {
|
|
182
|
-
throw new Error(`Failed to acquire instance token for agent instance: ${agentAppInstanceId}`)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return token.accessToken
|
|
186
|
-
})
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* This method can optionally accept a tenant ID that overrides the tenant ID in the connection settings.
|
|
191
|
-
* The passed tenantId is always preferred over the configured tenantId when present.
|
|
192
|
-
* @param tenantId
|
|
193
|
-
* @returns
|
|
194
|
-
*/
|
|
195
|
-
private resolveAuthority (tenantId?: string) : string {
|
|
196
|
-
const { authority: configuredAuth, tenantId: configuredTenantId } = this.connectionSettings ?? {}
|
|
197
|
-
|
|
198
|
-
if (!tenantId) {
|
|
199
|
-
// No agentic tenant override — delegate to shared utility
|
|
200
|
-
return resolveAuthorityUtil(configuredAuth, configuredTenantId)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Agentic override: build a clean base using the override tenant, then replace any
|
|
204
|
-
// /common or GUID placeholder left in the authority (e.g. from a multi-tenant config)
|
|
205
|
-
const base = resolveAuthorityUtil(configuredAuth, tenantId)
|
|
206
|
-
const guidPattern = /\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/
|
|
207
|
-
|
|
208
|
-
if (base.endsWith('/common') || guidPattern.test(base)) {
|
|
209
|
-
return base.replace(
|
|
210
|
-
/\/(?:common|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(?=\/|$)/,
|
|
211
|
-
`/${tenantId}`
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return base
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Does a direct HTTP call to acquire a token for agentic scenarios - do not use this directly!
|
|
220
|
-
* This method will be removed once MSAL is updated with the necessary features.
|
|
221
|
-
* (This is required in order to pass additional parameters into the auth call)
|
|
222
|
-
* @param tenantId
|
|
223
|
-
* @param clientId
|
|
224
|
-
* @param clientAssertion
|
|
225
|
-
* @param scopes
|
|
226
|
-
* @param tokenBodyParameters
|
|
227
|
-
* @returns
|
|
228
|
-
*/
|
|
229
|
-
private async acquireTokenForAgenticScenarios (tenantId: string, clientId: string, clientAssertion: string | undefined, scopes: string[], tokenBodyParameters: { [key: string]: any }): Promise<string | null> {
|
|
230
|
-
if (!this.connectionSettings) {
|
|
231
|
-
throw new Error('Connection settings must be provided when calling getAgenticInstanceToken')
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
logger.debug('acquireTokenForAgenticScenarios clientId=%s tenantId=%s scopes=%o grant_type=%s', clientId, tenantId, scopes, tokenBodyParameters.grant_type)
|
|
235
|
-
// Check cache first
|
|
236
|
-
const cacheKey = `${clientId}/${Object.keys(tokenBodyParameters).map(key => key !== 'user_federated_identity_credential' ? `${key}=${tokenBodyParameters[key]}` : '').join('&')}/${scopes.join(';')}`
|
|
237
|
-
if (this._agenticTokenCache.get(cacheKey)) {
|
|
238
|
-
return this._agenticTokenCache.get(cacheKey) as string
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const url = `${this.resolveAuthority(tenantId)}/oauth2/v2.0/token`
|
|
242
|
-
|
|
243
|
-
const data: { [key: string]: any } = {
|
|
244
|
-
client_id: clientId,
|
|
245
|
-
scope: scopes.join(' '),
|
|
246
|
-
...tokenBodyParameters
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (clientAssertion) {
|
|
250
|
-
data.client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
251
|
-
data.client_assertion = clientAssertion
|
|
252
|
-
} else {
|
|
253
|
-
data.client_secret = this.connectionSettings.clientSecret
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (data.grant_type !== 'user_fic') {
|
|
257
|
-
data.client_info = '2'
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const token = await axios.post(
|
|
261
|
-
url,
|
|
262
|
-
data,
|
|
263
|
-
{
|
|
264
|
-
headers: {
|
|
265
|
-
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
).catch((error) => {
|
|
269
|
-
logger.error('Error acquiring token: ', error.toJSON())
|
|
270
|
-
throw error
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
// capture token, expire local cache 5 minutes early
|
|
274
|
-
this._agenticTokenCache.set(cacheKey, token.data.access_token, token.data.expires_in - 300)
|
|
275
|
-
return token.data.access_token
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
public async getAgenticUserToken (tenantId: string, agentAppInstanceId: string, agenticUserId: string, scopes: string[]): Promise<string> {
|
|
279
|
-
return trace(AuthenticationTraceDefinitions.getAgenticUserToken, async ({ record }) => {
|
|
280
|
-
logger.debug('getAgenticUserToken tenantId=%s agentAppInstanceId=%s scopes=%o', tenantId, agentAppInstanceId, scopes)
|
|
281
|
-
record({ agenticInstanceId: agentAppInstanceId, agenticUserId, scopes })
|
|
282
|
-
|
|
283
|
-
const agentToken = await this.getAgenticApplicationToken(tenantId, agentAppInstanceId)
|
|
284
|
-
const instanceToken = await this.getAgenticInstanceToken(tenantId, agentAppInstanceId)
|
|
285
|
-
|
|
286
|
-
const token = await this.acquireTokenForAgenticScenarios(tenantId, agentAppInstanceId, agentToken, scopes, {
|
|
287
|
-
user_id: agenticUserId,
|
|
288
|
-
user_federated_identity_credential: instanceToken,
|
|
289
|
-
grant_type: 'user_fic',
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
if (!token) {
|
|
293
|
-
throw new Error(`Failed to acquire instance token for user token: ${agentAppInstanceId}`)
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return token
|
|
297
|
-
})
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
public async getAgenticApplicationToken (tenantId: string, agentAppInstanceId: string): Promise<string> {
|
|
301
|
-
if (!this.connectionSettings?.clientId) {
|
|
302
|
-
throw new Error('Connection settings must be provided when calling getAgenticApplicationToken')
|
|
303
|
-
}
|
|
304
|
-
logger.debug('getAgenticApplicationToken clientId=%s tenantId=%s agentAppInstanceId=%s', this.connectionSettings.clientId, tenantId, agentAppInstanceId)
|
|
305
|
-
|
|
306
|
-
let clientAssertion
|
|
307
|
-
|
|
308
|
-
if (this.connectionSettings.WIDAssertionFile !== undefined) {
|
|
309
|
-
clientAssertion = fs.readFileSync(this.connectionSettings.WIDAssertionFile as string, 'utf8')
|
|
310
|
-
} else if (this.connectionSettings.FICClientId !== undefined) {
|
|
311
|
-
clientAssertion = await this.fetchExternalToken(this.connectionSettings.FICClientId as string)
|
|
312
|
-
} else if (this.connectionSettings.certPemFile !== undefined &&
|
|
313
|
-
this.connectionSettings.certKeyFile !== undefined) {
|
|
314
|
-
clientAssertion = this.getAssertionFromCert(this.connectionSettings)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const token = await this.acquireTokenForAgenticScenarios(tenantId, this.connectionSettings.clientId, clientAssertion, ['api://AzureAdTokenExchange/.default'], {
|
|
318
|
-
grant_type: 'client_credentials',
|
|
319
|
-
fmi_path: agentAppInstanceId,
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
if (!token) {
|
|
323
|
-
throw new Error(`Failed to acquire token for agent instance: ${agentAppInstanceId}`)
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return token
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private readonly sysOptions: NodeSystemOptions = {
|
|
330
|
-
loggerOptions: {
|
|
331
|
-
logLevel: LogLevel.Trace,
|
|
332
|
-
loggerCallback: (level, message, containsPii) => {
|
|
333
|
-
if (containsPii) {
|
|
334
|
-
return
|
|
335
|
-
}
|
|
336
|
-
switch (level) {
|
|
337
|
-
case LogLevel.Error:
|
|
338
|
-
logger.error(message)
|
|
339
|
-
return
|
|
340
|
-
case LogLevel.Info:
|
|
341
|
-
logger.debug(message)
|
|
342
|
-
return
|
|
343
|
-
case LogLevel.Warning:
|
|
344
|
-
if (!message.includes('Warning - No client info in response')) {
|
|
345
|
-
logger.warn(message)
|
|
346
|
-
}
|
|
347
|
-
return
|
|
348
|
-
case LogLevel.Verbose:
|
|
349
|
-
logger.debug(message)
|
|
350
|
-
}
|
|
351
|
-
},
|
|
352
|
-
piiLoggingEnabled: false
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Generates the client assertion using the provided certificate.
|
|
358
|
-
* @param authConfig The authentication configuration.
|
|
359
|
-
* @returns The client assertion.
|
|
360
|
-
*/
|
|
361
|
-
private getAssertionFromCert (authConfig: AuthConfiguration): string {
|
|
362
|
-
const base64url = (buf: Buffer) =>
|
|
363
|
-
buf.toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
|
|
364
|
-
|
|
365
|
-
const privateKeyPem = fs.readFileSync(authConfig.certKeyFile as string)
|
|
366
|
-
|
|
367
|
-
const pemFile = fs.readFileSync(authConfig.certPemFile as string)
|
|
368
|
-
const pubKeyObject = new crypto.X509Certificate(pemFile)
|
|
369
|
-
|
|
370
|
-
const der = pubKeyObject.raw
|
|
371
|
-
const x5tS256 = base64url(crypto.createHash('sha256').update(der).digest())
|
|
372
|
-
|
|
373
|
-
let x5c
|
|
374
|
-
if (authConfig.sendX5C) {
|
|
375
|
-
x5c = pemFile.toString()
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const now = Math.floor(Date.now() / 1000)
|
|
379
|
-
const payload = {
|
|
380
|
-
aud: `${this.resolveAuthority(authConfig.tenantId)}/oauth2/v2.0/token`,
|
|
381
|
-
iss: authConfig.clientId,
|
|
382
|
-
sub: authConfig.clientId,
|
|
383
|
-
jti: v4(),
|
|
384
|
-
nbf: now,
|
|
385
|
-
iat: now,
|
|
386
|
-
exp: now + 600, // 10 minutes
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return jwt.sign(
|
|
390
|
-
payload,
|
|
391
|
-
privateKeyPem,
|
|
392
|
-
{
|
|
393
|
-
algorithm: 'PS256',
|
|
394
|
-
header: { alg: 'PS256', typ: 'JWT', 'x5t#S256': x5tS256, x5c }
|
|
395
|
-
}
|
|
396
|
-
)
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Acquires a token using a user-assigned identity.
|
|
401
|
-
* @param authConfig The authentication configuration.
|
|
402
|
-
* @param scope The scope for the token.
|
|
403
|
-
* @returns A promise that resolves to the access token.
|
|
404
|
-
*/
|
|
405
|
-
private async acquireTokenWithUserAssignedIdentity (authConfig: AuthConfiguration, scope: string) {
|
|
406
|
-
const mia = new ManagedIdentityApplication({
|
|
407
|
-
managedIdentityIdParams: {
|
|
408
|
-
userAssignedClientId: authConfig.clientId || ''
|
|
409
|
-
},
|
|
410
|
-
system: this.sysOptions
|
|
411
|
-
})
|
|
412
|
-
const token = await mia.acquireToken({
|
|
413
|
-
resource: scope
|
|
414
|
-
})
|
|
415
|
-
return token?.accessToken
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Acquires a token using a certificate.
|
|
420
|
-
* @param authConfig The authentication configuration.
|
|
421
|
-
* @param scope The scope for the token.
|
|
422
|
-
* @returns A promise that resolves to the access token.
|
|
423
|
-
*/
|
|
424
|
-
private async acquireTokenWithCertificate (authConfig: AuthConfiguration, scope: string) {
|
|
425
|
-
const privateKeySource = fs.readFileSync(authConfig.certKeyFile as string)
|
|
426
|
-
|
|
427
|
-
const privateKeyObject = crypto.createPrivateKey({
|
|
428
|
-
key: privateKeySource,
|
|
429
|
-
format: 'pem'
|
|
430
|
-
})
|
|
431
|
-
|
|
432
|
-
const privateKey = privateKeyObject.export({
|
|
433
|
-
format: 'pem',
|
|
434
|
-
type: 'pkcs8'
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
const pemFile = fs.readFileSync(authConfig.certPemFile as string)
|
|
438
|
-
const pubKeyObject = new crypto.X509Certificate(pemFile)
|
|
439
|
-
|
|
440
|
-
const cca = new ConfidentialClientApplication({
|
|
441
|
-
auth: {
|
|
442
|
-
clientId: authConfig.clientId || '',
|
|
443
|
-
authority: `${authConfig.authority}/${authConfig.tenantId || 'botframework.com'}`,
|
|
444
|
-
clientCertificate: {
|
|
445
|
-
privateKey: privateKey as string,
|
|
446
|
-
thumbprint: pubKeyObject.fingerprint.replaceAll(':', ''),
|
|
447
|
-
x5c: pemFile.toString()
|
|
448
|
-
}
|
|
449
|
-
},
|
|
450
|
-
system: this.sysOptions
|
|
451
|
-
})
|
|
452
|
-
const token = await cca.acquireTokenByClientCredential({
|
|
453
|
-
scopes: [`${scope}/.default`],
|
|
454
|
-
correlationId: v4(),
|
|
455
|
-
azureRegion: authConfig.azureRegion
|
|
456
|
-
})
|
|
457
|
-
if (!token?.accessToken) {
|
|
458
|
-
throw new Error('Failed to acquire token using certificate')
|
|
459
|
-
}
|
|
460
|
-
return token.accessToken
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Acquires a token using a client secret.
|
|
465
|
-
* @param authConfig The authentication configuration.
|
|
466
|
-
* @param scope The scope for the token.
|
|
467
|
-
* @returns A promise that resolves to the access token.
|
|
468
|
-
*/
|
|
469
|
-
private async acquireAccessTokenViaSecret (authConfig: AuthConfiguration, scope: string) {
|
|
470
|
-
const cca = new ConfidentialClientApplication({
|
|
471
|
-
auth: {
|
|
472
|
-
clientId: authConfig.clientId as string,
|
|
473
|
-
authority: `${authConfig.authority}/${authConfig.tenantId || 'botframework.com'}`,
|
|
474
|
-
clientSecret: authConfig.clientSecret
|
|
475
|
-
},
|
|
476
|
-
system: this.sysOptions
|
|
477
|
-
})
|
|
478
|
-
const token = await cca.acquireTokenByClientCredential({
|
|
479
|
-
scopes: [`${scope}/.default`],
|
|
480
|
-
correlationId: v4(),
|
|
481
|
-
azureRegion: authConfig.azureRegion
|
|
482
|
-
})
|
|
483
|
-
if (!token?.accessToken) {
|
|
484
|
-
throw new Error('Failed to acquire token using client secret')
|
|
485
|
-
}
|
|
486
|
-
return token.accessToken
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Acquires a token using a FIC client assertion.
|
|
491
|
-
* @param authConfig The authentication configuration.
|
|
492
|
-
* @param scope The scope for the token.
|
|
493
|
-
* @returns A promise that resolves to the access token.
|
|
494
|
-
*/
|
|
495
|
-
private async acquireAccessTokenViaFIC (authConfig: AuthConfiguration, scope: string) : Promise<string> {
|
|
496
|
-
const scopes = [`${scope}/.default`]
|
|
497
|
-
const clientAssertion = await this.fetchExternalToken(authConfig.FICClientId as string)
|
|
498
|
-
const cca = new ConfidentialClientApplication({
|
|
499
|
-
auth: {
|
|
500
|
-
clientId: authConfig.clientId as string,
|
|
501
|
-
authority: `${authConfig.authority}/${authConfig.tenantId}`,
|
|
502
|
-
clientAssertion
|
|
503
|
-
},
|
|
504
|
-
system: this.sysOptions
|
|
505
|
-
})
|
|
506
|
-
const token = await cca.acquireTokenByClientCredential({ scopes, azureRegion: authConfig.azureRegion })
|
|
507
|
-
logger.debug('got token using FIC client assertion')
|
|
508
|
-
if (!token?.accessToken) {
|
|
509
|
-
throw new Error('Failed to acquire token using FIC client assertion')
|
|
510
|
-
}
|
|
511
|
-
return token.accessToken
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
* Acquires a token using a Workload Identity client assertion.
|
|
516
|
-
* @param authConfig The authentication configuration.
|
|
517
|
-
* @param scope The scope for the token.
|
|
518
|
-
* @returns A promise that resolves to the access token.
|
|
519
|
-
*/
|
|
520
|
-
private async acquireAccessTokenViaWID (authConfig: AuthConfiguration, scope: string) : Promise<string> {
|
|
521
|
-
const scopes = [`${scope}/.default`]
|
|
522
|
-
const clientAssertion = fs.readFileSync(authConfig.WIDAssertionFile as string, 'utf8')
|
|
523
|
-
const cca = new ConfidentialClientApplication({
|
|
524
|
-
auth: {
|
|
525
|
-
clientId: authConfig.clientId as string,
|
|
526
|
-
authority: `https://login.microsoftonline.com/${authConfig.tenantId}`,
|
|
527
|
-
clientAssertion
|
|
528
|
-
},
|
|
529
|
-
system: this.sysOptions
|
|
530
|
-
})
|
|
531
|
-
const token = await cca.acquireTokenByClientCredential({ scopes, azureRegion: authConfig.azureRegion })
|
|
532
|
-
logger.debug('got token using WID client assertion')
|
|
533
|
-
if (!token?.accessToken) {
|
|
534
|
-
throw new Error('Failed to acquire token using WID client assertion')
|
|
535
|
-
}
|
|
536
|
-
return token.accessToken
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
/**
|
|
540
|
-
* Fetches an external token.
|
|
541
|
-
* @param FICClientId The FIC client ID.
|
|
542
|
-
* @returns A promise that resolves to the external token.
|
|
543
|
-
*/
|
|
544
|
-
private async fetchExternalToken (FICClientId: string) : Promise<string> {
|
|
545
|
-
const managedIdentityClientAssertion = new ManagedIdentityApplication({
|
|
546
|
-
managedIdentityIdParams: {
|
|
547
|
-
userAssignedClientId: FICClientId
|
|
548
|
-
},
|
|
549
|
-
system: this.sysOptions
|
|
550
|
-
}
|
|
551
|
-
)
|
|
552
|
-
const response = await managedIdentityClientAssertion.acquireToken({
|
|
553
|
-
resource: audience,
|
|
554
|
-
forceRefresh: true
|
|
555
|
-
})
|
|
556
|
-
logger.debug('got token for FIC')
|
|
557
|
-
if (!response?.accessToken) {
|
|
558
|
-
throw new Error('Failed to acquire external token for FIC client assertion')
|
|
559
|
-
}
|
|
560
|
-
return response.accessToken
|
|
561
|
-
}
|
|
562
|
-
}
|