@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.
Files changed (139) hide show
  1. package/dist/package.json +10 -6
  2. package/dist/src/activityWireCompat.js +8 -3
  3. package/dist/src/activityWireCompat.js.map +1 -1
  4. package/dist/src/agent-client/agentClient.js +7 -3
  5. package/dist/src/agent-client/agentClient.js.map +1 -1
  6. package/dist/src/agent-client/agentResponseHandler.js +6 -2
  7. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  8. package/dist/src/app/agentApplication.d.ts +26 -11
  9. package/dist/src/app/agentApplication.js +93 -82
  10. package/dist/src/app/agentApplication.js.map +1 -1
  11. package/dist/src/app/agentApplicationBuilder.d.ts +2 -2
  12. package/dist/src/app/agentApplicationBuilder.js.map +1 -1
  13. package/dist/src/app/agentApplicationOptions.d.ts +9 -2
  14. package/dist/src/app/appRoute.d.ts +7 -0
  15. package/dist/src/app/{authorization.d.ts → auth/authorization.d.ts} +33 -139
  16. package/dist/src/app/auth/authorization.js +188 -0
  17. package/dist/src/app/auth/authorization.js.map +1 -0
  18. package/dist/src/app/auth/authorizationManager.d.ts +71 -0
  19. package/dist/src/app/auth/authorizationManager.js +170 -0
  20. package/dist/src/app/auth/authorizationManager.js.map +1 -0
  21. package/dist/src/app/auth/handlerStorage.d.ts +36 -0
  22. package/dist/src/app/auth/handlerStorage.js +62 -0
  23. package/dist/src/app/auth/handlerStorage.js.map +1 -0
  24. package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +97 -0
  25. package/dist/src/app/auth/handlers/agenticAuthorization.js +145 -0
  26. package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -0
  27. package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +222 -0
  28. package/dist/src/app/auth/handlers/azureBotAuthorization.js +428 -0
  29. package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -0
  30. package/dist/src/app/auth/handlers/index.d.ts +2 -0
  31. package/dist/src/app/auth/handlers/index.js +19 -0
  32. package/dist/src/app/auth/handlers/index.js.map +1 -0
  33. package/dist/src/app/auth/index.d.ts +2 -0
  34. package/dist/src/app/auth/index.js +19 -0
  35. package/dist/src/app/auth/index.js.map +1 -0
  36. package/dist/src/app/auth/types.d.ts +104 -0
  37. package/dist/src/app/auth/types.js +24 -0
  38. package/dist/src/app/auth/types.js.map +1 -0
  39. package/dist/src/app/index.d.ts +2 -3
  40. package/dist/src/app/index.js +2 -3
  41. package/dist/src/app/index.js.map +1 -1
  42. package/dist/src/app/routeList.d.ts +1 -1
  43. package/dist/src/app/routeList.js +22 -5
  44. package/dist/src/app/routeList.js.map +1 -1
  45. package/dist/src/app/streaming/streamingResponse.js +2 -1
  46. package/dist/src/app/streaming/streamingResponse.js.map +1 -1
  47. package/dist/src/auth/MemoryCache.d.ts +16 -0
  48. package/dist/src/auth/MemoryCache.js +58 -0
  49. package/dist/src/auth/MemoryCache.js.map +1 -0
  50. package/dist/src/auth/authConfiguration.d.ts +44 -2
  51. package/dist/src/auth/authConfiguration.js +218 -53
  52. package/dist/src/auth/authConfiguration.js.map +1 -1
  53. package/dist/src/auth/authConstants.d.ts +11 -0
  54. package/dist/src/auth/authConstants.js +15 -0
  55. package/dist/src/auth/authConstants.js.map +1 -0
  56. package/dist/src/auth/authProvider.d.ts +23 -0
  57. package/dist/src/auth/connections.d.ts +40 -0
  58. package/dist/src/auth/connections.js +7 -0
  59. package/dist/src/auth/connections.js.map +1 -0
  60. package/dist/src/auth/index.d.ts +2 -0
  61. package/dist/src/auth/index.js +2 -0
  62. package/dist/src/auth/index.js.map +1 -1
  63. package/dist/src/auth/jwt-middleware.js +31 -18
  64. package/dist/src/auth/jwt-middleware.js.map +1 -1
  65. package/dist/src/auth/msalConnectionManager.d.ts +63 -0
  66. package/dist/src/auth/msalConnectionManager.js +124 -0
  67. package/dist/src/auth/msalConnectionManager.js.map +1 -0
  68. package/dist/src/auth/msalTokenProvider.d.ts +31 -0
  69. package/dist/src/auth/msalTokenProvider.js +167 -16
  70. package/dist/src/auth/msalTokenProvider.js.map +1 -1
  71. package/dist/src/baseAdapter.d.ts +10 -25
  72. package/dist/src/baseAdapter.js +2 -15
  73. package/dist/src/baseAdapter.js.map +1 -1
  74. package/dist/src/cloudAdapter.d.ts +40 -23
  75. package/dist/src/cloudAdapter.js +132 -56
  76. package/dist/src/cloudAdapter.js.map +1 -1
  77. package/dist/src/connector-client/connectorClient.d.ts +9 -0
  78. package/dist/src/connector-client/connectorClient.js +39 -9
  79. package/dist/src/connector-client/connectorClient.js.map +1 -1
  80. package/dist/src/index.d.ts +0 -1
  81. package/dist/src/index.js +0 -1
  82. package/dist/src/index.js.map +1 -1
  83. package/dist/src/oauth/index.d.ts +0 -1
  84. package/dist/src/oauth/index.js +0 -1
  85. package/dist/src/oauth/index.js.map +1 -1
  86. package/dist/src/oauth/userTokenClient.d.ts +30 -13
  87. package/dist/src/oauth/userTokenClient.js +64 -26
  88. package/dist/src/oauth/userTokenClient.js.map +1 -1
  89. package/dist/src/oauth/userTokenClient.types.d.ts +19 -6
  90. package/dist/src/turnContext.d.ts +7 -1
  91. package/dist/src/turnContext.js +11 -4
  92. package/dist/src/turnContext.js.map +1 -1
  93. package/package.json +10 -6
  94. package/src/activityWireCompat.ts +8 -3
  95. package/src/agent-client/agentClient.ts +9 -3
  96. package/src/agent-client/agentResponseHandler.ts +5 -2
  97. package/src/app/agentApplication.ts +97 -75
  98. package/src/app/agentApplicationBuilder.ts +2 -2
  99. package/src/app/agentApplicationOptions.ts +10 -2
  100. package/src/app/appRoute.ts +8 -0
  101. package/src/app/auth/authorization.ts +252 -0
  102. package/src/app/auth/authorizationManager.ts +213 -0
  103. package/src/app/auth/handlerStorage.ts +61 -0
  104. package/src/app/auth/handlers/agenticAuthorization.ts +194 -0
  105. package/src/app/auth/handlers/azureBotAuthorization.ts +599 -0
  106. package/src/app/auth/handlers/index.ts +2 -0
  107. package/src/app/auth/index.ts +2 -0
  108. package/src/app/auth/types.ts +111 -0
  109. package/src/app/index.ts +2 -3
  110. package/src/app/routeList.ts +24 -5
  111. package/src/app/streaming/streamingResponse.ts +2 -1
  112. package/src/auth/MemoryCache.ts +59 -0
  113. package/src/auth/authConfiguration.ts +258 -52
  114. package/src/auth/authConstants.ts +11 -0
  115. package/src/auth/authProvider.ts +31 -0
  116. package/src/auth/connections.ts +46 -0
  117. package/src/auth/index.ts +2 -0
  118. package/src/auth/jwt-middleware.ts +38 -21
  119. package/src/auth/msalConnectionManager.ts +150 -0
  120. package/src/auth/msalTokenProvider.ts +209 -9
  121. package/src/baseAdapter.ts +10 -29
  122. package/src/cloudAdapter.ts +192 -67
  123. package/src/connector-client/connectorClient.ts +49 -10
  124. package/src/index.ts +0 -1
  125. package/src/oauth/index.ts +0 -1
  126. package/src/oauth/userTokenClient.ts +79 -23
  127. package/src/oauth/userTokenClient.types.ts +20 -8
  128. package/src/turnContext.ts +16 -5
  129. package/dist/src/app/authorization.js +0 -387
  130. package/dist/src/app/authorization.js.map +0 -1
  131. package/dist/src/claimsIdentity.d.ts +0 -35
  132. package/dist/src/claimsIdentity.js +0 -43
  133. package/dist/src/claimsIdentity.js.map +0 -1
  134. package/dist/src/oauth/oAuthFlow.d.ts +0 -119
  135. package/dist/src/oauth/oAuthFlow.js +0 -316
  136. package/dist/src/oauth/oAuthFlow.js.map +0 -1
  137. package/src/app/authorization.ts +0 -432
  138. package/src/claimsIdentity.ts +0 -47
  139. 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
