@microsoft/agents-hosting 1.1.0-alpha.5 → 1.1.0-alpha.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +10 -6
- package/dist/src/activityWireCompat.d.ts +1 -1
- package/dist/src/activityWireCompat.js +11 -3
- package/dist/src/activityWireCompat.js.map +1 -1
- package/dist/src/agent-client/agentClient.js +7 -3
- package/dist/src/agent-client/agentClient.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.js +6 -2
- package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
- package/dist/src/app/agentApplication.d.ts +28 -13
- package/dist/src/app/agentApplication.js +93 -82
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationBuilder.d.ts +2 -2
- package/dist/src/app/agentApplicationBuilder.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +9 -2
- package/dist/src/app/appRoute.d.ts +7 -0
- package/dist/src/app/{authorization.d.ts → auth/authorization.d.ts} +33 -139
- package/dist/src/app/auth/authorization.js +188 -0
- package/dist/src/app/auth/authorization.js.map +1 -0
- package/dist/src/app/auth/authorizationManager.d.ts +71 -0
- package/dist/src/app/auth/authorizationManager.js +170 -0
- package/dist/src/app/auth/authorizationManager.js.map +1 -0
- package/dist/src/app/auth/handlerStorage.d.ts +36 -0
- package/dist/src/app/auth/handlerStorage.js +62 -0
- package/dist/src/app/auth/handlerStorage.js.map +1 -0
- package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +93 -0
- package/dist/src/app/auth/handlers/agenticAuthorization.js +134 -0
- package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -0
- package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +222 -0
- package/dist/src/app/auth/handlers/azureBotAuthorization.js +428 -0
- package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -0
- package/dist/src/app/auth/handlers/index.d.ts +2 -0
- package/dist/src/app/auth/handlers/index.js +19 -0
- package/dist/src/app/auth/handlers/index.js.map +1 -0
- package/dist/src/app/auth/index.d.ts +2 -0
- package/dist/src/app/auth/index.js +19 -0
- package/dist/src/app/auth/index.js.map +1 -0
- package/dist/src/app/auth/types.d.ts +104 -0
- package/dist/src/app/auth/types.js +24 -0
- package/dist/src/app/auth/types.js.map +1 -0
- package/dist/src/app/index.d.ts +2 -3
- package/dist/src/app/index.js +2 -3
- package/dist/src/app/index.js.map +1 -1
- package/dist/src/app/routeList.d.ts +1 -1
- package/dist/src/app/routeList.js +22 -5
- package/dist/src/app/routeList.js.map +1 -1
- package/dist/src/app/streaming/streamingResponse.js +2 -1
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/auth/MemoryCache.d.ts +16 -0
- package/dist/src/auth/MemoryCache.js +58 -0
- package/dist/src/auth/MemoryCache.js.map +1 -0
- package/dist/src/auth/authConfiguration.d.ts +44 -2
- package/dist/src/auth/authConfiguration.js +209 -53
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/authConstants.d.ts +11 -0
- package/dist/src/auth/authConstants.js +15 -0
- package/dist/src/auth/authConstants.js.map +1 -0
- package/dist/src/auth/authProvider.d.ts +23 -0
- package/dist/src/auth/connections.d.ts +41 -0
- package/dist/src/auth/connections.js +7 -0
- package/dist/src/auth/connections.js.map +1 -0
- package/dist/src/auth/index.d.ts +2 -0
- package/dist/src/auth/index.js +2 -0
- package/dist/src/auth/index.js.map +1 -1
- package/dist/src/auth/jwt-middleware.js +31 -18
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msalConnectionManager.d.ts +64 -0
- package/dist/src/auth/msalConnectionManager.js +148 -0
- package/dist/src/auth/msalConnectionManager.js.map +1 -0
- package/dist/src/auth/msalTokenProvider.d.ts +31 -0
- package/dist/src/auth/msalTokenProvider.js +167 -16
- package/dist/src/auth/msalTokenProvider.js.map +1 -1
- package/dist/src/baseAdapter.d.ts +10 -25
- package/dist/src/baseAdapter.js +2 -15
- package/dist/src/baseAdapter.js.map +1 -1
- package/dist/src/cloudAdapter.d.ts +40 -23
- package/dist/src/cloudAdapter.js +128 -60
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/connector-client/connectorClient.d.ts +15 -0
- package/dist/src/connector-client/connectorClient.js +49 -15
- package/dist/src/connector-client/connectorClient.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/oauth/customUserTokenAPI.d.ts +1 -0
- package/dist/src/oauth/customUserTokenAPI.js +11 -0
- package/dist/src/oauth/customUserTokenAPI.js.map +1 -0
- package/dist/src/oauth/index.d.ts +0 -1
- package/dist/src/oauth/index.js +0 -1
- package/dist/src/oauth/index.js.map +1 -1
- package/dist/src/oauth/userTokenClient.d.ts +30 -13
- package/dist/src/oauth/userTokenClient.js +60 -26
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/oauth/userTokenClient.types.d.ts +19 -6
- package/dist/src/turnContext.d.ts +7 -1
- package/dist/src/turnContext.js +11 -4
- package/dist/src/turnContext.js.map +1 -1
- package/package.json +10 -6
- package/src/activityWireCompat.ts +12 -4
- package/src/agent-client/agentClient.ts +9 -3
- package/src/agent-client/agentResponseHandler.ts +5 -2
- package/src/app/agentApplication.ts +98 -77
- package/src/app/agentApplicationBuilder.ts +2 -2
- package/src/app/agentApplicationOptions.ts +10 -2
- package/src/app/appRoute.ts +8 -0
- package/src/app/auth/authorization.ts +252 -0
- package/src/app/auth/authorizationManager.ts +213 -0
- package/src/app/auth/handlerStorage.ts +61 -0
- package/src/app/auth/handlers/agenticAuthorization.ts +182 -0
- package/src/app/auth/handlers/azureBotAuthorization.ts +599 -0
- package/src/app/auth/handlers/index.ts +2 -0
- package/src/app/auth/index.ts +2 -0
- package/src/app/auth/types.ts +111 -0
- package/src/app/index.ts +2 -3
- package/src/app/routeList.ts +24 -5
- package/src/app/streaming/streamingResponse.ts +2 -1
- package/src/auth/MemoryCache.ts +59 -0
- package/src/auth/authConfiguration.ts +245 -52
- package/src/auth/authConstants.ts +11 -0
- package/src/auth/authProvider.ts +31 -0
- package/src/auth/connections.ts +47 -0
- package/src/auth/index.ts +2 -0
- package/src/auth/jwt-middleware.ts +38 -21
- package/src/auth/msalConnectionManager.ts +175 -0
- package/src/auth/msalTokenProvider.ts +209 -9
- package/src/baseAdapter.ts +10 -29
- package/src/cloudAdapter.ts +189 -71
- package/src/connector-client/connectorClient.ts +59 -15
- package/src/index.ts +0 -1
- package/src/oauth/customUserTokenAPI.ts +5 -0
- package/src/oauth/index.ts +0 -1
- package/src/oauth/userTokenClient.ts +74 -22
- package/src/oauth/userTokenClient.types.ts +20 -8
- package/src/turnContext.ts +16 -5
- package/dist/src/app/authorization.js +0 -387
- package/dist/src/app/authorization.js.map +0 -1
- package/dist/src/claimsIdentity.d.ts +0 -35
- package/dist/src/claimsIdentity.js +0 -43
- package/dist/src/claimsIdentity.js.map +0 -1
- package/dist/src/oauth/oAuthFlow.d.ts +0 -119
- package/dist/src/oauth/oAuthFlow.js +0 -316
- package/dist/src/oauth/oAuthFlow.js.map +0 -1
- package/src/app/authorization.ts +0 -432
- package/src/claimsIdentity.ts +0 -47
- package/src/oauth/oAuthFlow.ts +0 -378
package/src/oauth/oAuthFlow.ts
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License.
|
|
3
|
-
import { debug } from '@microsoft/agents-activity/logger'
|
|
4
|
-
import { Activity, ActivityTypes, Attachment } from '@microsoft/agents-activity'
|
|
5
|
-
import {
|
|
6
|
-
CardFactory,
|
|
7
|
-
TurnContext,
|
|
8
|
-
Storage,
|
|
9
|
-
MessageFactory
|
|
10
|
-
} from '../'
|
|
11
|
-
import { UserTokenClient } from './userTokenClient'
|
|
12
|
-
import { TokenExchangeRequest, TokenResponse } from './userTokenClient.types'
|
|
13
|
-
import jwt, { JwtPayload } from 'jsonwebtoken'
|
|
14
|
-
|
|
15
|
-
const logger = debug('agents:oauth-flow')
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Represents the state of the OAuth flow.
|
|
19
|
-
* @interface FlowState
|
|
20
|
-
*/
|
|
21
|
-
export interface FlowState {
|
|
22
|
-
/** Indicates whether the OAuth flow has been started */
|
|
23
|
-
flowStarted: boolean | undefined,
|
|
24
|
-
/** Timestamp when the OAuth flow expires (in milliseconds since epoch) */
|
|
25
|
-
flowExpires: number | undefined,
|
|
26
|
-
/** The absolute OAuth connection name used for the flow, null if not set */
|
|
27
|
-
absOauthConnectionName: string
|
|
28
|
-
/** Optional activity to continue the flow with, used for multi-turn scenarios */
|
|
29
|
-
continuationActivity?: Activity | null
|
|
30
|
-
|
|
31
|
-
eTag?: string // Optional ETag for optimistic concurrency control
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface TokenVerifyState {
|
|
35
|
-
state: string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface CachedToken {
|
|
39
|
-
token: TokenResponse
|
|
40
|
-
expiresAt: number
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Manages the OAuth flow
|
|
45
|
-
*/
|
|
46
|
-
export class OAuthFlow {
|
|
47
|
-
/**
|
|
48
|
-
* The user token client used for managing user tokens.
|
|
49
|
-
*/
|
|
50
|
-
userTokenClient: UserTokenClient
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* The current state of the OAuth flow.
|
|
54
|
-
*/
|
|
55
|
-
state: FlowState
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* The ID of the token exchange request, used to deduplicate requests.
|
|
59
|
-
*/
|
|
60
|
-
tokenExchangeId: string | null = null
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* In-memory cache for tokens with expiration.
|
|
64
|
-
*/
|
|
65
|
-
private tokenCache: Map<string, CachedToken> = new Map()
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* The name of the OAuth connection.
|
|
69
|
-
*/
|
|
70
|
-
absOauthConnectionName: string
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* The title of the OAuth card.
|
|
74
|
-
*/
|
|
75
|
-
cardTitle: string = 'Sign in'
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* The text of the OAuth card.
|
|
79
|
-
*/
|
|
80
|
-
cardText: string = 'login'
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Creates a new instance of OAuthFlow.
|
|
84
|
-
* @param storage The storage provider for persisting flow state.
|
|
85
|
-
* @param absOauthConnectionName The absolute OAuth connection name.
|
|
86
|
-
* @param tokenClient Optional user token client. If not provided, will be initialized automatically.
|
|
87
|
-
* @param cardTitle Optional title for the OAuth card. Defaults to 'Sign in'.
|
|
88
|
-
* @param cardText Optional text for the OAuth card. Defaults to 'login'.
|
|
89
|
-
*/
|
|
90
|
-
constructor (private storage: Storage, absOauthConnectionName: string, tokenClient: UserTokenClient, cardTitle?: string, cardText?: string) {
|
|
91
|
-
this.state = { flowStarted: undefined, flowExpires: undefined, absOauthConnectionName }
|
|
92
|
-
this.absOauthConnectionName = absOauthConnectionName
|
|
93
|
-
this.userTokenClient = tokenClient
|
|
94
|
-
this.cardTitle = cardTitle ?? this.cardTitle
|
|
95
|
-
this.cardText = cardText ?? this.cardText
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Retrieves the user token from the user token service with in-memory caching for 10 minutes.
|
|
100
|
-
* @param context The turn context containing the activity information.
|
|
101
|
-
* @returns A promise that resolves to the user token response.
|
|
102
|
-
* @throws Will throw an error if the channelId or from properties are not set in the activity.
|
|
103
|
-
*/
|
|
104
|
-
public async getUserToken (context: TurnContext): Promise<TokenResponse> {
|
|
105
|
-
const activity = context.activity
|
|
106
|
-
|
|
107
|
-
if (!activity.channelId || !activity.from || !activity.from.id) {
|
|
108
|
-
throw new Error('UserTokenService requires channelId and from to be set')
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const cacheKey = this.getCacheKey(context)
|
|
112
|
-
|
|
113
|
-
const cachedEntry = this.tokenCache.get(cacheKey)
|
|
114
|
-
if (cachedEntry && Date.now() < cachedEntry.expiresAt) {
|
|
115
|
-
logger.info(`Returning cached token for user with cache key: ${cacheKey}`)
|
|
116
|
-
return cachedEntry.token
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
logger.info('Get token from user token service')
|
|
120
|
-
await this.refreshToken(context)
|
|
121
|
-
const tokenResponse = await this.userTokenClient.getUserToken(this.absOauthConnectionName, activity.channelId, activity.from.id)
|
|
122
|
-
|
|
123
|
-
// Cache the token if it's valid (has a token value)
|
|
124
|
-
if (tokenResponse && tokenResponse.token) {
|
|
125
|
-
const cacheExpiry = Date.now() + (10 * 60 * 1000) // 10 minutes from now
|
|
126
|
-
this.tokenCache.set(cacheKey, {
|
|
127
|
-
token: tokenResponse,
|
|
128
|
-
expiresAt: cacheExpiry
|
|
129
|
-
})
|
|
130
|
-
logger.info('Token cached for 10 minutes')
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return tokenResponse
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Begins the OAuth flow.
|
|
138
|
-
* @param context The turn context.
|
|
139
|
-
* @returns A promise that resolves to the user token if available, or undefined if OAuth flow needs to be started.
|
|
140
|
-
*/
|
|
141
|
-
public async beginFlow (context: TurnContext): Promise<TokenResponse | undefined> {
|
|
142
|
-
this.state = await this.getFlowState(context)
|
|
143
|
-
if (this.absOauthConnectionName === '') {
|
|
144
|
-
throw new Error('connectionName is not set')
|
|
145
|
-
}
|
|
146
|
-
logger.info('Starting OAuth flow for connectionName:', this.absOauthConnectionName)
|
|
147
|
-
await this.refreshToken(context)
|
|
148
|
-
|
|
149
|
-
const act = context.activity
|
|
150
|
-
|
|
151
|
-
// Check cache first before starting OAuth flow
|
|
152
|
-
if (act.channelId && act.from && act.from.id) {
|
|
153
|
-
const cacheKey = this.getCacheKey(context)
|
|
154
|
-
const cachedEntry = this.tokenCache.get(cacheKey)
|
|
155
|
-
if (cachedEntry && Date.now() < cachedEntry.expiresAt) {
|
|
156
|
-
logger.info(`Returning cached token for user in beginFlow with cache key: ${cacheKey}`)
|
|
157
|
-
return cachedEntry.token
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const output = await this.userTokenClient.getTokenOrSignInResource(act.from?.id!, this.absOauthConnectionName, act.channelId!, act.getConversationReference(), act.relatesTo!, undefined!)
|
|
162
|
-
if (output && output.tokenResponse) {
|
|
163
|
-
// Cache the token if it's valid
|
|
164
|
-
if (act.channelId && act.from && act.from.id) {
|
|
165
|
-
const cacheKey = this.getCacheKey(context)
|
|
166
|
-
const cacheExpiry = Date.now() + (10 * 60 * 1000) // 10 minutes from now
|
|
167
|
-
this.tokenCache.set(cacheKey, {
|
|
168
|
-
token: output.tokenResponse,
|
|
169
|
-
expiresAt: cacheExpiry
|
|
170
|
-
})
|
|
171
|
-
logger.info('Token cached for 10 minutes in beginFlow')
|
|
172
|
-
this.state = { flowStarted: false, flowExpires: 0, absOauthConnectionName: this.absOauthConnectionName }
|
|
173
|
-
}
|
|
174
|
-
logger.info('Token retrieved successfully')
|
|
175
|
-
return output.tokenResponse
|
|
176
|
-
}
|
|
177
|
-
const oCard: Attachment = CardFactory.oauthCard(this.absOauthConnectionName, this.cardTitle, this.cardText, output.signInResource)
|
|
178
|
-
await context.sendActivity(MessageFactory.attachment(oCard))
|
|
179
|
-
this.state = { flowStarted: true, flowExpires: Date.now() + 60 * 5 * 1000, absOauthConnectionName: this.absOauthConnectionName }
|
|
180
|
-
await this.storage.write({ [this.getFlowStateKey(context)]: this.state })
|
|
181
|
-
logger.info('OAuth card sent, flow started')
|
|
182
|
-
return undefined
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Continues the OAuth flow.
|
|
187
|
-
* @param context The turn context.
|
|
188
|
-
* @returns A promise that resolves to the user token response.
|
|
189
|
-
*/
|
|
190
|
-
public async continueFlow (context: TurnContext): Promise<TokenResponse> {
|
|
191
|
-
this.state = await this.getFlowState(context)
|
|
192
|
-
await this.refreshToken(context)
|
|
193
|
-
if (this.state?.flowExpires !== 0 && Date.now() > this.state?.flowExpires!) {
|
|
194
|
-
logger.warn('Flow expired')
|
|
195
|
-
await context.sendActivity(MessageFactory.text('Sign-in session expired. Please try again.'))
|
|
196
|
-
this.state!.flowStarted = false
|
|
197
|
-
return { token: undefined }
|
|
198
|
-
}
|
|
199
|
-
const contFlowActivity = context.activity
|
|
200
|
-
if (contFlowActivity.type === ActivityTypes.Message) {
|
|
201
|
-
const magicCode = contFlowActivity.text as string
|
|
202
|
-
if (magicCode.match(/^\d{6}$/)) {
|
|
203
|
-
const result = await this.userTokenClient?.getUserToken(this.absOauthConnectionName, contFlowActivity.channelId!, contFlowActivity.from?.id!, magicCode)!
|
|
204
|
-
if (result && result.token) {
|
|
205
|
-
// Cache the token if it's valid
|
|
206
|
-
if (contFlowActivity.channelId && contFlowActivity.from && contFlowActivity.from.id) {
|
|
207
|
-
const cacheKey = this.getCacheKey(context)
|
|
208
|
-
const cacheExpiry = Date.now() + (10 * 60 * 1000) // 10 minutes from now
|
|
209
|
-
this.tokenCache.set(cacheKey, {
|
|
210
|
-
token: result,
|
|
211
|
-
expiresAt: cacheExpiry
|
|
212
|
-
})
|
|
213
|
-
logger.info('Token cached for 10 minutes in continueFlow (magic code)')
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
await this.storage.delete([this.getFlowStateKey(context)])
|
|
217
|
-
logger.info('Token retrieved successfully')
|
|
218
|
-
return result
|
|
219
|
-
} else {
|
|
220
|
-
// await context.sendActivity(MessageFactory.text('Invalid code. Please try again.'))
|
|
221
|
-
logger.warn('Invalid magic code provided')
|
|
222
|
-
this.state = { flowStarted: true, flowExpires: Date.now() + 30000, absOauthConnectionName: this.absOauthConnectionName }
|
|
223
|
-
await this.storage.write({ [this.getFlowStateKey(context)]: this.state })
|
|
224
|
-
return { token: undefined }
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
logger.warn('Invalid magic code format')
|
|
228
|
-
await context.sendActivity(MessageFactory.text('Invalid code format. Please enter a 6-digit code.'))
|
|
229
|
-
return { token: undefined }
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (contFlowActivity.type === ActivityTypes.Invoke && contFlowActivity.name === 'signin/verifyState') {
|
|
234
|
-
logger.info('Continuing OAuth flow with verifyState')
|
|
235
|
-
const tokenVerifyState = contFlowActivity.value as TokenVerifyState
|
|
236
|
-
const magicCode = tokenVerifyState.state
|
|
237
|
-
const result = await this.userTokenClient?.getUserToken(this.absOauthConnectionName, contFlowActivity.channelId!, contFlowActivity.from?.id!, magicCode)!
|
|
238
|
-
// Cache the token if it's valid
|
|
239
|
-
if (result && result.token && contFlowActivity.channelId && contFlowActivity.from && contFlowActivity.from.id) {
|
|
240
|
-
const cacheKey = this.getCacheKey(context)
|
|
241
|
-
const cacheExpiry = Date.now() + (10 * 60 * 1000) // 10 minutes from now
|
|
242
|
-
this.tokenCache.set(cacheKey, {
|
|
243
|
-
token: result,
|
|
244
|
-
expiresAt: cacheExpiry
|
|
245
|
-
})
|
|
246
|
-
logger.info('Token cached for 10 minutes in continueFlow (verifyState)')
|
|
247
|
-
}
|
|
248
|
-
return result
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (contFlowActivity.type === ActivityTypes.Invoke && contFlowActivity.name === 'signin/tokenExchange') {
|
|
252
|
-
logger.info('Continuing OAuth flow with tokenExchange')
|
|
253
|
-
const tokenExchangeRequest = contFlowActivity.value as TokenExchangeRequest
|
|
254
|
-
if (this.tokenExchangeId === tokenExchangeRequest.id) { // dedupe
|
|
255
|
-
logger.debug('Token exchange request already processed, skipping')
|
|
256
|
-
return { token: undefined }
|
|
257
|
-
}
|
|
258
|
-
this.tokenExchangeId = tokenExchangeRequest.id!
|
|
259
|
-
const userTokenResp = await this.userTokenClient?.exchangeTokenAsync(contFlowActivity.from?.id!, this.absOauthConnectionName, contFlowActivity.channelId!, tokenExchangeRequest)
|
|
260
|
-
if (userTokenResp && userTokenResp.token) {
|
|
261
|
-
// Cache the token if it's valid
|
|
262
|
-
if (contFlowActivity.channelId && contFlowActivity.from && contFlowActivity.from.id) {
|
|
263
|
-
const cacheKey = this.getCacheKey(context)
|
|
264
|
-
const cacheExpiry = Date.now() + (10 * 60 * 1000) // 10 minutes from now
|
|
265
|
-
this.tokenCache.set(cacheKey, {
|
|
266
|
-
token: userTokenResp,
|
|
267
|
-
expiresAt: cacheExpiry
|
|
268
|
-
})
|
|
269
|
-
logger.info('Token cached for 10 minutes in continueFlow (tokenExchange)')
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
logger.info('Token exchanged')
|
|
273
|
-
this.state!.flowStarted = false
|
|
274
|
-
await this.storage.write({ [this.getFlowStateKey(context)]: this.state })
|
|
275
|
-
return userTokenResp
|
|
276
|
-
} else {
|
|
277
|
-
logger.warn('Token exchange failed')
|
|
278
|
-
this.state!.flowStarted = true
|
|
279
|
-
return { token: undefined }
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return { token: undefined }
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Signs the user out.
|
|
287
|
-
* @param context The turn context.
|
|
288
|
-
* @returns A promise that resolves when the sign-out operation is complete.
|
|
289
|
-
*/
|
|
290
|
-
public async signOut (context: TurnContext): Promise<void> {
|
|
291
|
-
await this.refreshToken(context)
|
|
292
|
-
|
|
293
|
-
// Clear cached token for this user
|
|
294
|
-
const activity = context.activity
|
|
295
|
-
if (activity.channelId && activity.from && activity.from.id) {
|
|
296
|
-
const cacheKey = this.getCacheKey(context)
|
|
297
|
-
this.tokenCache.delete(cacheKey)
|
|
298
|
-
logger.info('Cached token cleared for user')
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
await this.userTokenClient?.signOut(context.activity.from?.id as string, this.absOauthConnectionName, context.activity.channelId as string)
|
|
302
|
-
this.state = { flowStarted: false, flowExpires: 0, absOauthConnectionName: this.absOauthConnectionName }
|
|
303
|
-
await this.storage.delete([this.getFlowStateKey(context)])
|
|
304
|
-
logger.info('User signed out successfully from connection:', this.absOauthConnectionName)
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Gets the user state for the OAuth flow.
|
|
309
|
-
* @param context The turn context.
|
|
310
|
-
* @returns A promise that resolves to the flow state.
|
|
311
|
-
*/
|
|
312
|
-
public async getFlowState (context: TurnContext) : Promise<FlowState> {
|
|
313
|
-
const key = this.getFlowStateKey(context)
|
|
314
|
-
const data = await this.storage.read([key])
|
|
315
|
-
const flowState: FlowState = data[key] // ?? { flowStarted: false, flowExpires: 0 }
|
|
316
|
-
return flowState
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Sets the flow state for the OAuth flow.
|
|
321
|
-
* @param context The turn context.
|
|
322
|
-
* @param flowState The flow state to set.
|
|
323
|
-
* @returns A promise that resolves when the flow state is set.
|
|
324
|
-
*/
|
|
325
|
-
public async setFlowState (context: TurnContext, flowState: FlowState) : Promise<void> {
|
|
326
|
-
const key = this.getFlowStateKey(context)
|
|
327
|
-
await this.storage.write({ [key]: flowState })
|
|
328
|
-
this.state = flowState
|
|
329
|
-
logger.debug(`Flow state set: ${JSON.stringify(flowState)}`)
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Initializes the user token client if not already initialized.
|
|
334
|
-
* @param context The turn context used to get authentication credentials.
|
|
335
|
-
*/
|
|
336
|
-
private async refreshToken (context: TurnContext) {
|
|
337
|
-
const cachedToken = this.tokenCache.get('__access_token__')
|
|
338
|
-
if (!cachedToken || Date.now() > cachedToken.expiresAt) {
|
|
339
|
-
const accessToken = await context.adapter.authProvider.getAccessToken(context.adapter.authConfig, 'https://api.botframework.com')
|
|
340
|
-
const decodedToken = jwt.decode(accessToken) as JwtPayload
|
|
341
|
-
this.tokenCache.set('__access_token__', {
|
|
342
|
-
token: { token: accessToken },
|
|
343
|
-
expiresAt: decodedToken?.exp ? decodedToken.exp * 1000 - 1000 : Date.now() + 10 * 60 * 1000
|
|
344
|
-
})
|
|
345
|
-
this.userTokenClient.updateAuthToken(accessToken)
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Generates a cache key for storing user tokens.
|
|
351
|
-
* @param context The turn context containing activity information.
|
|
352
|
-
* @returns The cache key string in format: channelId_userId_connectionName.
|
|
353
|
-
* @throws Will throw an error if required activity properties are missing.
|
|
354
|
-
*/
|
|
355
|
-
private getCacheKey (context: TurnContext): string {
|
|
356
|
-
const activity = context.activity
|
|
357
|
-
if (!activity.channelId || !activity.from || !activity.from.id) {
|
|
358
|
-
throw new Error('ChannelId and from.id must be set in the activity for cache key generation')
|
|
359
|
-
}
|
|
360
|
-
return `${activity.channelId}_${activity.from.id}_${this.absOauthConnectionName}`
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Generates a storage key for persisting OAuth flow state.
|
|
365
|
-
* @param context The turn context containing activity information.
|
|
366
|
-
* @returns The storage key string in format: oauth/channelId/conversationId/userId/flowState.
|
|
367
|
-
* @throws Will throw an error if required activity properties are missing.
|
|
368
|
-
*/
|
|
369
|
-
private getFlowStateKey (context: TurnContext): string {
|
|
370
|
-
const channelId = context.activity.channelId
|
|
371
|
-
const conversationId = context.activity.conversation?.id
|
|
372
|
-
const userId = context.activity.from?.id
|
|
373
|
-
if (!channelId || !conversationId || !userId) {
|
|
374
|
-
throw new Error('ChannelId, conversationId, and userId must be set in the activity')
|
|
375
|
-
}
|
|
376
|
-
return `oauth/${channelId}/${userId}/${this.absOauthConnectionName}/flowState`
|
|
377
|
-
}
|
|
378
|
-
}
|