@microsoft/agents-hosting 0.2.14 → 0.3.5
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/README.md +17 -1
- package/dist/src/app/agentApplication.d.ts +16 -4
- package/dist/src/app/agentApplication.js +22 -12
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationBuilder.d.ts +3 -4
- package/dist/src/app/agentApplicationBuilder.js +7 -7
- package/dist/src/app/agentApplicationBuilder.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +26 -2
- package/dist/src/app/appRoute.d.ts +6 -0
- package/dist/src/app/attachmentDownloader.d.ts +18 -0
- package/dist/src/app/attachmentDownloader.js +18 -0
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/conversationUpdateEvents.d.ts +6 -0
- package/dist/src/app/index.d.ts +1 -1
- package/dist/src/app/index.js +1 -1
- package/dist/src/app/index.js.map +1 -1
- package/dist/src/app/inputFileDownloader.d.ts +22 -0
- package/dist/src/app/oauth/authorization.d.ts +87 -0
- package/dist/src/app/oauth/authorization.js +135 -0
- package/dist/src/app/oauth/authorization.js.map +1 -0
- package/dist/src/app/routeHandler.d.ts +8 -0
- package/dist/src/app/routeSelector.d.ts +9 -0
- package/dist/src/app/turnState.d.ts +150 -0
- package/dist/src/app/turnState.js +125 -0
- package/dist/src/app/turnState.js.map +1 -1
- package/dist/src/auth/authConfiguration.d.ts +24 -0
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/request.d.ts +12 -0
- package/dist/src/baseAdapter.d.ts +17 -0
- package/dist/src/baseAdapter.js +17 -0
- package/dist/src/baseAdapter.js.map +1 -1
- package/dist/src/cards/cardFactory.d.ts +3 -0
- package/dist/src/cards/cardFactory.js +3 -0
- package/dist/src/cards/cardFactory.js.map +1 -1
- package/dist/src/cards/o365ConnectorCardActionBase.d.ts +8 -0
- package/dist/src/oauth/oAuthFlow.d.ts +32 -3
- package/dist/src/oauth/oAuthFlow.js +38 -14
- package/dist/src/oauth/oAuthFlow.js.map +1 -1
- package/dist/src/oauth/userTokenClient.d.ts +2 -2
- package/dist/src/oauth/userTokenClient.js +3 -3
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/state/agentStatePropertyAccesor.d.ts +1 -1
- package/dist/src/state/agentStatePropertyAccesor.js +1 -1
- package/dist/src/statusCodes.d.ts +39 -0
- package/dist/src/statusCodes.js +39 -0
- package/dist/src/statusCodes.js.map +1 -1
- package/dist/src/tokenResponseEventName.d.ts +3 -0
- package/dist/src/tokenResponseEventName.js +3 -0
- package/dist/src/tokenResponseEventName.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentApplication.ts +24 -13
- package/src/app/agentApplicationBuilder.ts +8 -8
- package/src/app/agentApplicationOptions.ts +33 -2
- package/src/app/appRoute.ts +7 -0
- package/src/app/attachmentDownloader.ts +18 -0
- package/src/app/conversationUpdateEvents.ts +6 -0
- package/src/app/index.ts +1 -1
- package/src/app/inputFileDownloader.ts +24 -0
- package/src/app/oauth/authorization.ts +162 -0
- package/src/app/routeHandler.ts +8 -0
- package/src/app/routeSelector.ts +9 -0
- package/src/app/turnState.ts +151 -1
- package/src/auth/authConfiguration.ts +32 -1
- package/src/auth/request.ts +15 -0
- package/src/baseAdapter.ts +18 -0
- package/src/cards/cardFactory.ts +3 -0
- package/src/cards/o365ConnectorCardActionBase.ts +8 -0
- package/src/oauth/oAuthFlow.ts +59 -18
- package/src/oauth/userTokenClient.ts +4 -4
- package/src/state/agentStatePropertyAccesor.ts +1 -1
- package/src/statusCodes.ts +51 -0
- package/src/tokenResponseEventName.ts +3 -0
- package/dist/src/app/oauth/userIdentity.d.ts +0 -43
- package/dist/src/app/oauth/userIdentity.js +0 -54
- package/dist/src/app/oauth/userIdentity.js.map +0 -1
- package/src/app/oauth/userIdentity.ts +0 -78
package/src/app/turnState.ts
CHANGED
|
@@ -20,21 +20,47 @@ const TEMP_SCOPE = 'temp'
|
|
|
20
20
|
|
|
21
21
|
const SSO_SCOPE = 'sso'
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Default interface for conversation state.
|
|
25
|
+
* Extend this interface to define custom conversation state properties.
|
|
26
|
+
*/
|
|
23
27
|
export interface DefaultConversationState {}
|
|
24
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Default interface for user state.
|
|
31
|
+
* Extend this interface to define custom user state properties.
|
|
32
|
+
*/
|
|
25
33
|
export interface DefaultUserState {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Default interface for temporary state that persists only during the current turn.
|
|
37
|
+
* Contains properties used for handling user input, file attachments, and OAuth flows.
|
|
38
|
+
*/
|
|
26
39
|
export interface DefaultTempState {
|
|
40
|
+
/** Current user input text */
|
|
27
41
|
input: string;
|
|
42
|
+
/** Collection of files attached to the current message */
|
|
28
43
|
inputFiles: InputFile[];
|
|
44
|
+
/** Last output text generated by the agent */
|
|
29
45
|
lastOutput: string;
|
|
46
|
+
/** Results from actions performed by the agent */
|
|
30
47
|
actionOutputs: Record<string, string>;
|
|
48
|
+
/** OAuth tokens keyed by provider ID */
|
|
31
49
|
authTokens: { [key: string]: string };
|
|
50
|
+
/** Flag to prevent duplicate token exchanges */
|
|
32
51
|
duplicateTokenExchange?: boolean;
|
|
33
52
|
}
|
|
34
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Default interface for Single Sign-On (SSO) state.
|
|
56
|
+
* Contains properties used for managing OAuth authentication flows.
|
|
57
|
+
*/
|
|
35
58
|
export interface DefaultSSOState {
|
|
59
|
+
/** Whether an OAuth flow has been started */
|
|
36
60
|
flowStarted: boolean;
|
|
61
|
+
/** The user's access token */
|
|
37
62
|
userToken: string;
|
|
63
|
+
/** Expiration timestamp for the current flow */
|
|
38
64
|
flowExpires: number;
|
|
39
65
|
}
|
|
40
66
|
|
|
@@ -67,8 +93,11 @@ export interface DefaultSSOState {
|
|
|
67
93
|
* }
|
|
68
94
|
* }
|
|
69
95
|
* ```
|
|
96
|
+
* @template TConversationState - Type for conversation-scoped state
|
|
97
|
+
* @template TUserState - Type for user-scoped state
|
|
98
|
+
* @template TTempState - Type for temporary state that exists only for the current turn
|
|
99
|
+
* @template TSSOState - Type for Single Sign-On (SSO) state
|
|
70
100
|
*/
|
|
71
|
-
|
|
72
101
|
export class TurnState<
|
|
73
102
|
TConversationState = DefaultConversationState,
|
|
74
103
|
TUserState = DefaultUserState,
|
|
@@ -80,6 +109,12 @@ export class TurnState<
|
|
|
80
109
|
private _loadingPromise?: Promise<boolean>
|
|
81
110
|
private _stateNotLoadedString = 'TurnState hasn\'t been loaded. Call load() first.'
|
|
82
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Gets the conversation-scoped state.
|
|
114
|
+
* This state is shared by all users in the same conversation.
|
|
115
|
+
* @returns The conversation state object
|
|
116
|
+
* @throws Error if state hasn't been loaded
|
|
117
|
+
*/
|
|
83
118
|
public get conversation (): TConversationState {
|
|
84
119
|
const scope = this.getScope(CONVERSATION_SCOPE)
|
|
85
120
|
if (!scope) {
|
|
@@ -88,6 +123,11 @@ export class TurnState<
|
|
|
88
123
|
return scope.value as TConversationState
|
|
89
124
|
}
|
|
90
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Sets the conversation-scoped state.
|
|
128
|
+
* @param value - The new conversation state object
|
|
129
|
+
* @throws Error if state hasn't been loaded
|
|
130
|
+
*/
|
|
91
131
|
public set conversation (value: TConversationState) {
|
|
92
132
|
const scope = this.getScope(CONVERSATION_SCOPE)
|
|
93
133
|
if (!scope) {
|
|
@@ -96,10 +136,20 @@ export class TurnState<
|
|
|
96
136
|
scope.replace(value as Record<string, unknown>)
|
|
97
137
|
}
|
|
98
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Gets whether the state has been loaded from storage
|
|
141
|
+
* @returns True if the state has been loaded, false otherwise
|
|
142
|
+
*/
|
|
99
143
|
public get isLoaded (): boolean {
|
|
100
144
|
return this._isLoaded
|
|
101
145
|
}
|
|
102
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Gets the temporary state for the current turn.
|
|
149
|
+
* This state is not persisted between turns.
|
|
150
|
+
* @returns The temporary state object
|
|
151
|
+
* @throws Error if state hasn't been loaded
|
|
152
|
+
*/
|
|
103
153
|
public get temp (): TTempState {
|
|
104
154
|
const scope = this.getScope(TEMP_SCOPE)
|
|
105
155
|
if (!scope) {
|
|
@@ -108,6 +158,11 @@ export class TurnState<
|
|
|
108
158
|
return scope.value as TTempState
|
|
109
159
|
}
|
|
110
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Sets the temporary state for the current turn.
|
|
163
|
+
* @param value - The new temporary state object
|
|
164
|
+
* @throws Error if state hasn't been loaded
|
|
165
|
+
*/
|
|
111
166
|
public set temp (value: TTempState) {
|
|
112
167
|
const scope = this.getScope(TEMP_SCOPE)
|
|
113
168
|
if (!scope) {
|
|
@@ -116,6 +171,12 @@ export class TurnState<
|
|
|
116
171
|
scope.replace(value as Record<string, unknown>)
|
|
117
172
|
}
|
|
118
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Gets the user-scoped state.
|
|
176
|
+
* This state is unique to each user and persists across conversations.
|
|
177
|
+
* @returns The user state object
|
|
178
|
+
* @throws Error if state hasn't been loaded
|
|
179
|
+
*/
|
|
119
180
|
public get user (): TUserState {
|
|
120
181
|
const scope = this.getScope(USER_SCOPE)
|
|
121
182
|
if (!scope) {
|
|
@@ -124,6 +185,11 @@ export class TurnState<
|
|
|
124
185
|
return scope.value as TUserState
|
|
125
186
|
}
|
|
126
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Sets the user-scoped state.
|
|
190
|
+
* @param value - The new user state object
|
|
191
|
+
* @throws Error if state hasn't been loaded
|
|
192
|
+
*/
|
|
127
193
|
public set user (value: TUserState) {
|
|
128
194
|
const scope = this.getScope(USER_SCOPE)
|
|
129
195
|
if (!scope) {
|
|
@@ -132,6 +198,12 @@ export class TurnState<
|
|
|
132
198
|
scope.replace(value as Record<string, unknown>)
|
|
133
199
|
}
|
|
134
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Gets the Single Sign-On (SSO) state.
|
|
203
|
+
* This state is used to manage OAuth authentication flows.
|
|
204
|
+
* @returns The SSO state object
|
|
205
|
+
* @throws Error if state hasn't been loaded
|
|
206
|
+
*/
|
|
135
207
|
public get sso (): TSSOState {
|
|
136
208
|
const scope = this.getScope(SSO_SCOPE)
|
|
137
209
|
if (!scope) {
|
|
@@ -140,6 +212,11 @@ export class TurnState<
|
|
|
140
212
|
return scope.value as TSSOState
|
|
141
213
|
}
|
|
142
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Sets the Single Sign-On (SSO) state.
|
|
217
|
+
* @param value - The new SSO state object
|
|
218
|
+
* @throws Error if state hasn't been loaded
|
|
219
|
+
*/
|
|
143
220
|
public set sso (value: TSSOState) {
|
|
144
221
|
const scope = this.getScope(SSO_SCOPE)
|
|
145
222
|
if (!scope) {
|
|
@@ -148,6 +225,11 @@ export class TurnState<
|
|
|
148
225
|
scope.replace(value as Record<string, unknown>)
|
|
149
226
|
}
|
|
150
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Marks the conversation state for deletion.
|
|
230
|
+
* The state will be deleted from storage on the next call to save().
|
|
231
|
+
* @throws Error if state hasn't been loaded
|
|
232
|
+
*/
|
|
151
233
|
public deleteConversationState (): void {
|
|
152
234
|
const scope = this.getScope(CONVERSATION_SCOPE)
|
|
153
235
|
if (!scope) {
|
|
@@ -156,6 +238,11 @@ export class TurnState<
|
|
|
156
238
|
scope.delete()
|
|
157
239
|
}
|
|
158
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Marks the temporary state for deletion.
|
|
243
|
+
* Since temporary state is not persisted, this just clears the in-memory object.
|
|
244
|
+
* @throws Error if state hasn't been loaded
|
|
245
|
+
*/
|
|
159
246
|
public deleteTempState (): void {
|
|
160
247
|
const scope = this.getScope(TEMP_SCOPE)
|
|
161
248
|
if (!scope) {
|
|
@@ -164,6 +251,11 @@ export class TurnState<
|
|
|
164
251
|
scope.delete()
|
|
165
252
|
}
|
|
166
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Marks the user state for deletion.
|
|
256
|
+
* The state will be deleted from storage on the next call to save().
|
|
257
|
+
* @throws Error if state hasn't been loaded
|
|
258
|
+
*/
|
|
167
259
|
public deleteUserState (): void {
|
|
168
260
|
const scope = this.getScope(USER_SCOPE)
|
|
169
261
|
if (!scope) {
|
|
@@ -172,10 +264,20 @@ export class TurnState<
|
|
|
172
264
|
scope.delete()
|
|
173
265
|
}
|
|
174
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Gets a specific state scope by name.
|
|
269
|
+
* @param scope - The name of the scope to retrieve
|
|
270
|
+
* @returns The state entry for the scope, or undefined if not found
|
|
271
|
+
*/
|
|
175
272
|
public getScope (scope: string): TurnStateEntry | undefined {
|
|
176
273
|
return this._scopes[scope]
|
|
177
274
|
}
|
|
178
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Deletes a value from state by dot-notation path.
|
|
278
|
+
* Format: "scope.property" or just "property" (defaults to temp scope)
|
|
279
|
+
* @param path - The path to the value to delete
|
|
280
|
+
*/
|
|
179
281
|
public deleteValue (path: string): void {
|
|
180
282
|
const { scope, name } = this.getScopeAndName(path)
|
|
181
283
|
if (Object.prototype.hasOwnProperty.call(scope.value, name)) {
|
|
@@ -183,21 +285,47 @@ export class TurnState<
|
|
|
183
285
|
}
|
|
184
286
|
}
|
|
185
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Checks if a value exists in state by dot-notation path.
|
|
290
|
+
* Format: "scope.property" or just "property" (defaults to temp scope)
|
|
291
|
+
* @param path - The path to check
|
|
292
|
+
* @returns True if the value exists, false otherwise
|
|
293
|
+
*/
|
|
186
294
|
public hasValue (path: string): boolean {
|
|
187
295
|
const { scope, name } = this.getScopeAndName(path)
|
|
188
296
|
return Object.prototype.hasOwnProperty.call(scope.value, name)
|
|
189
297
|
}
|
|
190
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Gets a value from state by dot-notation path.
|
|
301
|
+
* Format: "scope.property" or just "property" (defaults to temp scope)
|
|
302
|
+
* @template TValue - The type of the value to retrieve
|
|
303
|
+
* @param path - The path to the value
|
|
304
|
+
* @returns The value at the specified path
|
|
305
|
+
*/
|
|
191
306
|
public getValue<TValue = unknown>(path: string): TValue {
|
|
192
307
|
const { scope, name } = this.getScopeAndName(path)
|
|
193
308
|
return scope.value[name] as TValue
|
|
194
309
|
}
|
|
195
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Sets a value in state by dot-notation path.
|
|
313
|
+
* Format: "scope.property" or just "property" (defaults to temp scope)
|
|
314
|
+
* @param path - The path to set
|
|
315
|
+
* @param value - The value to set
|
|
316
|
+
*/
|
|
196
317
|
public setValue (path: string, value: unknown): void {
|
|
197
318
|
const { scope, name } = this.getScopeAndName(path)
|
|
198
319
|
scope.value[name] = value
|
|
199
320
|
}
|
|
200
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Loads state from storage into memory.
|
|
324
|
+
* @param context - The turn context
|
|
325
|
+
* @param storage - Optional storage provider (if not provided, state will be in-memory only)
|
|
326
|
+
* @param force - If true, forces a reload from storage even if state is already loaded
|
|
327
|
+
* @returns Promise that resolves to true if state was loaded, false if it was already loaded
|
|
328
|
+
*/
|
|
201
329
|
public load (context: TurnContext, storage?: Storage, force: boolean = false): Promise<boolean> {
|
|
202
330
|
if (this._isLoaded && !force) {
|
|
203
331
|
return Promise.resolve(false)
|
|
@@ -242,6 +370,14 @@ export class TurnState<
|
|
|
242
370
|
return this._loadingPromise
|
|
243
371
|
}
|
|
244
372
|
|
|
373
|
+
/**
|
|
374
|
+
* Saves state changes to storage.
|
|
375
|
+
* Only changed scopes will be persisted.
|
|
376
|
+
* @param context - The turn context
|
|
377
|
+
* @param storage - Optional storage provider (if not provided, state changes won't be persisted)
|
|
378
|
+
* @returns Promise that resolves when the save operation is complete
|
|
379
|
+
* @throws Error if state hasn't been loaded
|
|
380
|
+
*/
|
|
245
381
|
public async save (context: TurnContext, storage?: Storage): Promise<void> {
|
|
246
382
|
if (!this._isLoaded && this._loadingPromise) {
|
|
247
383
|
await this._loadingPromise
|
|
@@ -291,6 +427,13 @@ export class TurnState<
|
|
|
291
427
|
}
|
|
292
428
|
}
|
|
293
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Computes the storage keys for each scope based on the turn context.
|
|
432
|
+
* Override this method in derived classes to add or modify storage keys.
|
|
433
|
+
* @param context - The turn context
|
|
434
|
+
* @returns Promise that resolves to a dictionary of scope names to storage keys
|
|
435
|
+
* @protected
|
|
436
|
+
*/
|
|
294
437
|
protected onComputeStorageKeys (context: TurnContext): Promise<Record<string, string>> {
|
|
295
438
|
const activity = context.activity
|
|
296
439
|
const channelId = activity?.channelId
|
|
@@ -321,6 +464,13 @@ export class TurnState<
|
|
|
321
464
|
return Promise.resolve(keys)
|
|
322
465
|
}
|
|
323
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Parses a dot-notation path into scope and property name.
|
|
469
|
+
* If no scope is specified, defaults to the temp scope.
|
|
470
|
+
* @param path - The path to parse (format: "scope.property" or just "property")
|
|
471
|
+
* @returns Object containing the scope entry and property name
|
|
472
|
+
* @private
|
|
473
|
+
*/
|
|
324
474
|
private getScopeAndName (path: string): { scope: TurnStateEntry; name: string } {
|
|
325
475
|
const parts = path.split('.')
|
|
326
476
|
if (parts.length > 2) {
|
|
@@ -7,13 +7,44 @@
|
|
|
7
7
|
* Represents the authentication configuration.
|
|
8
8
|
*/
|
|
9
9
|
export interface AuthConfiguration {
|
|
10
|
+
/**
|
|
11
|
+
* The tenant ID for the authentication configuration.
|
|
12
|
+
*/
|
|
10
13
|
tenantId?: string
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The client ID for the authentication configuration. Required in production.
|
|
17
|
+
*/
|
|
11
18
|
clientId?: string
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The client secret for the authentication configuration.
|
|
22
|
+
*/
|
|
12
23
|
clientSecret?: string
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The path to the certificate PEM file.
|
|
27
|
+
*/
|
|
13
28
|
certPemFile?: string
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The path to the certificate key file.
|
|
32
|
+
*/
|
|
14
33
|
certKeyFile?: string
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A list of valid issuers for the authentication configuration.
|
|
37
|
+
*/
|
|
15
38
|
issuers: string[]
|
|
16
|
-
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The connection name for the authentication configuration.
|
|
42
|
+
*/
|
|
43
|
+
connectionName?: string
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The FIC (First-Party Integration Channel) client ID.
|
|
47
|
+
*/
|
|
17
48
|
FICClientId?: string
|
|
18
49
|
}
|
|
19
50
|
|
package/src/auth/request.ts
CHANGED
|
@@ -13,8 +13,23 @@ export interface Request<
|
|
|
13
13
|
Body extends Record<string, unknown> = Record<string, unknown>,
|
|
14
14
|
Headers extends Record<string, string[] | string | undefined> = Record<string, string[] | string | undefined>
|
|
15
15
|
> {
|
|
16
|
+
/**
|
|
17
|
+
* The body of the HTTP request, containing parsed data.
|
|
18
|
+
*/
|
|
16
19
|
body?: Body
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The headers of the HTTP request, represented as key-value pairs.
|
|
23
|
+
*/
|
|
17
24
|
headers: Headers
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The HTTP method of the request (e.g., GET, POST, PUT, DELETE).
|
|
28
|
+
*/
|
|
18
29
|
method?: string
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The user information extracted from a JWT payload, if available.
|
|
33
|
+
*/
|
|
19
34
|
user?: JwtPayload
|
|
20
35
|
}
|
package/src/baseAdapter.ts
CHANGED
|
@@ -20,6 +20,9 @@ const logger = debug('agents:base-adapter')
|
|
|
20
20
|
* Base class for all adapters, providing middleware and error handling capabilities.
|
|
21
21
|
*/
|
|
22
22
|
export abstract class BaseAdapter {
|
|
23
|
+
/**
|
|
24
|
+
* The middleware set used to process the pipeline of middleware handlers.
|
|
25
|
+
*/
|
|
23
26
|
protected middleware: MiddlewareSet = new MiddlewareSet()
|
|
24
27
|
|
|
25
28
|
private turnError: (context: TurnContext, error: Error) => Promise<void> = async (context: TurnContext, error: Error) => {
|
|
@@ -42,7 +45,14 @@ export abstract class BaseAdapter {
|
|
|
42
45
|
readonly ConnectorClientKey = Symbol('ConnectorClient')
|
|
43
46
|
readonly OAuthScopeKey = Symbol('OAuthScope')
|
|
44
47
|
|
|
48
|
+
/**
|
|
49
|
+
* The authentication provider used for token management.
|
|
50
|
+
*/
|
|
45
51
|
authProvider: AuthProvider = new MsalTokenProvider()
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The authentication configuration for the adapter.
|
|
55
|
+
*/
|
|
46
56
|
authConfig: AuthConfiguration = { issuers: [] }
|
|
47
57
|
|
|
48
58
|
/**
|
|
@@ -103,10 +113,18 @@ export abstract class BaseAdapter {
|
|
|
103
113
|
*/
|
|
104
114
|
abstract getAttachment (attachmentId: string, viewId: string): Promise<NodeJS.ReadableStream>
|
|
105
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Gets the error handler for the adapter.
|
|
118
|
+
* @returns The current error handler function.
|
|
119
|
+
*/
|
|
106
120
|
get onTurnError (): (context: TurnContext, error: Error) => Promise<void> {
|
|
107
121
|
return this.turnError
|
|
108
122
|
}
|
|
109
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Sets the error handler for the adapter.
|
|
126
|
+
* @param value - The error handler function to set.
|
|
127
|
+
*/
|
|
110
128
|
set onTurnError (value: (context: TurnContext, error: Error) => Promise<void>) {
|
|
111
129
|
this.turnError = value
|
|
112
130
|
}
|
package/src/cards/cardFactory.ts
CHANGED
|
@@ -21,6 +21,9 @@ import { SigninCard } from './signinCard'
|
|
|
21
21
|
* Factory class for creating various types of cards.
|
|
22
22
|
*/
|
|
23
23
|
export class CardFactory {
|
|
24
|
+
/**
|
|
25
|
+
* The content types supported by the card factory.
|
|
26
|
+
*/
|
|
24
27
|
static contentTypes: any = {
|
|
25
28
|
adaptiveCard: 'application/vnd.microsoft.card.adaptive',
|
|
26
29
|
animationCard: 'application/vnd.microsoft.card.animation',
|
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Defines the possible types of actions in an O365 connector card.
|
|
8
|
+
*
|
|
9
|
+
* - `ViewAction`: Represents an action to view content.
|
|
10
|
+
* - `OpenUri`: Represents an action to open a URI.
|
|
11
|
+
* - `HttpPOST`: Represents an action to make an HTTP POST request.
|
|
12
|
+
* - `ActionCard`: Represents an action that opens a card with additional actions or inputs.
|
|
13
|
+
*/
|
|
6
14
|
export type O365ConnectorCardActionType = 'ViewAction' | 'OpenUri' | 'HttpPOST' | 'ActionCard'
|
|
7
15
|
|
|
8
16
|
/**
|
package/src/oauth/oAuthFlow.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import { debug } from './../logger'
|
|
4
|
-
import {
|
|
4
|
+
import { ActivityTypes, Attachment } from '@microsoft/agents-activity'
|
|
5
5
|
import {
|
|
6
6
|
CardFactory,
|
|
7
7
|
AgentStatePropertyAccessor,
|
|
8
8
|
UserState,
|
|
9
9
|
TurnContext,
|
|
10
10
|
MessageFactory,
|
|
11
|
-
SigningResource,
|
|
12
11
|
TokenExchangeRequest,
|
|
13
12
|
UserTokenClient
|
|
14
13
|
} from '../'
|
|
@@ -25,28 +24,72 @@ interface TokenVerifyState {
|
|
|
25
24
|
state: string
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
28
|
-
* Manages the OAuth flow
|
|
27
|
+
* Manages the OAuth flow
|
|
29
28
|
*/
|
|
30
29
|
export class OAuthFlow {
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The user token client used for managing user tokens.
|
|
32
|
+
*/
|
|
33
|
+
userTokenClient: UserTokenClient
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The current state of the OAuth flow.
|
|
37
|
+
*/
|
|
32
38
|
state: FlowState | null
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The accessor for managing the flow state in user state.
|
|
42
|
+
*/
|
|
33
43
|
flowStateAccessor: AgentStatePropertyAccessor<FlowState | null>
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The ID of the token exchange request, used to deduplicate requests.
|
|
47
|
+
*/
|
|
34
48
|
tokenExchangeId: string | null = null
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The name of the OAuth connection.
|
|
52
|
+
*/
|
|
35
53
|
absOauthConnectionName: string
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The title of the OAuth card.
|
|
57
|
+
*/
|
|
58
|
+
cardTitle: string = 'Sign in'
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The text of the OAuth card.
|
|
62
|
+
*/
|
|
63
|
+
cardText: string = 'login'
|
|
64
|
+
|
|
36
65
|
/**
|
|
37
66
|
* Creates a new instance of OAuthFlow.
|
|
38
67
|
* @param userState The user state.
|
|
39
68
|
*/
|
|
40
|
-
constructor (userState: UserState, absOauthConnectionName: string, tokenClient?: UserTokenClient) {
|
|
41
|
-
this.state =
|
|
69
|
+
constructor (userState: UserState, absOauthConnectionName: string, tokenClient?: UserTokenClient, cardTitle?: string, cardText?: string) {
|
|
70
|
+
this.state = new FlowState()
|
|
42
71
|
this.flowStateAccessor = userState.createProperty('flowState')
|
|
43
72
|
this.absOauthConnectionName = absOauthConnectionName
|
|
44
|
-
this.userTokenClient = tokenClient
|
|
73
|
+
this.userTokenClient = tokenClient ?? null!
|
|
74
|
+
this.cardTitle = cardTitle ?? this.cardTitle
|
|
75
|
+
this.cardText = cardText ?? this.cardText
|
|
45
76
|
}
|
|
46
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Retrieves the user token from the user token service.
|
|
80
|
+
* @param context The turn context containing the activity information.
|
|
81
|
+
* @returns A promise that resolves to the user token response.
|
|
82
|
+
* @throws Will throw an error if the channelId or from properties are not set in the activity.
|
|
83
|
+
*/
|
|
47
84
|
public async getUserToken (context: TurnContext): Promise<TokenResponse> {
|
|
48
85
|
await this.initializeTokenClient(context)
|
|
49
|
-
|
|
86
|
+
logger.info('Get token from user token service')
|
|
87
|
+
const activity = context.activity
|
|
88
|
+
if (activity.channelId && activity.from && activity.from.id) {
|
|
89
|
+
return await this.userTokenClient.getUserToken(this.absOauthConnectionName, activity.channelId, activity.from.id)
|
|
90
|
+
} else {
|
|
91
|
+
throw new Error('UserTokenService requires channelId and from to be set')
|
|
92
|
+
}
|
|
50
93
|
}
|
|
51
94
|
|
|
52
95
|
/**
|
|
@@ -55,16 +98,14 @@ export class OAuthFlow {
|
|
|
55
98
|
* @returns A promise that resolves to the user token.
|
|
56
99
|
*/
|
|
57
100
|
public async beginFlow (context: TurnContext): Promise<TokenResponse> {
|
|
58
|
-
logger.info('Starting OAuth flow')
|
|
59
101
|
this.state = await this.getUserState(context)
|
|
60
|
-
|
|
61
|
-
const authConfig = context.adapter.authConfig
|
|
62
102
|
if (this.absOauthConnectionName === '') {
|
|
63
|
-
throw new Error('connectionName is not set
|
|
103
|
+
throw new Error('connectionName is not set')
|
|
64
104
|
}
|
|
105
|
+
logger.info('Starting OAuth flow for connectionName:', this.absOauthConnectionName)
|
|
65
106
|
await this.initializeTokenClient(context)
|
|
66
107
|
|
|
67
|
-
const tokenResponse = await this.userTokenClient
|
|
108
|
+
const tokenResponse = await this.userTokenClient.getUserToken(this.absOauthConnectionName, context.activity.channelId!, context.activity.from?.id!)
|
|
68
109
|
if (tokenResponse?.status === TokenRequestStatus.Success) {
|
|
69
110
|
this.state.flowStarted = false
|
|
70
111
|
this.state.flowExpires = 0
|
|
@@ -73,10 +114,10 @@ export class OAuthFlow {
|
|
|
73
114
|
return tokenResponse
|
|
74
115
|
}
|
|
75
116
|
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
await context.sendActivity(
|
|
117
|
+
const authConfig = context.adapter.authConfig
|
|
118
|
+
const signingResource = await this.userTokenClient.getSignInResource(authConfig.clientId!, this.absOauthConnectionName, context.activity.getConversationReference(), context.activity.relatesTo)
|
|
119
|
+
const oCard: Attachment = CardFactory.oauthCard(this.absOauthConnectionName, this.cardTitle, this.cardText, signingResource)
|
|
120
|
+
await context.sendActivity(MessageFactory.attachment(oCard))
|
|
80
121
|
this.state.flowStarted = true
|
|
81
122
|
this.state.flowExpires = Date.now() + 30000
|
|
82
123
|
await this.flowStateAccessor.set(context, this.state)
|
|
@@ -166,7 +207,7 @@ export class OAuthFlow {
|
|
|
166
207
|
}
|
|
167
208
|
|
|
168
209
|
private async initializeTokenClient (context: TurnContext) {
|
|
169
|
-
if (this.userTokenClient === undefined) {
|
|
210
|
+
if (this.userTokenClient === undefined || this.userTokenClient === null) {
|
|
170
211
|
const scope = 'https://api.botframework.com'
|
|
171
212
|
const accessToken = await context.adapter.authProvider.getAccessToken(context.adapter.authConfig, scope)
|
|
172
213
|
this.userTokenClient = new UserTokenClient(accessToken)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import axios, { AxiosInstance } from 'axios'
|
|
5
5
|
import { SigningResource } from './signingResource'
|
|
6
|
-
import {
|
|
6
|
+
import { ConversationReference } from '@microsoft/agents-activity'
|
|
7
7
|
import { debug } from '../logger'
|
|
8
8
|
import { TokenExchangeRequest } from './tokenExchangeRequest'
|
|
9
9
|
import { normalizeTokenExchangeState } from '../activityWireCompat'
|
|
@@ -86,12 +86,12 @@ export class UserTokenClient {
|
|
|
86
86
|
* @param activity The activity.
|
|
87
87
|
* @returns A promise that resolves to the signing resource.
|
|
88
88
|
*/
|
|
89
|
-
async getSignInResource (appId: string, cnxName: string,
|
|
89
|
+
async getSignInResource (appId: string, cnxName: string, conversationReference: ConversationReference, relatesTo?: ConversationReference) : Promise<SigningResource> {
|
|
90
90
|
try {
|
|
91
91
|
const tokenExchangeState = {
|
|
92
92
|
connectionName: cnxName,
|
|
93
|
-
conversation:
|
|
94
|
-
relatesTo
|
|
93
|
+
conversation: conversationReference,
|
|
94
|
+
relatesTo,
|
|
95
95
|
msAppId: appId
|
|
96
96
|
}
|
|
97
97
|
const tokenExchangeStateNormalized = normalizeTokenExchangeState(tokenExchangeState)
|
|
@@ -84,7 +84,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
84
84
|
* - Type checking for properties (when using TypeScript)
|
|
85
85
|
* - Ensuring properties exist before access
|
|
86
86
|
*
|
|
87
|
-
* Property accessors are created through the AgentState.createProperty
|
|
87
|
+
* Property accessors are created through the {@link AgentState.createProperty} method:
|
|
88
88
|
*
|
|
89
89
|
* ```typescript
|
|
90
90
|
* // Create a property accessor for a user profile
|
package/src/statusCodes.ts
CHANGED
|
@@ -4,17 +4,68 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export enum StatusCodes {
|
|
7
|
+
/**
|
|
8
|
+
* The request has succeeded.
|
|
9
|
+
*/
|
|
7
10
|
OK = 200,
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The request has been fulfilled and resulted in a new resource being created.
|
|
14
|
+
*/
|
|
8
15
|
CREATED = 201,
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Indicates multiple options for the resource that the client may follow.
|
|
19
|
+
*/
|
|
9
20
|
MULTIPLE_CHOICES = 300,
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The server cannot or will not process the request due to a client error.
|
|
24
|
+
*/
|
|
10
25
|
BAD_REQUEST = 400,
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The request requires user authentication.
|
|
29
|
+
*/
|
|
11
30
|
UNAUTHORIZED = 401,
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The requested resource could not be found.
|
|
34
|
+
*/
|
|
12
35
|
NOT_FOUND = 404,
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The request method is not allowed for the requested resource.
|
|
39
|
+
*/
|
|
13
40
|
METHOD_NOT_ALLOWED = 405,
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The request could not be completed due to a conflict with the current state of the resource.
|
|
44
|
+
*/
|
|
14
45
|
CONFLICT = 409,
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The server does not meet one of the preconditions specified by the client.
|
|
49
|
+
*/
|
|
15
50
|
PRECONDITION_FAILED = 412,
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The client should switch to a different protocol.
|
|
54
|
+
*/
|
|
16
55
|
UPGRADE_REQUIRED = 426,
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The server encountered an unexpected condition that prevented it from fulfilling the request.
|
|
59
|
+
*/
|
|
17
60
|
INTERNAL_SERVER_ERROR = 500,
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The server does not support the functionality required to fulfill the request.
|
|
64
|
+
*/
|
|
18
65
|
NOT_IMPLEMENTED = 501,
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The server received an invalid response from the upstream server.
|
|
69
|
+
*/
|
|
19
70
|
BAD_GATEWAY = 502,
|
|
20
71
|
}
|