@@ -1,5 +1,7 @@
1
1
  export * from './authConfiguration'
2
+ export * from './authConstants'
2
3
  export * from './authProvider'
3
4
  export * from './msalTokenProvider'
4
5
  export * from './request'
5
6
  export * from './msalTokenCredential'
7
+ export * from './msalConnectionManager'
@@ -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 getKey: GetPublicKeyOrSecret = (header: JwtHeader, callback: SignCallback) => {
23
- const payload = jwt.decode(raw) as JwtPayload
24
- logger.debug('jwt.decode ', JSON.stringify(payload))
25
- const jwksUri: string = payload.iss === 'https://api.botframework.com'
26
- ? 'https://login.botframework.com/v1/.well-known/keys'
27
- : `${config.authority}/${config.tenantId}/discovery/v2.0/keys`
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
- logger.debug(`fetching keys from ${jwksUri}`)
30
- const jwksClient: JwksClient = jwksRsa({ jwksUri })
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 != null) {
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
- return await new Promise((resolve, reject) => {
45
- const verifyOptions: jwt.VerifyOptions = {
46
- issuer: config.issuers as [string, ...string[]],
47
- audience: [config.clientId!, 'https://api.botframework.com'],
48
- ignoreExpiration: false,
49
- algorithms: ['RS256'],
50
- clockTolerance: 300
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 != null) {
73
+ if (err) {
55
74
  logger.error('jwt.verify ', JSON.stringify(err))
56
75
  reject(err)
57
76
  return
58
77
  }
59
- const tokenClaims = user as JwtPayload
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.FICClientId !== undefined) {
34
- token = await this.acquireAccessTokenViaFIC(authConfig, scope)
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, scope)
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, scope)
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, scope)
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 (authConfig: AuthConfiguration, scopes: string[], oboAssertion: string): Promise<string> {
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.
@@ -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 { UserTokenClient } from './oauth'
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 OAuth scope information in the TurnContext.
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
- abstract authConfig: AuthConfiguration
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.