@microsoft/agents-hosting 1.5.0-beta.6.ga236d9a19c → 1.5.1
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 -9
- package/dist/src/activityHandler.js +2 -2
- package/dist/src/activityHandler.js.map +1 -1
- package/dist/src/agent-client/agentClient.js +49 -40
- 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.d.ts +36 -10
- package/dist/src/app/agentApplication.js +169 -99
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationBuilder.d.ts +15 -0
- package/dist/src/app/agentApplicationBuilder.js +22 -4
- package/dist/src/app/agentApplicationBuilder.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +38 -0
- package/dist/src/app/attachmentDownloader.js +2 -2
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/auth/authorization.js +12 -9
- package/dist/src/app/auth/authorization.js.map +1 -1
- package/dist/src/app/auth/authorizationManager.d.ts +18 -5
- package/dist/src/app/auth/authorizationManager.js +258 -45
- package/dist/src/app/auth/authorizationManager.js.map +1 -1
- package/dist/src/app/auth/handlerStorage.js +3 -1
- package/dist/src/app/auth/handlerStorage.js.map +1 -1
- package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +19 -16
- package/dist/src/app/auth/handlers/agenticAuthorization.js +46 -52
- package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -1
- package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +51 -75
- package/dist/src/app/auth/handlers/azureBotAuthorization.js +217 -192
- package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -1
- package/dist/src/app/auth/types.d.ts +100 -1
- package/dist/src/app/auth/utils.d.ts +10 -0
- package/dist/src/app/auth/utils.js +21 -0
- package/dist/src/app/auth/utils.js.map +1 -0
- package/dist/src/app/index.d.ts +1 -0
- package/dist/src/app/index.js +1 -0
- package/dist/src/app/index.js.map +1 -1
- package/dist/src/app/proactive/conversation.d.ts +43 -0
- package/dist/src/app/proactive/conversation.js +67 -0
- package/dist/src/app/proactive/conversation.js.map +1 -0
- package/dist/src/app/proactive/conversationBuilder.d.ts +54 -0
- package/dist/src/app/proactive/conversationBuilder.js +110 -0
- package/dist/src/app/proactive/conversationBuilder.js.map +1 -0
- package/dist/src/app/proactive/conversationReferenceBuilder.d.ts +68 -0
- package/dist/src/app/proactive/conversationReferenceBuilder.js +125 -0
- package/dist/src/app/proactive/conversationReferenceBuilder.js.map +1 -0
- package/dist/src/app/proactive/createConversationOptions.d.ts +30 -0
- package/dist/src/app/proactive/createConversationOptions.js +10 -0
- package/dist/src/app/proactive/createConversationOptions.js.map +1 -0
- package/dist/src/app/proactive/createConversationOptionsBuilder.d.ts +69 -0
- package/dist/src/app/proactive/createConversationOptionsBuilder.js +141 -0
- package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -0
- package/dist/src/app/proactive/index.d.ts +7 -0
- package/dist/src/app/proactive/index.js +26 -0
- package/dist/src/app/proactive/index.js.map +1 -0
- package/dist/src/app/proactive/proactive.d.ts +248 -0
- package/dist/src/app/proactive/proactive.js +310 -0
- package/dist/src/app/proactive/proactive.js.map +1 -0
- package/dist/src/app/proactive/proactiveOptions.d.ts +19 -0
- package/dist/src/app/proactive/proactiveOptions.js +5 -0
- package/dist/src/app/proactive/proactiveOptions.js.map +1 -0
- package/dist/src/app/streaming/streamingResponse.js +2 -2
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/app/teamsAttachmentDownloader.js +2 -2
- package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
- package/dist/src/app/turnState.js +2 -2
- package/dist/src/app/turnState.js.map +1 -1
- package/dist/src/auth/authConfiguration.d.ts +61 -0
- package/dist/src/auth/authConfiguration.js +52 -3
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/jwt-middleware.js +2 -2
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msalConnectionManager.js +20 -0
- package/dist/src/auth/msalConnectionManager.js.map +1 -1
- package/dist/src/auth/msalTokenCredential.js +3 -0
- package/dist/src/auth/msalTokenCredential.js.map +1 -1
- package/dist/src/auth/msalTokenProvider.js +136 -110
- package/dist/src/auth/msalTokenProvider.js.map +1 -1
- package/dist/src/baseAdapter.js +2 -2
- package/dist/src/baseAdapter.js.map +1 -1
- package/dist/src/cloudAdapter.js +201 -154
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/connector-client/connectorClient.js +176 -127
- package/dist/src/connector-client/connectorClient.js.map +1 -1
- package/dist/src/errorHelper.js +108 -0
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/middlewareSet.js +2 -2
- package/dist/src/middlewareSet.js.map +1 -1
- package/dist/src/oauth/userTokenClient.js +78 -48
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/observability/index.d.ts +2 -0
- package/dist/src/observability/index.js +21 -0
- package/dist/src/observability/index.js.map +1 -0
- package/dist/src/observability/metrics.d.ts +21 -0
- package/dist/src/observability/metrics.js +87 -0
- package/dist/src/observability/metrics.js.map +1 -0
- package/dist/src/observability/traces.d.ts +234 -0
- package/dist/src/observability/traces.js +962 -0
- package/dist/src/observability/traces.js.map +1 -0
- package/dist/src/state/agentState.js +2 -2
- package/dist/src/state/agentState.js.map +1 -1
- package/dist/src/storage/fileStorage.js +38 -28
- package/dist/src/storage/fileStorage.js.map +1 -1
- package/dist/src/storage/memoryStorage.js +41 -30
- package/dist/src/storage/memoryStorage.js.map +1 -1
- package/dist/src/transcript/fileTranscriptLogger.js +2 -2
- package/dist/src/transcript/fileTranscriptLogger.js.map +1 -1
- package/dist/src/transcript/transcriptLoggerMiddleware.js +2 -2
- package/dist/src/transcript/transcriptLoggerMiddleware.js.map +1 -1
- package/dist/src/turnContext.js +48 -42
- package/dist/src/turnContext.js.map +1 -1
- package/package.json +10 -9
- package/src/activityHandler.ts +1 -1
- package/src/agent-client/agentClient.ts +53 -42
- package/src/agent-client/agentResponseHandler.ts +1 -1
- package/src/app/agentApplication.ts +212 -86
- package/src/app/agentApplicationBuilder.ts +26 -4
- package/src/app/agentApplicationOptions.ts +43 -0
- package/src/app/attachmentDownloader.ts +1 -1
- package/src/app/auth/authorization.ts +11 -8
- package/src/app/auth/authorizationManager.ts +297 -45
- package/src/app/auth/handlerStorage.ts +3 -1
- package/src/app/auth/handlers/agenticAuthorization.ts +68 -72
- package/src/app/auth/handlers/azureBotAuthorization.ts +260 -264
- package/src/app/auth/types.ts +102 -1
- package/src/app/auth/utils.ts +22 -0
- package/src/app/index.ts +1 -0
- package/src/app/proactive/conversation.ts +87 -0
- package/src/app/proactive/conversationBuilder.ts +139 -0
- package/src/app/proactive/conversationReferenceBuilder.ts +161 -0
- package/src/app/proactive/createConversationOptions.ts +35 -0
- package/src/app/proactive/createConversationOptionsBuilder.ts +181 -0
- package/src/app/proactive/index.ts +10 -0
- package/src/app/proactive/proactive.ts +524 -0
- package/src/app/proactive/proactiveOptions.ts +24 -0
- package/src/app/streaming/streamingResponse.ts +1 -1
- package/src/app/teamsAttachmentDownloader.ts +1 -1
- package/src/app/turnState.ts +1 -1
- package/src/auth/authConfiguration.ts +58 -1
- package/src/auth/jwt-middleware.ts +1 -1
- package/src/auth/msalConnectionManager.ts +22 -0
- package/src/auth/msalTokenCredential.ts +4 -0
- package/src/auth/msalTokenProvider.ts +138 -107
- package/src/baseAdapter.ts +1 -1
- package/src/cloudAdapter.ts +239 -184
- package/src/connector-client/connectorClient.ts +169 -126
- package/src/errorHelper.ts +124 -0
- package/src/middlewareSet.ts +1 -1
- package/src/oauth/userTokenClient.ts +70 -46
- package/src/observability/index.ts +5 -0
- package/src/observability/metrics.ts +103 -0
- package/src/observability/traces.ts +988 -0
- package/src/state/agentState.ts +1 -1
- package/src/storage/fileStorage.ts +36 -26
- package/src/storage/memoryStorage.ts +40 -29
- package/src/transcript/fileTranscriptLogger.ts +1 -1
- package/src/transcript/transcriptLoggerMiddleware.ts +1 -1
- package/src/turnContext.ts +47 -41
|
@@ -12,6 +12,36 @@ import { TurnState } from './turnState'
|
|
|
12
12
|
import { HeaderPropagationDefinition } from '../headerPropagation'
|
|
13
13
|
import { AuthorizationOptions } from './auth/types'
|
|
14
14
|
import { Connections } from '../auth/connections'
|
|
15
|
+
import { ProactiveOptions } from './proactive'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Typing timer settings for a specific channel or the global default.
|
|
19
|
+
*/
|
|
20
|
+
export interface TypingTimingOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Delay in milliseconds before the first typing indicator is sent.
|
|
23
|
+
*
|
|
24
|
+
* @default 0
|
|
25
|
+
*/
|
|
26
|
+
initialDelayMs?: number
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Delay in milliseconds between subsequent typing indicators.
|
|
30
|
+
*
|
|
31
|
+
* @default 4000
|
|
32
|
+
*/
|
|
33
|
+
intervalMs?: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Configuration options for automatic typing indicators.
|
|
38
|
+
*/
|
|
39
|
+
export interface TypingOptions extends TypingTimingOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Optional channel-specific timing overrides keyed by channel ID.
|
|
42
|
+
*/
|
|
43
|
+
channelStrategies?: Record<string, TypingTimingOptions>
|
|
44
|
+
}
|
|
15
45
|
|
|
16
46
|
/**
|
|
17
47
|
* Configuration options for creating and initializing an Agent Application.
|
|
@@ -59,6 +89,13 @@ export interface AgentApplicationOptions<TState extends TurnState> {
|
|
|
59
89
|
*/
|
|
60
90
|
startTypingTimer: boolean;
|
|
61
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Optional timing settings for automatic typing indicators.
|
|
94
|
+
*
|
|
95
|
+
* @default undefined
|
|
96
|
+
*/
|
|
97
|
+
typing?: TypingOptions;
|
|
98
|
+
|
|
62
99
|
/**
|
|
63
100
|
* Whether to enable support for long-running message processing operations.
|
|
64
101
|
* When enabled, the agent can handle operations that take longer than the typical
|
|
@@ -147,4 +184,10 @@ export interface AgentApplicationOptions<TState extends TurnState> {
|
|
|
147
184
|
* identity providers.
|
|
148
185
|
*/
|
|
149
186
|
connections?: Connections
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Optional. Configuration for the proactive messaging subsystem.
|
|
190
|
+
* When provided, `app.proactive` will be available.
|
|
191
|
+
*/
|
|
192
|
+
proactive?: ProactiveOptions
|
|
150
193
|
}
|
|
@@ -9,7 +9,7 @@ import { TurnState } from './turnState'
|
|
|
9
9
|
import { TurnContext } from '../turnContext'
|
|
10
10
|
import { Attachment } from '@microsoft/agents-activity'
|
|
11
11
|
import { AuthProvider } from '../auth/authProvider'
|
|
12
|
-
import { debug } from '@microsoft/agents-
|
|
12
|
+
import { debug } from '@microsoft/agents-telemetry'
|
|
13
13
|
import { loadAuthConfigFromEnv, MsalTokenProvider } from '../auth'
|
|
14
14
|
|
|
15
15
|
const logger = debug('agents:attachmentDownloader')
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { debug } from '@microsoft/agents-
|
|
6
|
+
import { debug } from '@microsoft/agents-telemetry'
|
|
7
|
+
import { ExceptionHelper } from '@microsoft/agents-activity'
|
|
7
8
|
import { TokenResponse } from '../../oauth'
|
|
8
9
|
import { TurnContext } from '../../turnContext'
|
|
9
10
|
import { TurnState } from '../turnState'
|
|
10
11
|
import { AuthorizationManager } from './authorizationManager'
|
|
11
12
|
import { AuthorizationHandlerTokenOptions } from './types'
|
|
13
|
+
import { Errors } from '../../errorHelper'
|
|
12
14
|
|
|
13
15
|
const logger = debug('agents:authorization')
|
|
14
16
|
|
|
@@ -148,7 +150,7 @@ export class UserAuthorization implements Authorization {
|
|
|
148
150
|
return { token }
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
throw
|
|
153
|
+
throw ExceptionHelper.generateException(Error, Errors.InvalidExchangeTokenParameters)
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
/**
|
|
@@ -180,7 +182,7 @@ export class UserAuthorization implements Authorization {
|
|
|
180
182
|
if (authHandlerId) {
|
|
181
183
|
await this.getHandler(authHandlerId).signout(context)
|
|
182
184
|
} else {
|
|
183
|
-
for (const handler of
|
|
185
|
+
for (const handler of this.manager.handlers) {
|
|
184
186
|
await handler.signout(context)
|
|
185
187
|
}
|
|
186
188
|
}
|
|
@@ -207,7 +209,7 @@ export class UserAuthorization implements Authorization {
|
|
|
207
209
|
* @public
|
|
208
210
|
*/
|
|
209
211
|
public onSignInSuccess (handler: (context: TurnContext, state: TurnState, authHandlerId?: string) => Promise<void>) {
|
|
210
|
-
for (const authHandler of
|
|
212
|
+
for (const authHandler of this.manager.handlers) {
|
|
211
213
|
authHandler.onSuccess((context) => handler(context, new TurnState(), authHandler.id))
|
|
212
214
|
}
|
|
213
215
|
}
|
|
@@ -239,7 +241,7 @@ export class UserAuthorization implements Authorization {
|
|
|
239
241
|
* @public
|
|
240
242
|
*/
|
|
241
243
|
public onSignInFailure (handler: (context: TurnContext, state: TurnState, authHandlerId?: string, errorMessage?: string) => Promise<void>) {
|
|
242
|
-
for (const authHandler of
|
|
244
|
+
for (const authHandler of this.manager.handlers) {
|
|
243
245
|
authHandler.onFailure((context, reason) => handler(context, new TurnState(), authHandler.id, reason))
|
|
244
246
|
}
|
|
245
247
|
}
|
|
@@ -253,9 +255,10 @@ export class UserAuthorization implements Authorization {
|
|
|
253
255
|
* @private
|
|
254
256
|
*/
|
|
255
257
|
private getHandler (id: string) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
const handler = this.manager.handlers.find(e => e.id.toLowerCase() === id.toLowerCase())
|
|
259
|
+
if (!handler) {
|
|
260
|
+
throw ExceptionHelper.generateException(Error, Errors.AuthHandlerNotFound, undefined, { handlerId: id })
|
|
258
261
|
}
|
|
259
|
-
return
|
|
262
|
+
return handler
|
|
260
263
|
}
|
|
261
264
|
}
|
|
@@ -3,16 +3,24 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Activity,
|
|
6
|
+
import { Activity, ExceptionHelper } from '@microsoft/agents-activity'
|
|
7
|
+
import { debug } from '@microsoft/agents-telemetry'
|
|
7
8
|
import { AgentApplication } from '../agentApplication'
|
|
8
9
|
import { AgenticAuthorization, AzureBotAuthorization } from './handlers'
|
|
9
10
|
import { TurnContext } from '../../turnContext'
|
|
10
11
|
import { HandlerStorage } from './handlerStorage'
|
|
12
|
+
import { Errors } from '../../errorHelper'
|
|
11
13
|
import { ActiveAuthorizationHandler, AuthorizationHandlerStatus, AuthorizationHandler, AuthorizationHandlerSettings, AuthorizationOptions } from './types'
|
|
12
14
|
import { Connections } from '../../auth/connections'
|
|
15
|
+
import { sendInvokeResponse } from './utils'
|
|
16
|
+
import { envParser, envParserUtils } from '../../auth'
|
|
13
17
|
|
|
14
18
|
const logger = debug('agents:authorization:manager')
|
|
15
19
|
|
|
20
|
+
const AGENTIC = 'AgenticUserAuthorization'
|
|
21
|
+
const AGENTIC_LEGACY = 'agentic'
|
|
22
|
+
const AZURE_BOT = 'AzureBotUserAuthorization'
|
|
23
|
+
|
|
16
24
|
/**
|
|
17
25
|
* Active handler information used by the AuthorizationManager.
|
|
18
26
|
*/
|
|
@@ -52,53 +60,113 @@ export class AuthorizationManager {
|
|
|
52
60
|
private _handlers: Record<string, AuthorizationHandler> = {}
|
|
53
61
|
|
|
54
62
|
/**
|
|
55
|
-
*
|
|
56
|
-
* @param app The agent application instance.
|
|
63
|
+
* Environment variable configuration for the latest format.
|
|
57
64
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
private _envLatest = {
|
|
66
|
+
key: {
|
|
67
|
+
prefix: 'AgentApplication__UserAuthorization__Handlers__',
|
|
68
|
+
separator: '__Settings__',
|
|
69
|
+
create (id: string, prop: string) {
|
|
70
|
+
return `${this.prefix}${id}${this.separator}${prop}`
|
|
71
|
+
},
|
|
72
|
+
extract (envKey: string) {
|
|
73
|
+
// Substring: AgentApplication__UserAuthorization__Handlers__<id>__Settings__<prop>
|
|
74
|
+
// position —————————————————————————————————————————> start^ ^end ^prop
|
|
75
|
+
const start = this.prefix.length
|
|
76
|
+
const end = envKey.toUpperCase().indexOf(this.separator.toUpperCase())
|
|
77
|
+
if (end === -1) {
|
|
78
|
+
return { id: undefined, prop: undefined }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const id = envKey.substring(start, end)
|
|
82
|
+
const prop = envKey.substring(end + this.separator.length)
|
|
83
|
+
return { id, prop }
|
|
74
84
|
}
|
|
75
|
-
}
|
|
85
|
+
},
|
|
86
|
+
parser: envParser({
|
|
87
|
+
// Common
|
|
88
|
+
type: envParserUtils.bypass,
|
|
89
|
+
|
|
90
|
+
// Azure Bot
|
|
91
|
+
azureBotOAuthConnectionName: envParserUtils.bypass,
|
|
92
|
+
title: envParserUtils.bypass,
|
|
93
|
+
text: envParserUtils.bypass,
|
|
94
|
+
invalidSignInRetryMessage: envParserUtils.bypass,
|
|
95
|
+
invalidSignInRetryMessageFormat: envParserUtils.bypass,
|
|
96
|
+
invalidSignInRetryMaxExceededMessage: envParserUtils.bypass,
|
|
97
|
+
oboConnectionName: envParserUtils.bypass,
|
|
98
|
+
enableSso (value) {
|
|
99
|
+
return { value: value !== 'false' }
|
|
100
|
+
},
|
|
101
|
+
invalidSignInRetryMax (value) {
|
|
102
|
+
return { value: parseInt(value) }
|
|
103
|
+
},
|
|
104
|
+
oboScopes (value) {
|
|
105
|
+
return this.scopes(value)
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
// Agentic
|
|
109
|
+
altBlueprintConnectionName: envParserUtils.bypass,
|
|
110
|
+
scopes (value) {
|
|
111
|
+
if (value.includes(',')) {
|
|
112
|
+
return { value: value.split(',').map(s => s.trim()).filter(Boolean) }
|
|
113
|
+
}
|
|
114
|
+
return { value: value.split(/\s+/).filter(Boolean) }
|
|
115
|
+
}
|
|
116
|
+
}),
|
|
76
117
|
}
|
|
77
118
|
|
|
78
119
|
/**
|
|
79
|
-
*
|
|
120
|
+
* Environment variable configuration for the legacy format.
|
|
80
121
|
*/
|
|
81
|
-
private
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
122
|
+
private _envLegacy = {
|
|
123
|
+
key: {
|
|
124
|
+
separator: '_',
|
|
125
|
+
},
|
|
126
|
+
parser: envParser({
|
|
127
|
+
// Common
|
|
128
|
+
type: envParserUtils.redirect(this._envLatest.parser, 'type'),
|
|
129
|
+
|
|
130
|
+
// Azure Bot
|
|
131
|
+
connectionName: envParserUtils.redirect(this._envLatest.parser, 'azureBotOAuthConnectionName'),
|
|
132
|
+
connectionTitle: envParserUtils.redirect(this._envLatest.parser, 'title'),
|
|
133
|
+
connectionText: envParserUtils.redirect(this._envLatest.parser, 'text'),
|
|
134
|
+
maxAttempts: envParserUtils.redirect(this._envLatest.parser, 'invalidSignInRetryMax'),
|
|
135
|
+
messages_invalidCode: envParserUtils.redirect(this._envLatest.parser, 'invalidSignInRetryMessage'),
|
|
136
|
+
messages_invalidCodeFormat: envParserUtils.redirect(this._envLatest.parser, 'invalidSignInRetryMessageFormat'),
|
|
137
|
+
messages_maxAttemptsExceeded: envParserUtils.redirect(this._envLatest.parser, 'invalidSignInRetryMaxExceededMessage'),
|
|
138
|
+
obo_connection: envParserUtils.redirect(this._envLatest.parser, 'oboConnectionName'),
|
|
139
|
+
obo_scopes: envParserUtils.redirect(this._envLatest.parser, 'oboScopes'),
|
|
140
|
+
enableSso: envParserUtils.redirect(this._envLatest.parser, 'enableSso'),
|
|
141
|
+
|
|
142
|
+
// Agentic
|
|
143
|
+
scopes: envParserUtils.redirect(this._envLatest.parser, 'scopes'),
|
|
144
|
+
altBlueprintConnectionName: envParserUtils.redirect(this._envLatest.parser, 'altBlueprintConnectionName')
|
|
145
|
+
})
|
|
146
|
+
}
|
|
86
147
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Creates an instance of the AuthorizationManager.
|
|
150
|
+
* @param app The agent application instance.
|
|
151
|
+
*/
|
|
152
|
+
constructor (private app: AgentApplication<any>, private connections: Connections) {
|
|
153
|
+
this.createHandlers()
|
|
154
|
+
|
|
155
|
+
if (this.handlers.length === 0 && app.options.authorization !== undefined) {
|
|
156
|
+
throw ExceptionHelper.generateException(Error, Errors.NoAuthHandlersConfigured)
|
|
91
157
|
}
|
|
92
158
|
|
|
93
|
-
|
|
159
|
+
for (const [id, handler] of Object.entries(this._handlers)) {
|
|
160
|
+
logger.debug('auth handler "%s" type=%s scopes=%o', id, handler.type, handler.scopes)
|
|
161
|
+
}
|
|
94
162
|
}
|
|
95
163
|
|
|
96
164
|
/**
|
|
97
165
|
* Gets the registered authorization handlers.
|
|
98
166
|
* @returns A record of authorization handlers by their IDs.
|
|
99
167
|
*/
|
|
100
|
-
public get handlers ():
|
|
101
|
-
return this._handlers
|
|
168
|
+
public get handlers (): AuthorizationHandler[] {
|
|
169
|
+
return Object.values(this._handlers)
|
|
102
170
|
}
|
|
103
171
|
|
|
104
172
|
/**
|
|
@@ -108,24 +176,45 @@ export class AuthorizationManager {
|
|
|
108
176
|
* @returns The result of the authorization process.
|
|
109
177
|
*/
|
|
110
178
|
public async process (context: TurnContext, getHandlerIds: GetHandlerIds): Promise<AuthorizationManagerProcessResult> {
|
|
179
|
+
const activity = context.activity
|
|
180
|
+
|
|
181
|
+
if (this.handlers.length === 0) {
|
|
182
|
+
return { authorized: true, context }
|
|
183
|
+
}
|
|
184
|
+
|
|
111
185
|
const storage = new HandlerStorage(this.app.options.storage!, context)
|
|
112
186
|
|
|
113
187
|
let active = await this.active(storage, getHandlerIds)
|
|
114
188
|
|
|
115
|
-
if (active
|
|
189
|
+
if (!active && activity.name?.startsWith('signin/')) {
|
|
190
|
+
const reason = `Received '${activity.name}' but no active sign-in flow exists for user '${activity.from?.id}'.`
|
|
191
|
+
logger.warn(reason, activity)
|
|
192
|
+
await sendInvokeResponse(context, {
|
|
193
|
+
status: 400,
|
|
194
|
+
body: { failureDetail: reason }
|
|
195
|
+
})
|
|
196
|
+
return { authorized: false, context }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (active !== undefined && active?.data.activity.conversation?.id !== activity.conversation?.id) {
|
|
116
200
|
logger.warn('Discarding the active session due to the conversation has changed during an active sign-in process', active?.data.activity)
|
|
117
201
|
await storage.delete()
|
|
118
202
|
return { authorized: true, context }
|
|
119
203
|
}
|
|
120
204
|
|
|
121
|
-
const handlers = active?.handlers ?? this.mapHandlers(await getHandlerIds(
|
|
205
|
+
const handlers = active?.handlers ?? this.mapHandlers(await getHandlerIds(activity) ?? []) ?? []
|
|
122
206
|
|
|
123
207
|
// Create a shallow copy to modify the activity, since the signin process depends on it and we want to ensure the next handler depends on the initial activity, not the modified one.
|
|
124
208
|
const sharedContext = new TurnContext(context)
|
|
125
209
|
|
|
126
210
|
for (const handler of handlers) {
|
|
211
|
+
if (handler.scopes?.length) {
|
|
212
|
+
logger.debug('invoking auth handler "%s" scopes=[%s]', handler.id, handler.scopes.join(','))
|
|
213
|
+
} else {
|
|
214
|
+
logger.debug('invoking auth handler "%s"', handler.id)
|
|
215
|
+
}
|
|
127
216
|
const status = await this.signin(storage, handler, sharedContext, active?.data)
|
|
128
|
-
logger.debug(
|
|
217
|
+
logger.debug('auth handler "%s" sign-in status=%s', handler.id, status)
|
|
129
218
|
|
|
130
219
|
if (status === AuthorizationHandlerStatus.IGNORED) {
|
|
131
220
|
await storage.delete()
|
|
@@ -147,7 +236,7 @@ export class AuthorizationManager {
|
|
|
147
236
|
}
|
|
148
237
|
|
|
149
238
|
if (status !== AuthorizationHandlerStatus.APPROVED) {
|
|
150
|
-
throw
|
|
239
|
+
throw ExceptionHelper.generateException(Error, Errors.UnexpectedRegistrationStatus, undefined, { status })
|
|
151
240
|
}
|
|
152
241
|
|
|
153
242
|
await storage.delete()
|
|
@@ -194,7 +283,7 @@ export class AuthorizationManager {
|
|
|
194
283
|
return await handler.signin(context, active)
|
|
195
284
|
} catch (cause) {
|
|
196
285
|
await storage.delete()
|
|
197
|
-
throw
|
|
286
|
+
throw ExceptionHelper.generateException(Error, Errors.FailedToSignIn, cause as Error)
|
|
198
287
|
}
|
|
199
288
|
}
|
|
200
289
|
|
|
@@ -202,16 +291,19 @@ export class AuthorizationManager {
|
|
|
202
291
|
* Maps an array of handler IDs to their corresponding handler instances.
|
|
203
292
|
*/
|
|
204
293
|
private mapHandlers (ids: string[]): AuthorizationHandler[] {
|
|
205
|
-
|
|
294
|
+
const unknownHandlers: string[] = []
|
|
206
295
|
const handlers = ids.map(id => {
|
|
207
|
-
|
|
208
|
-
|
|
296
|
+
const handler = this.handlers.find(e => e.id.toLowerCase() === id.toLowerCase())
|
|
297
|
+
if (!handler) {
|
|
298
|
+
unknownHandlers.push(id)
|
|
209
299
|
}
|
|
210
|
-
return
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
|
|
300
|
+
return handler
|
|
301
|
+
}).filter((handler) => handler !== undefined)
|
|
302
|
+
|
|
303
|
+
if (unknownHandlers.length > 0) {
|
|
304
|
+
throw ExceptionHelper.generateException(Error, Errors.AuthHandlersNotFound, undefined, { handlerIds: unknownHandlers.join(', ') })
|
|
214
305
|
}
|
|
306
|
+
|
|
215
307
|
return handlers
|
|
216
308
|
}
|
|
217
309
|
|
|
@@ -221,4 +313,164 @@ export class AuthorizationManager {
|
|
|
221
313
|
private prefix (id: string, message: string) {
|
|
222
314
|
return `[handler:${id}] ${message}`
|
|
223
315
|
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Creates authorization handlers based on the application configuration and environment variables.
|
|
319
|
+
*/
|
|
320
|
+
private createHandlers () {
|
|
321
|
+
let legacyMessage = ''
|
|
322
|
+
const settings: AuthorizationHandlerSettings = { storage: this.app.options.storage!, connections: this.connections }
|
|
323
|
+
let runtimeEntries = Object.entries(this.app.options.authorization ?? {})
|
|
324
|
+
const result = { latest: {}, legacy: {} } as {
|
|
325
|
+
latest: Record<string, Record<string, any> | undefined>;
|
|
326
|
+
legacy: Record<string, Record<string, any> | undefined>;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
for (const [envKey, envValue] of Object.entries(process.env)) {
|
|
330
|
+
if (!envValue?.trim()) {
|
|
331
|
+
continue
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const upperEnvKey = envKey.toUpperCase()
|
|
335
|
+
|
|
336
|
+
// Legacy: extract handler ID, handler options key and its value, and assign it to the correct runtime handler ID.
|
|
337
|
+
if (!upperEnvKey.startsWith(this._envLatest.key.prefix.toUpperCase())) {
|
|
338
|
+
const [id] = runtimeEntries.find(([id]) => upperEnvKey.startsWith(`${id.toUpperCase()}${this._envLegacy.key.separator}`)) ?? []
|
|
339
|
+
if (!id) {
|
|
340
|
+
continue
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const prop = envKey.substring(id.length + this._envLegacy.key.separator.length)
|
|
344
|
+
if (!prop) {
|
|
345
|
+
continue
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const { key, value } = this._envLegacy.parser.parse(prop as any, envValue)
|
|
349
|
+
if (!key) {
|
|
350
|
+
continue
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
legacyMessage += ` ${envKey}= # Use ${this._envLatest.key.create(id, key)} instead.\n`
|
|
354
|
+
|
|
355
|
+
result.legacy[id] ??= {}
|
|
356
|
+
result.legacy[id][key] = value
|
|
357
|
+
continue
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Latest: extract handler ID, handler options key and its value, and assign it to the correct latest handler ID.
|
|
361
|
+
const { id, prop } = this._envLatest.key.extract(envKey)
|
|
362
|
+
if (!id || !prop) {
|
|
363
|
+
continue
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const { key, value } = this._envLatest.parser.parse(prop as any, envValue)
|
|
367
|
+
if (!key) {
|
|
368
|
+
continue
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Find existing handler ID case-insensitively to avoid duplicates, but keep the original casing for later processing.
|
|
372
|
+
// This allows users to specify environment variables in a case-insensitive way, while ensuring that the handler IDs are treated in a case-sensitive way internally, to avoid issues when referencing them later.
|
|
373
|
+
// For example, if the user specifies AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME and agentapplication__userauthorization__handlers__graph__settings__azurebotoauthconnectionname, we want to treat them as the same handler ID "graph", and not create two separate handlers "GRAPH" and "graph".
|
|
374
|
+
// Note: the first environment variable that is processed will determine the casing of the handler ID, and any subsequent environment variable that matches the same handler ID case-insensitively will be treated as referring to the same handler, regardless of its casing.
|
|
375
|
+
const existingId = id in result.latest ? id : Object.keys(result.latest).find(e => e.toLowerCase() === id.toLowerCase())
|
|
376
|
+
const realId = existingId ?? id
|
|
377
|
+
result.latest[realId] ??= {}
|
|
378
|
+
result.latest[realId][key] = value
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (legacyMessage.length > 0) {
|
|
382
|
+
logger.warn('Deprecated environment variables detected, update to the latest format: (case-insensitive)', `[\n${legacyMessage}]`)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Fix types
|
|
386
|
+
const fixTypes = (entries: [string, any][]) => entries
|
|
387
|
+
.map(([id, options]) => [id, { ...options, type: this.fixType(id, options?.type) }] as [string, AuthorizationOptions[string]])
|
|
388
|
+
|
|
389
|
+
runtimeEntries = fixTypes(runtimeEntries)
|
|
390
|
+
const latestEntries = fixTypes(Object.entries(result.latest))
|
|
391
|
+
const legacyEntries = fixTypes(Object.entries(result.legacy))
|
|
392
|
+
|
|
393
|
+
const registeredHandlers = new Set()
|
|
394
|
+
for (const [id] of [...runtimeEntries, ...latestEntries]) {
|
|
395
|
+
if (registeredHandlers.has(id.toLowerCase())) {
|
|
396
|
+
continue
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Find entries case-insensitively for later processing
|
|
400
|
+
const [, runtime] = runtimeEntries.find(([key]) => key.toLowerCase() === id.toLowerCase()) ?? []
|
|
401
|
+
const [, latest] = latestEntries.find(([key]) => key.toLowerCase() === id.toLowerCase()) ?? []
|
|
402
|
+
const [, legacy] = legacyEntries.find(([key]) => key.toLowerCase() === id.toLowerCase()) ?? []
|
|
403
|
+
|
|
404
|
+
if (runtime !== undefined && latest !== undefined) {
|
|
405
|
+
logger.warn(this.prefix(id, 'Both runtime and latest environment variable configurations detected. Runtime configuration will take precedence over latest environment variables.'))
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Normalize runtime legacy options to latest format
|
|
409
|
+
if (runtime?.type === AZURE_BOT) {
|
|
410
|
+
runtime.azureBotOAuthConnectionName ??= runtime.name
|
|
411
|
+
runtime.invalidSignInRetryMax ??= runtime.maxAttempts
|
|
412
|
+
runtime.invalidSignInRetryMessage ??= runtime.messages?.invalidCode
|
|
413
|
+
runtime.invalidSignInRetryMessageFormat ??= runtime.messages?.invalidCodeFormat
|
|
414
|
+
runtime.invalidSignInRetryMaxExceededMessage ??= runtime.messages?.maxAttemptsExceeded
|
|
415
|
+
runtime.oboConnectionName ??= runtime.obo?.connection
|
|
416
|
+
runtime.oboScopes ??= runtime.obo?.scopes
|
|
417
|
+
delete runtime.name
|
|
418
|
+
delete runtime.maxAttempts
|
|
419
|
+
delete runtime.messages
|
|
420
|
+
delete runtime.obo
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Helper to remove undefined options
|
|
424
|
+
const prune = <T extends Record<string, any>>(obj: T) => {
|
|
425
|
+
const entries = Object.entries(obj).filter(([, e]) => e !== undefined)
|
|
426
|
+
return Object.fromEntries(entries) as T
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Priority: runtime > latest > legacy
|
|
430
|
+
// Pruning done individually to avoid overwriting with undefined when merging.
|
|
431
|
+
const options: AuthorizationOptions[string] = runtime ? { ...prune(legacy || {}), ...prune(runtime) } : prune(latest || {})
|
|
432
|
+
|
|
433
|
+
if (!settings.storage) {
|
|
434
|
+
throw ExceptionHelper.generateException(Error, Errors.StorageRequiredForAuthorization)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (options.type === AGENTIC) {
|
|
438
|
+
this._handlers[id] = new AgenticAuthorization(id, options, settings)
|
|
439
|
+
} else if (options.type === AZURE_BOT) {
|
|
440
|
+
// Set default values if not provided
|
|
441
|
+
options.title ||= 'Sign-in'
|
|
442
|
+
options.text ||= 'Please sign-in to continue'
|
|
443
|
+
options.oboScopes ??= []
|
|
444
|
+
options.enableSso = options.enableSso !== false // default value is true if undefined.
|
|
445
|
+
|
|
446
|
+
this._handlers[id] = new AzureBotAuthorization(id, options, settings)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
registeredHandlers.add(id.toLowerCase())
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Fixes the handler type based on the provided type string, supporting both latest and legacy formats.
|
|
455
|
+
*/
|
|
456
|
+
private fixType (handlerId: string, type: AuthorizationOptions[string]['type'] | string) {
|
|
457
|
+
if (!type) {
|
|
458
|
+
return AZURE_BOT
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (type.toLowerCase() === AGENTIC_LEGACY.toLowerCase()) {
|
|
462
|
+
logger.warn(this.prefix(handlerId, 'The \'agentic\' type is deprecated. Please use \'AgenticUserAuthorization\' instead.'))
|
|
463
|
+
return AGENTIC
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (type.toLowerCase() === AGENTIC.toLowerCase()) {
|
|
467
|
+
return AGENTIC
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (type.toLowerCase() === AZURE_BOT.toLowerCase()) {
|
|
471
|
+
return AZURE_BOT
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
throw ExceptionHelper.generateException(Error, Errors.UnsupportedAuthHandlerType, undefined, { handlerType: type })
|
|
475
|
+
}
|
|
224
476
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { ActiveAuthorizationHandler } from './types'
|
|
7
7
|
import { TurnContext } from '../../turnContext'
|
|
8
8
|
import { Storage } from '../../storage'
|
|
9
|
+
import { ExceptionHelper } from '@microsoft/agents-activity'
|
|
10
|
+
import { Errors } from '../../errorHelper'
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Storage manager for handler state.
|
|
@@ -25,7 +27,7 @@ export class HandlerStorage<TActiveHandler extends ActiveAuthorizationHandler =
|
|
|
25
27
|
const channelId = this.context.activity.channelId?.trim()
|
|
26
28
|
const userId = this.context.activity.from?.id?.trim()
|
|
27
29
|
if (!channelId || !userId) {
|
|
28
|
-
throw
|
|
30
|
+
throw ExceptionHelper.generateException(Error, Errors.ChannelIdAndFromIdRequired)
|
|
29
31
|
}
|
|
30
32
|
return `auth/${channelId}/${userId}`
|
|
31
33
|
}
|