@microsoft/agents-hosting 1.1.0-alpha.5 → 1.1.0-alpha.58
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 +10 -6
- package/dist/src/activityWireCompat.js +8 -3
- package/dist/src/activityWireCompat.js.map +1 -1
- package/dist/src/agent-client/agentClient.js +7 -3
- package/dist/src/agent-client/agentClient.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.js +6 -2
- package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
- package/dist/src/app/agentApplication.d.ts +26 -11
- package/dist/src/app/agentApplication.js +93 -82
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationBuilder.d.ts +2 -2
- package/dist/src/app/agentApplicationBuilder.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +9 -2
- package/dist/src/app/appRoute.d.ts +7 -0
- package/dist/src/app/{authorization.d.ts → auth/authorization.d.ts} +33 -139
- package/dist/src/app/auth/authorization.js +188 -0
- package/dist/src/app/auth/authorization.js.map +1 -0
- package/dist/src/app/auth/authorizationManager.d.ts +71 -0
- package/dist/src/app/auth/authorizationManager.js +170 -0
- package/dist/src/app/auth/authorizationManager.js.map +1 -0
- package/dist/src/app/auth/handlerStorage.d.ts +36 -0
- package/dist/src/app/auth/handlerStorage.js +62 -0
- package/dist/src/app/auth/handlerStorage.js.map +1 -0
- package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +97 -0
- package/dist/src/app/auth/handlers/agenticAuthorization.js +145 -0
- package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -0
- package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +222 -0
- package/dist/src/app/auth/handlers/azureBotAuthorization.js +428 -0
- package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -0
- package/dist/src/app/auth/handlers/index.d.ts +2 -0
- package/dist/src/app/auth/handlers/index.js +19 -0
- package/dist/src/app/auth/handlers/index.js.map +1 -0
- package/dist/src/app/auth/index.d.ts +2 -0
- package/dist/src/app/auth/index.js +19 -0
- package/dist/src/app/auth/index.js.map +1 -0
- package/dist/src/app/auth/types.d.ts +104 -0
- package/dist/src/app/auth/types.js +24 -0
- package/dist/src/app/auth/types.js.map +1 -0
- package/dist/src/app/index.d.ts +2 -3
- package/dist/src/app/index.js +2 -3
- package/dist/src/app/index.js.map +1 -1
- package/dist/src/app/routeList.d.ts +1 -1
- package/dist/src/app/routeList.js +22 -5
- package/dist/src/app/routeList.js.map +1 -1
- package/dist/src/app/streaming/streamingResponse.js +2 -1
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/auth/MemoryCache.d.ts +16 -0
- package/dist/src/auth/MemoryCache.js +58 -0
- package/dist/src/auth/MemoryCache.js.map +1 -0
- package/dist/src/auth/authConfiguration.d.ts +44 -2
- package/dist/src/auth/authConfiguration.js +218 -53
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/authConstants.d.ts +11 -0
- package/dist/src/auth/authConstants.js +15 -0
- package/dist/src/auth/authConstants.js.map +1 -0
- package/dist/src/auth/authProvider.d.ts +23 -0
- package/dist/src/auth/connections.d.ts +40 -0
- package/dist/src/auth/connections.js +7 -0
- package/dist/src/auth/connections.js.map +1 -0
- package/dist/src/auth/index.d.ts +2 -0
- package/dist/src/auth/index.js +2 -0
- package/dist/src/auth/index.js.map +1 -1
- package/dist/src/auth/jwt-middleware.js +31 -18
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msalConnectionManager.d.ts +63 -0
- package/dist/src/auth/msalConnectionManager.js +124 -0
- package/dist/src/auth/msalConnectionManager.js.map +1 -0
- package/dist/src/auth/msalTokenProvider.d.ts +31 -0
- package/dist/src/auth/msalTokenProvider.js +167 -16
- package/dist/src/auth/msalTokenProvider.js.map +1 -1
- package/dist/src/baseAdapter.d.ts +10 -25
- package/dist/src/baseAdapter.js +2 -15
- package/dist/src/baseAdapter.js.map +1 -1
- package/dist/src/cloudAdapter.d.ts +40 -23
- package/dist/src/cloudAdapter.js +132 -56
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/connector-client/connectorClient.d.ts +9 -0
- package/dist/src/connector-client/connectorClient.js +39 -9
- package/dist/src/connector-client/connectorClient.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/oauth/index.d.ts +0 -1
- package/dist/src/oauth/index.js +0 -1
- package/dist/src/oauth/index.js.map +1 -1
- package/dist/src/oauth/userTokenClient.d.ts +30 -13
- package/dist/src/oauth/userTokenClient.js +64 -26
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/oauth/userTokenClient.types.d.ts +19 -6
- package/dist/src/turnContext.d.ts +7 -1
- package/dist/src/turnContext.js +11 -4
- package/dist/src/turnContext.js.map +1 -1
- package/package.json +10 -6
- package/src/activityWireCompat.ts +8 -3
- package/src/agent-client/agentClient.ts +9 -3
- package/src/agent-client/agentResponseHandler.ts +5 -2
- package/src/app/agentApplication.ts +97 -75
- package/src/app/agentApplicationBuilder.ts +2 -2
- package/src/app/agentApplicationOptions.ts +10 -2
- package/src/app/appRoute.ts +8 -0
- package/src/app/auth/authorization.ts +252 -0
- package/src/app/auth/authorizationManager.ts +213 -0
- package/src/app/auth/handlerStorage.ts +61 -0
- package/src/app/auth/handlers/agenticAuthorization.ts +194 -0
- package/src/app/auth/handlers/azureBotAuthorization.ts +599 -0
- package/src/app/auth/handlers/index.ts +2 -0
- package/src/app/auth/index.ts +2 -0
- package/src/app/auth/types.ts +111 -0
- package/src/app/index.ts +2 -3
- package/src/app/routeList.ts +24 -5
- package/src/app/streaming/streamingResponse.ts +2 -1
- package/src/auth/MemoryCache.ts +59 -0
- package/src/auth/authConfiguration.ts +258 -52
- package/src/auth/authConstants.ts +11 -0
- package/src/auth/authProvider.ts +31 -0
- package/src/auth/connections.ts +46 -0
- package/src/auth/index.ts +2 -0
- package/src/auth/jwt-middleware.ts +38 -21
- package/src/auth/msalConnectionManager.ts +150 -0
- package/src/auth/msalTokenProvider.ts +209 -9
- package/src/baseAdapter.ts +10 -29
- package/src/cloudAdapter.ts +192 -67
- package/src/connector-client/connectorClient.ts +49 -10
- package/src/index.ts +0 -1
- package/src/oauth/index.ts +0 -1
- package/src/oauth/userTokenClient.ts +79 -23
- package/src/oauth/userTokenClient.types.ts +20 -8
- package/src/turnContext.ts +16 -5
- package/dist/src/app/authorization.js +0 -387
- package/dist/src/app/authorization.js.map +0 -1
- package/dist/src/claimsIdentity.d.ts +0 -35
- package/dist/src/claimsIdentity.js +0 -43
- package/dist/src/claimsIdentity.js.map +0 -1
- package/dist/src/oauth/oAuthFlow.d.ts +0 -119
- package/dist/src/oauth/oAuthFlow.js +0 -316
- package/dist/src/oauth/oAuthFlow.js.map +0 -1
- package/src/app/authorization.ts +0 -432
- package/src/claimsIdentity.ts +0 -47
- package/src/oauth/oAuthFlow.ts +0 -378
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Activity } from '@microsoft/agents-activity'
|
|
7
|
+
import { AuthConfiguration } from './authConfiguration'
|
|
8
|
+
import { AuthProvider } from './authProvider'
|
|
9
|
+
|
|
10
|
+
export interface Connections {
|
|
11
|
+
/**
|
|
12
|
+
* Get the OAuth connection for the agent.
|
|
13
|
+
* @param name - The connection name. Must match a configured OAuth connection.
|
|
14
|
+
* @returns An AuthProvider instance.
|
|
15
|
+
* @throws {Error} If the connection name is not found.
|
|
16
|
+
*/
|
|
17
|
+
getConnection: (name: string) => AuthProvider
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the default OAuth connection for the agent.
|
|
21
|
+
* @returns An AuthProvider instance.
|
|
22
|
+
*/
|
|
23
|
+
getDefaultConnection: () => AuthProvider
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the OAuth token provider for the agent.
|
|
27
|
+
* @param audience - The audience.
|
|
28
|
+
* @param serviceUrl - The service url.
|
|
29
|
+
* @returns An AuthProvider instance.
|
|
30
|
+
*/
|
|
31
|
+
getTokenProvider: (audience: string, serviceUrl: string) => AuthProvider
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get the OAuth token provider for the agent.
|
|
35
|
+
* @param audience - The audience.
|
|
36
|
+
* @param activity - The activity.
|
|
37
|
+
* @returns An AuthProvider instance.
|
|
38
|
+
*/
|
|
39
|
+
getTokenProviderFromActivity: (audience: string, activity: Activity) => AuthProvider
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get the default connection configuration for the agent.
|
|
43
|
+
* @returns An Auth Configuration.
|
|
44
|
+
*/
|
|
45
|
+
getDefaultConnectionConfiguration: () => AuthConfiguration
|
|
46
|
+
}
|
package/src/auth/index.ts
CHANGED
|
@@ -19,18 +19,37 @@ const logger = debug('agents:jwt-middleware')
|
|
|
19
19
|
* @returns A promise that resolves to the JWT payload.
|
|
20
20
|
*/
|
|
21
21
|
const verifyToken = async (raw: string, config: AuthConfiguration): Promise<JwtPayload> => {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const payload = jwt.decode(raw) as JwtPayload
|
|
23
|
+
logger.debug('jwt.decode ', JSON.stringify(payload))
|
|
24
|
+
|
|
25
|
+
if (!payload) {
|
|
26
|
+
throw new Error('invalid token')
|
|
27
|
+
}
|
|
28
|
+
const audience = payload.aud
|
|
29
|
+
|
|
30
|
+
const matchingEntry = config.connections && config.connections.size > 0
|
|
31
|
+
? [...config.connections.entries()].find(([_, configuration]) => configuration.clientId === audience)
|
|
32
|
+
: undefined
|
|
33
|
+
|
|
34
|
+
if (!matchingEntry) {
|
|
35
|
+
const err = new Error('Audience mismatch')
|
|
36
|
+
logger.error(err.message, audience)
|
|
37
|
+
throw err
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const [key, authConfig] = matchingEntry
|
|
41
|
+
logger.debug(`Audience found at key: ${key}`)
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
const jwksUri = payload.iss === 'https://api.botframework.com'
|
|
44
|
+
? 'https://login.botframework.com/v1/.well-known/keys'
|
|
45
|
+
: `${authConfig.authority}/${authConfig.tenantId}/discovery/v2.0/keys`
|
|
31
46
|
|
|
47
|
+
logger.debug(`fetching keys from ${jwksUri}`)
|
|
48
|
+
const jwksClient: JwksClient = jwksRsa({ jwksUri })
|
|
49
|
+
|
|
50
|
+
const getKey: GetPublicKeyOrSecret = (header: JwtHeader, callback: SignCallback) => {
|
|
32
51
|
jwksClient.getSigningKey(header.kid, (err: Error | null, key: SigningKey | undefined): void => {
|
|
33
|
-
if (err
|
|
52
|
+
if (err) {
|
|
34
53
|
logger.error('jwksClient.getSigningKey ', JSON.stringify(err))
|
|
35
54
|
logger.error(JSON.stringify(err))
|
|
36
55
|
callback(err, undefined)
|
|
@@ -41,24 +60,22 @@ const verifyToken = async (raw: string, config: AuthConfiguration): Promise<JwtP
|
|
|
41
60
|
})
|
|
42
61
|
}
|
|
43
62
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
63
|
+
const verifyOptions: jwt.VerifyOptions = {
|
|
64
|
+
issuer: authConfig.issuers as [string, ...string[]],
|
|
65
|
+
audience: [authConfig.clientId!, 'https://api.botframework.com'],
|
|
66
|
+
ignoreExpiration: false,
|
|
67
|
+
algorithms: ['RS256'],
|
|
68
|
+
clockTolerance: 300
|
|
69
|
+
}
|
|
52
70
|
|
|
71
|
+
return await new Promise((resolve, reject) => {
|
|
53
72
|
jwt.verify(raw, getKey, verifyOptions, (err, user) => {
|
|
54
|
-
if (err
|
|
73
|
+
if (err) {
|
|
55
74
|
logger.error('jwt.verify ', JSON.stringify(err))
|
|
56
75
|
reject(err)
|
|
57
76
|
return
|
|
58
77
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
resolve(tokenClaims)
|
|
78
|
+
resolve(user as JwtPayload)
|
|
62
79
|
})
|
|
63
80
|
})
|
|
64
81
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Activity, RoleTypes } from '@microsoft/agents-activity'
|
|
7
|
+
import { AuthConfiguration } from './authConfiguration'
|
|
8
|
+
import { AuthProvider } from './authProvider'
|
|
9
|
+
import { Connections } from './connections'
|
|
10
|
+
import { MsalTokenProvider } from './msalTokenProvider'
|
|
11
|
+
|
|
12
|
+
export interface ConnectionMapItem {
|
|
13
|
+
audience?: string
|
|
14
|
+
serviceUrl: string
|
|
15
|
+
connection: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class MsalConnectionManager implements Connections {
|
|
19
|
+
private _connections: Map<string, MsalTokenProvider>
|
|
20
|
+
private _connectionsMap: ConnectionMapItem[]
|
|
21
|
+
private _serviceConnectionConfiguration: AuthConfiguration
|
|
22
|
+
private static readonly DEFAULT_CONNECTION = 'serviceConnection'
|
|
23
|
+
|
|
24
|
+
constructor (
|
|
25
|
+
connectionsConfigurations: Map<string, AuthConfiguration> = new Map(),
|
|
26
|
+
connectionsMap: ConnectionMapItem[] = [],
|
|
27
|
+
configuration: AuthConfiguration = {}) {
|
|
28
|
+
this._connections = new Map()
|
|
29
|
+
this._connectionsMap = connectionsMap.length > 0 ? connectionsMap : (configuration.connectionsMap || [])
|
|
30
|
+
this._serviceConnectionConfiguration = {}
|
|
31
|
+
|
|
32
|
+
const providedConnections = connectionsConfigurations.size > 0 ? connectionsConfigurations : (configuration.connections || new Map())
|
|
33
|
+
|
|
34
|
+
for (const [name, config] of providedConnections) {
|
|
35
|
+
// Instantiate MsalTokenProvider for each connection
|
|
36
|
+
this._connections.set(name, new MsalTokenProvider(config))
|
|
37
|
+
if (name === MsalConnectionManager.DEFAULT_CONNECTION) {
|
|
38
|
+
this._serviceConnectionConfiguration = config
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the OAuth connection for the agent.
|
|
45
|
+
* @param connectionName The name of the connection.
|
|
46
|
+
* @returns The OAuth connection for the agent.
|
|
47
|
+
*/
|
|
48
|
+
getConnection (connectionName: string): MsalTokenProvider {
|
|
49
|
+
const conn = this._connections.get(connectionName)
|
|
50
|
+
if (!conn) {
|
|
51
|
+
throw new Error(`Connection not found: ${connectionName}`)
|
|
52
|
+
}
|
|
53
|
+
return conn
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get the default OAuth connection for the agent.
|
|
58
|
+
* @returns The default OAuth connection for the agent.
|
|
59
|
+
*/
|
|
60
|
+
getDefaultConnection (): MsalTokenProvider {
|
|
61
|
+
if (this._connections.size === 0) {
|
|
62
|
+
throw new Error('No connections found for this Agent in the Connections Configuration.')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Return the wildcard map item instance.
|
|
66
|
+
for (const item of this._connectionsMap) {
|
|
67
|
+
if (item.serviceUrl === '*' && !item.audience) {
|
|
68
|
+
return this.getConnection(item.connection)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return this._connections.values().next().value as MsalTokenProvider
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Finds a connection based on a map.
|
|
77
|
+
*
|
|
78
|
+
* @param audience The audience.
|
|
79
|
+
* @param serviceUrl The service URL.
|
|
80
|
+
* @returns The TokenProvider for the connection.
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* Example environment variables:
|
|
84
|
+
* connectionsMap__0__connection=seviceConnection
|
|
85
|
+
* connectionsMap__0__serviceUrl=http://*..botframework.com/*
|
|
86
|
+
* connectionsMap__0__audience=optional
|
|
87
|
+
* connectionsMap__1__connection=agentic
|
|
88
|
+
* connectionsMap__1__serviceUrl=agentic
|
|
89
|
+
*
|
|
90
|
+
* ServiceUrl is: A regex to match with, or "*" for any serviceUrl value.
|
|
91
|
+
* Connection is: A name in the 'Connections' list.
|
|
92
|
+
*/
|
|
93
|
+
getTokenProvider (audience: string, serviceUrl: string): MsalTokenProvider {
|
|
94
|
+
if (!audience || !serviceUrl) throw new Error('Audience and Service URL are required to get the token provider.')
|
|
95
|
+
|
|
96
|
+
if (this._connectionsMap.length === 0) {
|
|
97
|
+
return this.getDefaultConnection()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const item of this._connectionsMap) {
|
|
101
|
+
let audienceMatch = true
|
|
102
|
+
|
|
103
|
+
// if we have an audience to match against, match it.
|
|
104
|
+
if (item.audience && audience) {
|
|
105
|
+
audienceMatch = item.audience === audience
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (audienceMatch) {
|
|
109
|
+
if (item.serviceUrl === '*' || !item.serviceUrl) {
|
|
110
|
+
return this.getConnection(item.connection)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const regex = new RegExp(item.serviceUrl, 'i')
|
|
114
|
+
if (regex.test(serviceUrl)) {
|
|
115
|
+
return this.getConnection(item.connection)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`No connection found for audience: ${audience} and serviceUrl: ${serviceUrl}`)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Finds a connection based on an activity's blueprint.
|
|
124
|
+
* @param audience The audience.
|
|
125
|
+
* @param activity The activity.
|
|
126
|
+
* @returns The TokenProvider for the connection.
|
|
127
|
+
*/
|
|
128
|
+
getTokenProviderFromActivity (audience: string, activity: Activity): AuthProvider {
|
|
129
|
+
let connection = this.getTokenProvider(audience, activity.serviceUrl || '')
|
|
130
|
+
|
|
131
|
+
// This is for the case where the Agentic BlueprintId is not the same as the AppId
|
|
132
|
+
if (connection &&
|
|
133
|
+
(activity.recipient?.role === RoleTypes.AgenticIdentity ||
|
|
134
|
+
activity.recipient?.role === RoleTypes.AgenticUser)) {
|
|
135
|
+
if (connection.connectionSettings?.altBlueprintConnectionName &&
|
|
136
|
+
connection.connectionSettings.altBlueprintConnectionName.trim() !== '') {
|
|
137
|
+
connection = this.getConnection(connection.connectionSettings?.altBlueprintConnectionName as string)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return connection
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get the default connection configuration for the agent.
|
|
145
|
+
* @returns The default connection configuration for the agent.
|
|
146
|
+
*/
|
|
147
|
+
getDefaultConnectionConfiguration (): AuthConfiguration {
|
|
148
|
+
return this._serviceConnectionConfiguration
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ConfidentialClientApplication, LogLevel, ManagedIdentityApplication, NodeSystemOptions } from '@azure/msal-node'
|
|
7
|
+
import axios from 'axios'
|
|
7
8
|
import { AuthConfiguration } from './authConfiguration'
|
|
8
9
|
import { AuthProvider } from './authProvider'
|
|
9
10
|
import { debug } from '@microsoft/agents-activity/logger'
|
|
10
11
|
import { v4 } from 'uuid'
|
|
12
|
+
import { MemoryCache } from './MemoryCache'
|
|
11
13
|
|
|
12
14
|
import fs from 'fs'
|
|
13
15
|
import crypto from 'crypto'
|
|
@@ -19,28 +21,62 @@ const logger = debug('agents:msal')
|
|
|
19
21
|
* Provides tokens using MSAL.
|
|
20
22
|
*/
|
|
21
23
|
export class MsalTokenProvider implements AuthProvider {
|
|
24
|
+
private readonly _agenticTokenCache: MemoryCache<string>
|
|
25
|
+
public readonly connectionSettings?: AuthConfiguration
|
|
26
|
+
|
|
27
|
+
constructor (connectionSettings?: AuthConfiguration) {
|
|
28
|
+
this._agenticTokenCache = new MemoryCache<string>()
|
|
29
|
+
this.connectionSettings = connectionSettings
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Gets an access token using the auth configuration from the MsalTokenProvider instance and the provided scope.
|
|
34
|
+
* @param scope The scope for the token.
|
|
35
|
+
* @returns A promise that resolves to the access token.
|
|
36
|
+
*/
|
|
37
|
+
public async getAccessToken (scope: string): Promise<string>
|
|
22
38
|
/**
|
|
23
39
|
* Gets an access token.
|
|
24
40
|
* @param authConfig The authentication configuration.
|
|
25
41
|
* @param scope The scope for the token.
|
|
26
42
|
* @returns A promise that resolves to the access token.
|
|
27
43
|
*/
|
|
28
|
-
public async getAccessToken (authConfig: AuthConfiguration, scope: string): Promise<string>
|
|
44
|
+
public async getAccessToken (authConfig: AuthConfiguration, scope: string): Promise<string>
|
|
45
|
+
|
|
46
|
+
public async getAccessToken (authConfigOrScope: AuthConfiguration | string, scope?: string): Promise<string> {
|
|
47
|
+
let authConfig: AuthConfiguration
|
|
48
|
+
let actualScope: string
|
|
49
|
+
|
|
50
|
+
if (typeof authConfigOrScope === 'string') {
|
|
51
|
+
// Called as getAccessToken(scope)
|
|
52
|
+
if (!this.connectionSettings) {
|
|
53
|
+
throw new Error('Connection settings must be provided to constructor when calling getAccessToken(scope)')
|
|
54
|
+
}
|
|
55
|
+
authConfig = this.connectionSettings
|
|
56
|
+
actualScope = authConfigOrScope
|
|
57
|
+
} else {
|
|
58
|
+
// Called as getAccessToken(authConfig, scope)
|
|
59
|
+
authConfig = authConfigOrScope
|
|
60
|
+
actualScope = scope as string
|
|
61
|
+
}
|
|
62
|
+
|
|
29
63
|
if (!authConfig.clientId && process.env.NODE_ENV !== 'production') {
|
|
30
64
|
return ''
|
|
31
65
|
}
|
|
32
66
|
let token
|
|
33
|
-
if (authConfig.
|
|
34
|
-
token = await this.
|
|
67
|
+
if (authConfig.WIDAssertionFile !== undefined) {
|
|
68
|
+
token = await this.acquireAccessTokenViaWID(authConfig, actualScope)
|
|
69
|
+
} else if (authConfig.FICClientId !== undefined) {
|
|
70
|
+
token = await this.acquireAccessTokenViaFIC(authConfig, actualScope)
|
|
35
71
|
} else if (authConfig.clientSecret !== undefined) {
|
|
36
|
-
token = await this.acquireAccessTokenViaSecret(authConfig,
|
|
72
|
+
token = await this.acquireAccessTokenViaSecret(authConfig, actualScope)
|
|
37
73
|
} else if (authConfig.certPemFile !== undefined &&
|
|
38
74
|
authConfig.certKeyFile !== undefined) {
|
|
39
|
-
token = await this.acquireTokenWithCertificate(authConfig,
|
|
75
|
+
token = await this.acquireTokenWithCertificate(authConfig, actualScope)
|
|
40
76
|
} else if (authConfig.clientSecret === undefined &&
|
|
41
77
|
authConfig.certPemFile === undefined &&
|
|
42
78
|
authConfig.certKeyFile === undefined) {
|
|
43
|
-
token = await this.acquireTokenWithUserAssignedIdentity(authConfig,
|
|
79
|
+
token = await this.acquireTokenWithUserAssignedIdentity(authConfig, actualScope)
|
|
44
80
|
} else {
|
|
45
81
|
throw new Error('Invalid authConfig. ')
|
|
46
82
|
}
|
|
@@ -51,7 +87,33 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
51
87
|
return token
|
|
52
88
|
}
|
|
53
89
|
|
|
54
|
-
public async acquireTokenOnBehalfOf (
|
|
90
|
+
public async acquireTokenOnBehalfOf (scopes: string[], oboAssertion: string): Promise<string>
|
|
91
|
+
public async acquireTokenOnBehalfOf (authConfig: AuthConfiguration, scopes: string[], oboAssertion: string): Promise<string>
|
|
92
|
+
|
|
93
|
+
public async acquireTokenOnBehalfOf (
|
|
94
|
+
authConfigOrScopes: AuthConfiguration | string[],
|
|
95
|
+
scopesOrOboAssertion?: string[] | string,
|
|
96
|
+
oboAssertion?: string
|
|
97
|
+
): Promise<string> {
|
|
98
|
+
let authConfig: AuthConfiguration
|
|
99
|
+
let actualScopes: string[]
|
|
100
|
+
let actualOboAssertion: string
|
|
101
|
+
|
|
102
|
+
if (Array.isArray(authConfigOrScopes)) {
|
|
103
|
+
// Called as acquireTokenOnBehalfOf(scopes, oboAssertion)
|
|
104
|
+
if (!this.connectionSettings) {
|
|
105
|
+
throw new Error('Connection settings must be provided to constructor when calling acquireTokenOnBehalfOf(scopes, oboAssertion)')
|
|
106
|
+
}
|
|
107
|
+
authConfig = this.connectionSettings
|
|
108
|
+
actualScopes = authConfigOrScopes
|
|
109
|
+
actualOboAssertion = scopesOrOboAssertion as string
|
|
110
|
+
} else {
|
|
111
|
+
// Called as acquireTokenOnBehalfOf(authConfig, scopes, oboAssertion)
|
|
112
|
+
authConfig = authConfigOrScopes
|
|
113
|
+
actualScopes = scopesOrOboAssertion as string[]
|
|
114
|
+
actualOboAssertion = oboAssertion!
|
|
115
|
+
}
|
|
116
|
+
|
|
55
117
|
const cca = new ConfidentialClientApplication({
|
|
56
118
|
auth: {
|
|
57
119
|
clientId: authConfig.clientId as string,
|
|
@@ -61,12 +123,128 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
61
123
|
system: this.sysOptions
|
|
62
124
|
})
|
|
63
125
|
const token = await cca.acquireTokenOnBehalfOf({
|
|
64
|
-
oboAssertion,
|
|
65
|
-
scopes
|
|
126
|
+
oboAssertion: actualOboAssertion,
|
|
127
|
+
scopes: actualScopes
|
|
66
128
|
})
|
|
67
129
|
return token?.accessToken as string
|
|
68
130
|
}
|
|
69
131
|
|
|
132
|
+
public async getAgenticInstanceToken (agentAppInstanceId: string): Promise<string> {
|
|
133
|
+
logger.debug('Getting agentic instance token')
|
|
134
|
+
if (!this.connectionSettings) {
|
|
135
|
+
throw new Error('Connection settings must be provided when calling getAgenticInstanceToken')
|
|
136
|
+
}
|
|
137
|
+
const appToken = await this.getAgenticApplicationToken(agentAppInstanceId)
|
|
138
|
+
const cca = new ConfidentialClientApplication({
|
|
139
|
+
auth: {
|
|
140
|
+
clientId: agentAppInstanceId,
|
|
141
|
+
clientAssertion: appToken,
|
|
142
|
+
authority: `${this.connectionSettings.authority}/${this.connectionSettings.tenantId || 'botframework.com'}`,
|
|
143
|
+
},
|
|
144
|
+
system: this.sysOptions
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const token = await cca.acquireTokenByClientCredential({
|
|
148
|
+
scopes: ['api://AzureAdTokenExchange/.default'],
|
|
149
|
+
correlationId: v4()
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
if (!token?.accessToken) {
|
|
153
|
+
throw new Error(`Failed to acquire instance token for agent instance: ${agentAppInstanceId}`)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return token.accessToken
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Does a direct HTTP call to acquire a token for agentic scenarios - do not use this directly!
|
|
161
|
+
* This method will be removed once MSAL is updated with the necessary features.
|
|
162
|
+
* (This is required in order to pass additional parameters into the auth call)
|
|
163
|
+
* @param clientId
|
|
164
|
+
* @param clientAssertion
|
|
165
|
+
* @param scopes
|
|
166
|
+
* @param tokenBodyParameters
|
|
167
|
+
* @returns
|
|
168
|
+
*/
|
|
169
|
+
private async acquireTokenByForAgenticScenarios (clientId: string, clientAssertion: string | undefined, scopes: string[], tokenBodyParameters: { [key: string]: any }): Promise<string | null> {
|
|
170
|
+
if (!this.connectionSettings) {
|
|
171
|
+
throw new Error('Connection settings must be provided when calling getAgenticInstanceToken')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check cache first
|
|
175
|
+
const cacheKey = `${clientId}/${Object.keys(tokenBodyParameters).map(key => key !== 'user_federated_identity_credential' ? `${key}=${tokenBodyParameters[key]}` : '').join('&')}/${scopes.join(';')}`
|
|
176
|
+
if (this._agenticTokenCache.get(cacheKey)) {
|
|
177
|
+
return this._agenticTokenCache.get(cacheKey) as string
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const url = `${this.connectionSettings.authority}/${this.connectionSettings.tenantId || 'botframework.com'}/oauth2/v2.0/token`
|
|
181
|
+
|
|
182
|
+
const data: { [key: string]: any } = {
|
|
183
|
+
client_id: clientId,
|
|
184
|
+
scope: scopes.join(' '),
|
|
185
|
+
...tokenBodyParameters
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (clientAssertion) {
|
|
189
|
+
data.client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
|
190
|
+
data.client_assertion = clientAssertion
|
|
191
|
+
} else {
|
|
192
|
+
data.client_secret = this.connectionSettings.clientSecret
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const token = await axios.post(
|
|
196
|
+
url,
|
|
197
|
+
data,
|
|
198
|
+
{
|
|
199
|
+
headers: {
|
|
200
|
+
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
).catch((error) => {
|
|
204
|
+
logger.error('Error acquiring token: ', error.toJSON())
|
|
205
|
+
throw error
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// capture token, expire local cache 5 minutes early
|
|
209
|
+
this._agenticTokenCache.set(cacheKey, token.data.access_token, token.data.expires_in - 300)
|
|
210
|
+
return token.data.access_token
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public async getAgenticUserToken (agentAppInstanceId: string, agenticUserId: string, scopes: string[]): Promise<string> {
|
|
214
|
+
logger.debug('Getting agentic user token')
|
|
215
|
+
const agentToken = await this.getAgenticApplicationToken(agentAppInstanceId)
|
|
216
|
+
const instanceToken = await this.getAgenticInstanceToken(agentAppInstanceId)
|
|
217
|
+
|
|
218
|
+
const token = await this.acquireTokenByForAgenticScenarios(agentAppInstanceId, agentToken, scopes, {
|
|
219
|
+
user_id: agenticUserId,
|
|
220
|
+
user_federated_identity_credential: instanceToken,
|
|
221
|
+
grant_type: 'user_fic',
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
if (!token) {
|
|
225
|
+
throw new Error(`Failed to acquire instance token for user token: ${agentAppInstanceId}`)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return token
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
public async getAgenticApplicationToken (agentAppInstanceId: string): Promise<string> {
|
|
232
|
+
if (!this.connectionSettings?.clientId) {
|
|
233
|
+
throw new Error('Connection settings must be provided when calling getAgenticApplicationToken')
|
|
234
|
+
}
|
|
235
|
+
logger.debug('Getting agentic application token')
|
|
236
|
+
const token = await this.acquireTokenByForAgenticScenarios(this.connectionSettings.clientId, undefined, ['api://AzureAdTokenExchange/.default'], {
|
|
237
|
+
grant_type: 'client_credentials',
|
|
238
|
+
fmi_path: agentAppInstanceId,
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
if (!token) {
|
|
242
|
+
throw new Error(`Failed to acquire token for agent instance: ${agentAppInstanceId}`)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return token
|
|
246
|
+
}
|
|
247
|
+
|
|
70
248
|
private readonly sysOptions: NodeSystemOptions = {
|
|
71
249
|
loggerOptions: {
|
|
72
250
|
logLevel: LogLevel.Trace,
|
|
@@ -197,6 +375,28 @@ export class MsalTokenProvider implements AuthProvider {
|
|
|
197
375
|
return token?.accessToken as string
|
|
198
376
|
}
|
|
199
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Acquires a token using a Workload Identity client assertion.
|
|
380
|
+
* @param authConfig The authentication configuration.
|
|
381
|
+
* @param scope The scope for the token.
|
|
382
|
+
* @returns A promise that resolves to the access token.
|
|
383
|
+
*/
|
|
384
|
+
private async acquireAccessTokenViaWID (authConfig: AuthConfiguration, scope: string) : Promise<string> {
|
|
385
|
+
const scopes = [`${scope}/.default`]
|
|
386
|
+
const clientAssertion = fs.readFileSync(authConfig.WIDAssertionFile as string, 'utf8')
|
|
387
|
+
const cca = new ConfidentialClientApplication({
|
|
388
|
+
auth: {
|
|
389
|
+
clientId: authConfig.clientId as string,
|
|
390
|
+
authority: `https://login.microsoftonline.com/${authConfig.tenantId}`,
|
|
391
|
+
clientAssertion
|
|
392
|
+
},
|
|
393
|
+
system: this.sysOptions
|
|
394
|
+
})
|
|
395
|
+
const token = await cca.acquireTokenByClientCredential({ scopes })
|
|
396
|
+
logger.info('got token using WID client assertion')
|
|
397
|
+
return token?.accessToken as string
|
|
398
|
+
}
|
|
399
|
+
|
|
200
400
|
/**
|
|
201
401
|
* Fetches an external token.
|
|
202
402
|
* @param FICClientId The FIC client ID.
|
package/src/baseAdapter.ts
CHANGED
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { AuthConfiguration } from './auth/authConfiguration'
|
|
7
|
-
import { AuthProvider } from './auth/authProvider'
|
|
8
|
-
import { MsalTokenProvider } from './auth/msalTokenProvider'
|
|
9
6
|
import { Middleware, MiddlewareHandler, MiddlewareSet } from './middlewareSet'
|
|
10
7
|
import { TurnContext } from './turnContext'
|
|
11
8
|
import { debug } from '@microsoft/agents-activity/logger'
|
|
@@ -13,7 +10,7 @@ import { Activity, ConversationReference } from '@microsoft/agents-activity'
|
|
|
13
10
|
import { ResourceResponse } from './connector-client/resourceResponse'
|
|
14
11
|
import { AttachmentData } from './connector-client/attachmentData'
|
|
15
12
|
import { AttachmentInfo } from './connector-client/attachmentInfo'
|
|
16
|
-
import {
|
|
13
|
+
import { JwtPayload } from 'jsonwebtoken'
|
|
17
14
|
|
|
18
15
|
const logger = debug('agents:base-adapter')
|
|
19
16
|
|
|
@@ -54,35 +51,15 @@ export abstract class BaseAdapter {
|
|
|
54
51
|
await context.sendActivity('To continue to run this agent, please fix the source code.')
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
/**
|
|
58
|
-
* Symbol key used to store agent identity information in the TurnContext.
|
|
59
|
-
*/
|
|
60
|
-
readonly AgentIdentityKey = Symbol('AgentIdentity')
|
|
61
|
-
|
|
62
54
|
/**
|
|
63
55
|
* Symbol key used to store connector client instances in the TurnContext.
|
|
64
56
|
*/
|
|
65
57
|
readonly ConnectorClientKey = Symbol('ConnectorClient')
|
|
66
58
|
|
|
67
59
|
/**
|
|
68
|
-
* Symbol key used to store
|
|
69
|
-
*/
|
|
70
|
-
readonly OAuthScopeKey = Symbol('OAuthScope')
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* The authentication provider used for token management.
|
|
74
|
-
*/
|
|
75
|
-
authProvider: AuthProvider = new MsalTokenProvider()
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* The user token client used for managing user tokens.
|
|
79
|
-
*/
|
|
80
|
-
userTokenClient: UserTokenClient | null = null
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The authentication configuration for the adapter.
|
|
60
|
+
* Symbol key used to store User Token Client instances in the TurnContext.
|
|
84
61
|
*/
|
|
85
|
-
|
|
62
|
+
readonly UserTokenClientKey = Symbol('UserTokenClient')
|
|
86
63
|
|
|
87
64
|
/**
|
|
88
65
|
* Sends a set of activities to the conversation.
|
|
@@ -115,32 +92,36 @@ export abstract class BaseAdapter {
|
|
|
115
92
|
* @returns A promise representing the completion of the continue operation.
|
|
116
93
|
*/
|
|
117
94
|
abstract continueConversation (
|
|
95
|
+
botAppIdOrIdentity: string | JwtPayload,
|
|
118
96
|
reference: Partial<ConversationReference>,
|
|
119
97
|
logic: (revocableContext: TurnContext) => Promise<void>
|
|
120
98
|
): Promise<void>
|
|
121
99
|
|
|
122
100
|
/**
|
|
101
|
+
* @deprecated This function will not be supported in future versions. Use TurnContext.turnState.get<ConnectorClient>(CloudAdapter.ConnectorClientKey).
|
|
123
102
|
* Uploads an attachment.
|
|
124
103
|
* @param conversationId - The conversation ID.
|
|
125
104
|
* @param attachmentData - The attachment data.
|
|
126
105
|
* @returns A promise representing the ResourceResponse for the uploaded attachment.
|
|
127
106
|
*/
|
|
128
|
-
abstract uploadAttachment (conversationId: string, attachmentData: AttachmentData): Promise<ResourceResponse>
|
|
107
|
+
abstract uploadAttachment (context: TurnContext, conversationId: string, attachmentData: AttachmentData): Promise<ResourceResponse>
|
|
129
108
|
|
|
130
109
|
/**
|
|
110
|
+
* @deprecated This function will not be supported in future versions. Use TurnContext.turnState.get<ConnectorClient>(CloudAdapter.ConnectorClientKey).
|
|
131
111
|
* Gets attachment information.
|
|
132
112
|
* @param attachmentId - The attachment ID.
|
|
133
113
|
* @returns A promise representing the AttachmentInfo for the requested attachment.
|
|
134
114
|
*/
|
|
135
|
-
abstract getAttachmentInfo (attachmentId: string): Promise<AttachmentInfo>
|
|
115
|
+
abstract getAttachmentInfo (context: TurnContext, attachmentId: string): Promise<AttachmentInfo>
|
|
136
116
|
|
|
137
117
|
/**
|
|
118
|
+
* @deprecated This function will not be supported in future versions. Use TurnContext.turnState.get<ConnectorClient>(CloudAdapter.ConnectorClientKey).
|
|
138
119
|
* Gets an attachment.
|
|
139
120
|
* @param attachmentId - The attachment ID.
|
|
140
121
|
* @param viewId - The view ID.
|
|
141
122
|
* @returns A promise representing the NodeJS.ReadableStream for the requested attachment.
|
|
142
123
|
*/
|
|
143
|
-
abstract getAttachment (attachmentId: string, viewId: string): Promise<NodeJS.ReadableStream>
|
|
124
|
+
abstract getAttachment (context: TurnContext, attachmentId: string, viewId: string): Promise<NodeJS.ReadableStream>
|
|
144
125
|
|
|
145
126
|
/**
|
|
146
127
|
* Gets the error handler for the adapter.
|