botbuilder-dialogs
Advanced tools
Comparing version 4.8.0-preview-109324 to 4.8.0
@@ -67,2 +67,10 @@ /** | ||
noAction?: boolean; | ||
/** | ||
* (Optional) Default is `true`. If `false`, the Number Model will not be used to check the utterance for numbers. | ||
*/ | ||
recognizeNumbers?: boolean; | ||
/** | ||
* (Optional) Default is `true`. If `false`, the Ordinal Model will not be used to check the utterance for ordinal numbers. | ||
*/ | ||
recognizeOrdinals?: boolean; | ||
} | ||
@@ -69,0 +77,0 @@ /** |
@@ -67,2 +67,8 @@ "use strict"; | ||
} | ||
// Initialize options | ||
options = Object.assign({ | ||
locale: 'en-us', | ||
recognizeNumbers: true, | ||
recognizeOrdinals: true | ||
}, options); | ||
// Normalize choices | ||
@@ -75,13 +81,12 @@ const list = (choices || []).map((choice) => typeof choice === 'string' ? { value: choice } : choice).filter((choice) => choice // TODO: does this do anything? | ||
// a numerical index or ordinal as well. | ||
const locale = options && options.locale ? options.locale : 'en-us'; | ||
let matched = findChoices_1.findChoices(utterance, list, options); | ||
if (matched.length === 0) { | ||
// Next try finding by ordinal | ||
const ordinals = Recognizers.recognizeOrdinal(utterance, locale); | ||
if (ordinals.length > 0) { | ||
if (options.recognizeOrdinals) { | ||
const ordinals = Recognizers.recognizeOrdinal(utterance, options.locale); | ||
ordinals.forEach(matchChoiceByIndex); | ||
} | ||
else { | ||
// Finally try by numerical index | ||
Recognizers.recognizeNumber(utterance, locale).forEach(matchChoiceByIndex); | ||
// Finally try by numerical index | ||
if (matched.length === 0 && options.recognizeNumbers) { | ||
Recognizers.recognizeNumber(utterance, options.locale).forEach(matchChoiceByIndex); | ||
} | ||
@@ -88,0 +93,0 @@ // Sort any found matches by their position within the utterance. |
@@ -8,14 +8,18 @@ /** | ||
*/ | ||
export * from './beginSkillDialogOptions'; | ||
export * from './choices'; | ||
export * from './memory'; | ||
export * from './prompts'; | ||
export * from './dialog'; | ||
export * from './componentDialog'; | ||
export * from './configurable'; | ||
export * from './dialog'; | ||
export * from './dialogContainer'; | ||
export * from './dialogContext'; | ||
export * from './dialogEvents'; | ||
export { runDialog } from './dialogHelper'; | ||
export * from './dialogSet'; | ||
export * from './memory'; | ||
export * from './prompts'; | ||
export * from './skillDialog'; | ||
export * from './skillDialogOptions'; | ||
export * from './waterfallDialog'; | ||
export * from './waterfallStepContext'; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
@@ -13,14 +9,21 @@ * @module botbuilder-dialogs | ||
*/ | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./choices")); | ||
__export(require("./memory")); | ||
__export(require("./prompts")); | ||
__export(require("./dialog")); | ||
__export(require("./componentDialog")); | ||
__export(require("./configurable")); | ||
__export(require("./dialog")); | ||
__export(require("./dialogContainer")); | ||
__export(require("./dialogContext")); | ||
__export(require("./dialogEvents")); | ||
var dialogHelper_1 = require("./dialogHelper"); | ||
exports.runDialog = dialogHelper_1.runDialog; | ||
__export(require("./dialogSet")); | ||
__export(require("./memory")); | ||
__export(require("./prompts")); | ||
__export(require("./skillDialog")); | ||
__export(require("./waterfallDialog")); | ||
__export(require("./waterfallStepContext")); | ||
//# sourceMappingURL=index.js.map |
@@ -83,2 +83,5 @@ "use strict"; | ||
const utterance = activity.text; | ||
if (utterance == null) { | ||
return result; | ||
} | ||
const culture = this.determineCulture(context.activity); | ||
@@ -85,0 +88,0 @@ const results = Recognizers.recognizeBoolean(utterance, culture); |
@@ -8,3 +8,3 @@ /** | ||
*/ | ||
import { TokenResponse, TurnContext } from 'botbuilder-core'; | ||
import { CoreAppCredentials, TokenResponse, TurnContext } from 'botbuilder-core'; | ||
import { Dialog, DialogTurnResult } from '../dialog'; | ||
@@ -18,2 +18,6 @@ import { DialogContext } from '../dialogContext'; | ||
/** | ||
* AppCredentials for OAuth. | ||
*/ | ||
oAuthAppCredentials?: CoreAppCredentials; | ||
/** | ||
* Name of the OAuth connection being used. | ||
@@ -138,7 +142,11 @@ */ | ||
private recognizeToken; | ||
private getTokenExchangeInvokeResponse; | ||
private static isFromStreamingConnection; | ||
private isTokenResponseEvent; | ||
private isTeamsVerificationInvoke; | ||
private isOAuthCardSupported; | ||
private isTokenExchangeRequestInvoke; | ||
private isTokenExchangeRequest; | ||
private channelSupportsOAuthCard; | ||
} | ||
//# sourceMappingURL=oauthPrompt.d.ts.map |
@@ -22,2 +22,12 @@ "use strict"; | ||
/** | ||
* Response body returned for a token exchange invoke activity. | ||
*/ | ||
class TokenExchangeInvokeResponse { | ||
constructor(id, connectionName, failureDetail) { | ||
this.id = id; | ||
this.connectionName = connectionName; | ||
this.failureDetail = failureDetail; | ||
} | ||
} | ||
/** | ||
* Creates a new prompt that asks the user to sign in using the Bot Frameworks Single Sign On (SSO) | ||
@@ -185,3 +195,3 @@ * service. | ||
const adapter = context.adapter; | ||
return yield adapter.getUserToken(context, this.settings.connectionName, code); | ||
return yield adapter.getUserToken(context, this.settings.connectionName, code, this.settings.oAuthAppCredentials); | ||
}); | ||
@@ -212,3 +222,3 @@ } | ||
const adapter = context.adapter; | ||
return adapter.signOutUser(context, this.settings.connectionName); | ||
return adapter.signOutUser(context, this.settings.connectionName, null, this.settings.oAuthAppCredentials); | ||
}); | ||
@@ -228,22 +238,23 @@ } | ||
// Add login card as needed | ||
if (this.channelSupportsOAuthCard(context.activity.channelId)) { | ||
if (this.isOAuthCardSupported(context)) { | ||
const cards = msg.attachments.filter((a) => a.contentType === botbuilder_core_1.CardFactory.contentTypes.oauthCard); | ||
if (cards.length === 0) { | ||
let cardActionType = botbuilder_core_1.ActionTypes.Signin; | ||
let link; | ||
if (OAuthPrompt.isFromStreamingConnection(context.activity)) { | ||
link = yield context.adapter.getSignInLink(context, this.settings.connectionName); | ||
} | ||
else { | ||
// Retrieve the ClaimsIdentity from a BotFrameworkAdapter. For more information see | ||
// https://github.com/microsoft/botbuilder-js/commit/b7932e37bb6e421985d5ce53edd9e82af6240a63#diff-3e3af334c0c6adf4906ee5e2a23beaebR250 | ||
const identity = context.turnState.get(context.adapter.BotIdentityKey); | ||
if (identity && skillsHelpers_1.isSkillClaim(identity.claims)) { | ||
// Force magic code for Skills (to be addressed in R8) | ||
link = yield context.adapter.getSignInLink(context, this.settings.connectionName); | ||
const signInResource = yield context.adapter.getSignInResource(context, this.settings.connectionName, context.activity.from.id, null, this.settings.oAuthAppCredentials); | ||
let link = signInResource.signInLink; | ||
const identity = context.turnState.get(context.adapter.BotIdentityKey); | ||
// use the SignInLink when | ||
// in speech channel or | ||
// bot is a skill or | ||
// an extra OAuthAppCredentials is being passed in | ||
if ((identity && skillsHelpers_1.isSkillClaim(identity.claims)) || OAuthPrompt.isFromStreamingConnection(context.activity) || this.settings.oAuthAppCredentials) { | ||
if (context.activity.channelId === botbuilder_core_1.Channels.Emulator) { | ||
cardActionType = botbuilder_core_1.ActionTypes.OpenUrl; | ||
} | ||
} | ||
else { | ||
link = undefined; | ||
} | ||
// Append oauth card | ||
const card = botbuilder_core_1.CardFactory.oauthCard(this.settings.connectionName, this.settings.title, this.settings.text, link); | ||
const card = botbuilder_core_1.CardFactory.oauthCard(this.settings.connectionName, this.settings.title, this.settings.text, link, signInResource.tokenExchangeResource); | ||
// Set the appropriate ActionType for the button. | ||
@@ -258,4 +269,4 @@ card.content.buttons[0].type = cardActionType; | ||
// Append signin card | ||
const link = yield context.adapter.getSignInLink(context, this.settings.connectionName); | ||
msg.attachments.push(botbuilder_core_1.CardFactory.signinCard(this.settings.title, link, this.settings.text)); | ||
const signInResource = yield context.adapter.getSignInResource(context, this.settings.connectionName, context.activity.from.id, null, this.settings.oAuthAppCredentials); | ||
msg.attachments.push(botbuilder_core_1.CardFactory.signinCard(this.settings.title, signInResource.signInLink, this.settings.text)); | ||
} | ||
@@ -282,3 +293,3 @@ } | ||
if (token !== undefined) { | ||
yield context.sendActivity({ type: 'invokeResponse', value: { status: 200 } }); | ||
yield context.sendActivity({ type: 'invokeResponse', value: { status: botbuilder_core_1.StatusCodes.OK } }); | ||
} | ||
@@ -293,2 +304,35 @@ else { | ||
} | ||
else if (this.isTokenExchangeRequestInvoke(context)) { | ||
// Received activity is not a token exchange request | ||
if (!(context.activity.value && this.isTokenExchangeRequest(context.activity.value))) { | ||
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder_core_1.StatusCodes.BAD_REQUEST, 'The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.')); | ||
} | ||
else if (context.activity.value.connectionName != this.settings.connectionName) { | ||
// Connection name on activity does not match that of setting | ||
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder_core_1.StatusCodes.BAD_REQUEST, 'The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a ConnectionName that does not match the ConnectionName' + | ||
'expected by the bots active OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid ConnectionName in the TokenExchangeInvokeRequest')); | ||
} | ||
else if (!('exchangeToken' in context.adapter)) { | ||
// Token Exchange not supported in the adapter | ||
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder_core_1.StatusCodes.BAD_GATEWAY, 'The bot\'s BotAdapter does not support token exchange operations. Ensure the bot\'s Adapter supports the ITokenExchangeProvider interface.')); | ||
throw new Error('OAuthPrompt.recognize(): not supported by the current adapter'); | ||
} | ||
else { | ||
// No errors. Proceed with token exchange | ||
const extendedUserTokenProvider = context.adapter; | ||
const tokenExchangeResponse = yield extendedUserTokenProvider.exchangeToken(context, this.settings.connectionName, context.activity.from.id, { token: context.activity.value.token }); | ||
if (!tokenExchangeResponse || !tokenExchangeResponse.token) { | ||
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder_core_1.StatusCodes.CONFLICT, 'The bot is unable to exchange token. Proceed with regular login.')); | ||
} | ||
else { | ||
yield context.sendActivity(this.getTokenExchangeInvokeResponse(botbuilder_core_1.StatusCodes.OK, null, context.activity.value.id)); | ||
token = { | ||
channelId: tokenExchangeResponse.channelId, | ||
connectionName: tokenExchangeResponse.connectionName, | ||
token: tokenExchangeResponse.token, | ||
expiration: null | ||
}; | ||
} | ||
} | ||
} | ||
else if (context.activity.type === botbuilder_core_1.ActivityTypes.Message) { | ||
@@ -303,2 +347,9 @@ const matched = /(\d{6})/.exec(context.activity.text); | ||
} | ||
getTokenExchangeInvokeResponse(status, failureDetail, id) { | ||
const invokeResponse = { | ||
type: 'invokeResponse', | ||
value: { status, body: new TokenExchangeInvokeResponse(id, this.settings.connectionName, failureDetail) } | ||
}; | ||
return invokeResponse; | ||
} | ||
static isFromStreamingConnection(activity) { | ||
@@ -309,8 +360,37 @@ return activity && activity.serviceUrl && !activity.serviceUrl.toLowerCase().startsWith('http'); | ||
const activity = context.activity; | ||
return activity.type === botbuilder_core_1.ActivityTypes.Event && activity.name === 'tokens/response'; | ||
return activity.type === botbuilder_core_1.ActivityTypes.Event && activity.name === botbuilder_core_1.tokenResponseEventName; | ||
} | ||
isTeamsVerificationInvoke(context) { | ||
const activity = context.activity; | ||
return activity.type === botbuilder_core_1.ActivityTypes.Invoke && activity.name === 'signin/verifyState'; | ||
return activity.type === botbuilder_core_1.ActivityTypes.Invoke && activity.name === botbuilder_core_1.verifyStateOperationName; | ||
} | ||
isOAuthCardSupported(context) { | ||
// Azure Bot Service OAuth cards are not supported in the community adapters. Since community adapters | ||
// have a 'name' in them, we cast the adapter to 'any' to check for the name. | ||
const adapter = context.adapter; | ||
if (adapter.name) { | ||
switch (adapter.name) { | ||
case 'Facebook Adapter': | ||
case 'Google Hangouts Adapter': | ||
case 'Slack Adapter': | ||
case 'Twilio SMS Adapter': | ||
case 'Web Adapter': | ||
case 'Webex Adapter': | ||
case 'Botkit CMS': | ||
return false; | ||
default: | ||
} | ||
} | ||
return this.channelSupportsOAuthCard(context.activity.channelId); | ||
} | ||
isTokenExchangeRequestInvoke(context) { | ||
const activity = context.activity; | ||
return activity.type === botbuilder_core_1.ActivityTypes.Invoke && activity.name === botbuilder_core_1.tokenExchangeOperationName; | ||
} | ||
isTokenExchangeRequest(obj) { | ||
if (obj.hasOwnProperty('token')) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
channelSupportsOAuthCard(channelId) { | ||
@@ -317,0 +397,0 @@ switch (channelId) { |
@@ -5,3 +5,3 @@ { | ||
"description": "A dialog stack based conversation manager for Microsoft BotBuilder.", | ||
"version": "4.8.0-preview-109324", | ||
"version": "4.8.0", | ||
"license": "MIT", | ||
@@ -29,3 +29,3 @@ "keywords": [ | ||
"@types/node": "^10.12.18", | ||
"botbuilder-core": "4.8.0-preview-109324", | ||
"botbuilder-core": "4.8.0", | ||
"globalize": "^1.4.2" | ||
@@ -37,3 +37,3 @@ }, | ||
"mocha": "^5.2.0", | ||
"nyc": "^11.4.1", | ||
"nyc": "^15.0.0", | ||
"source-map-support": "^0.5.3", | ||
@@ -48,3 +48,3 @@ "ts-node": "^4.1.0", | ||
"clean": "erase /q /s .\\lib", | ||
"set-version": "npm version --allow-same-version 4.8.0-preview-109324" | ||
"set-version": "npm version --allow-same-version 4.8.0" | ||
}, | ||
@@ -51,0 +51,0 @@ "files": [ |
@@ -73,2 +73,12 @@ /** | ||
noAction?: boolean; | ||
/** | ||
* (Optional) Default is `true`. If `false`, the Number Model will not be used to check the utterance for numbers. | ||
*/ | ||
recognizeNumbers?: boolean; | ||
/** | ||
* (Optional) Default is `true`. If `false`, the Ordinal Model will not be used to check the utterance for ordinal numbers. | ||
*/ | ||
recognizeOrdinals?: boolean; | ||
} | ||
@@ -75,0 +85,0 @@ |
@@ -67,2 +67,9 @@ /** | ||
} | ||
// Initialize options | ||
options = Object.assign({ | ||
locale: 'en-us', | ||
recognizeNumbers: true, | ||
recognizeOrdinals: true | ||
} as FindChoicesOptions, options); | ||
@@ -80,14 +87,15 @@ // Normalize choices | ||
// a numerical index or ordinal as well. | ||
const locale: string = options && options.locale ? options.locale : 'en-us'; | ||
let matched: ModelResult<FoundChoice>[] = findChoices(utterance, list, options); | ||
if (matched.length === 0) { | ||
// Next try finding by ordinal | ||
const ordinals: ModelResult[] = Recognizers.recognizeOrdinal(utterance, locale); | ||
if (ordinals.length > 0) { | ||
if (options.recognizeOrdinals) { | ||
const ordinals: ModelResult[] = Recognizers.recognizeOrdinal(utterance, options.locale); | ||
ordinals.forEach(matchChoiceByIndex); | ||
} else { | ||
// Finally try by numerical index | ||
Recognizers.recognizeNumber(utterance, locale).forEach(matchChoiceByIndex); | ||
} | ||
// Finally try by numerical index | ||
if (matched.length === 0 && options.recognizeNumbers) { | ||
Recognizers.recognizeNumber(utterance, options.locale).forEach(matchChoiceByIndex); | ||
} | ||
// Sort any found matches by their position within the utterance. | ||
@@ -94,0 +102,0 @@ // - The results from findChoices() are already properly sorted so we just need this |
@@ -237,3 +237,3 @@ /** | ||
*/ | ||
constructor(dialogId?: string) { | ||
public constructor(dialogId?: string) { | ||
super(); | ||
@@ -249,12 +249,12 @@ this.id = dialogId; | ||
*/ | ||
public get id(): string { | ||
if (this._id === undefined) { | ||
this._id = this.onComputeId(); | ||
} | ||
return this._id; | ||
} | ||
public get id(): string { | ||
if (this._id === undefined) { | ||
this._id = this.onComputeId(); | ||
} | ||
return this._id; | ||
} | ||
public set id(value: string) { | ||
this._id = value; | ||
} | ||
public set id(value: string) { | ||
this._id = value; | ||
} | ||
@@ -261,0 +261,0 @@ /** |
@@ -8,13 +8,18 @@ /** | ||
*/ | ||
export * from './beginSkillDialogOptions'; | ||
export * from './choices'; | ||
export * from './memory'; | ||
export * from './prompts'; | ||
export * from './dialog'; | ||
export * from './componentDialog'; | ||
export * from './configurable'; | ||
export * from './dialog'; | ||
export * from './dialogContainer'; | ||
export * from './dialogContext'; | ||
export * from './dialogEvents'; | ||
export { runDialog } from './dialogHelper'; | ||
export * from './dialogSet'; | ||
export * from './memory'; | ||
export * from './prompts'; | ||
export * from './skillDialog'; | ||
export * from './skillDialogOptions'; | ||
export * from './waterfallDialog'; | ||
export * from './waterfallStepContext'; |
@@ -108,2 +108,5 @@ /** | ||
const utterance: string = activity.text; | ||
if (utterance == null) { | ||
return result; | ||
} | ||
const culture: string = this.determineCulture(context.activity); | ||
@@ -110,0 +113,0 @@ const results: any = Recognizers.recognizeBoolean(utterance, culture); |
@@ -8,3 +8,3 @@ /** | ||
*/ | ||
import { Activity, ActivityTypes, Attachment, CardFactory, Channels, InputHints, MessageFactory, OAuthLoginTimeoutKey, TokenResponse, TurnContext, IUserTokenProvider, OAuthCard, ActionTypes, } from 'botbuilder-core'; | ||
import { Activity, ActivityTypes, Attachment, CoreAppCredentials, BotAdapter, CardFactory, Channels, InputHints, MessageFactory, OAuthLoginTimeoutKey, TokenResponse, TurnContext, OAuthCard, ActionTypes, ExtendedUserTokenProvider, verifyStateOperationName, StatusCodes, tokenExchangeOperationName, tokenResponseEventName } from 'botbuilder-core'; | ||
import { Dialog, DialogTurnResult } from '../dialog'; | ||
@@ -16,2 +16,26 @@ import { DialogContext } from '../dialogContext'; | ||
/** | ||
* Request body accepted for a token exchange invoke activity. | ||
*/ | ||
interface TokenExchangeInvokeRequest { | ||
id: string; | ||
connectionName: string; | ||
token: string; | ||
} | ||
/** | ||
* Response body returned for a token exchange invoke activity. | ||
*/ | ||
class TokenExchangeInvokeResponse { | ||
id: string; | ||
connectionName: string; | ||
failureDetail: string; | ||
constructor(id:string, connectionName:string, failureDetail:string) { | ||
this.id = id; | ||
this.connectionName = connectionName; | ||
this.failureDetail = failureDetail; | ||
} | ||
} | ||
/** | ||
* Settings used to configure an `OAuthPrompt` instance. | ||
@@ -21,2 +45,7 @@ */ | ||
/** | ||
* AppCredentials for OAuth. | ||
*/ | ||
oAuthAppCredentials?: CoreAppCredentials; | ||
/** | ||
* Name of the OAuth connection being used. | ||
@@ -110,3 +139,2 @@ */ | ||
export class OAuthPrompt extends Dialog { | ||
/** | ||
@@ -208,5 +236,5 @@ * Creates a new OAuthPrompt instance. | ||
// Get the token and call validator | ||
const adapter: IUserTokenProvider = context.adapter as IUserTokenProvider; | ||
const adapter: ExtendedUserTokenProvider = context.adapter as ExtendedUserTokenProvider; | ||
return await adapter.getUserToken(context, this.settings.connectionName, code); | ||
return await adapter.getUserToken(context, this.settings.connectionName, code, this.settings.oAuthAppCredentials); | ||
} | ||
@@ -236,5 +264,5 @@ | ||
// Sign out user | ||
const adapter: IUserTokenProvider = context.adapter as IUserTokenProvider; | ||
const adapter: ExtendedUserTokenProvider = context.adapter as ExtendedUserTokenProvider; | ||
return adapter.signOutUser(context, this.settings.connectionName); | ||
return adapter.signOutUser(context, this.settings.connectionName, null, this.settings.oAuthAppCredentials); | ||
} | ||
@@ -254,19 +282,23 @@ | ||
// Add login card as needed | ||
if (this.channelSupportsOAuthCard(context.activity.channelId)) { | ||
if (this.isOAuthCardSupported(context)) { | ||
const cards: Attachment[] = msg.attachments.filter((a: Attachment) => a.contentType === CardFactory.contentTypes.oauthCard); | ||
if (cards.length === 0) { | ||
let cardActionType = ActionTypes.Signin; | ||
let link: string; | ||
if (OAuthPrompt.isFromStreamingConnection(context.activity)) { | ||
link = await (context.adapter as any).getSignInLink(context, this.settings.connectionName); | ||
} else { | ||
// Retrieve the ClaimsIdentity from a BotFrameworkAdapter. For more information see | ||
// https://github.com/microsoft/botbuilder-js/commit/b7932e37bb6e421985d5ce53edd9e82af6240a63#diff-3e3af334c0c6adf4906ee5e2a23beaebR250 | ||
const identity = context.turnState.get((context.adapter as any).BotIdentityKey); | ||
if (identity && isSkillClaim(identity.claims)) { | ||
// Force magic code for Skills (to be addressed in R8) | ||
link = await (context.adapter as any).getSignInLink(context, this.settings.connectionName); | ||
const signInResource = await (context.adapter as ExtendedUserTokenProvider).getSignInResource(context, this.settings.connectionName, context.activity.from.id, null, this.settings.oAuthAppCredentials); | ||
let link = signInResource.signInLink; | ||
const identity = context.turnState.get((context.adapter as BotAdapter).BotIdentityKey); | ||
// use the SignInLink when | ||
// in speech channel or | ||
// bot is a skill or | ||
// an extra OAuthAppCredentials is being passed in | ||
if((identity && isSkillClaim(identity.claims)) || OAuthPrompt.isFromStreamingConnection(context.activity) || this.settings.oAuthAppCredentials) { | ||
if(context.activity.channelId === Channels.Emulator) { | ||
cardActionType = ActionTypes.OpenUrl; | ||
} | ||
} | ||
else { | ||
link = undefined; | ||
} | ||
// Append oauth card | ||
@@ -277,3 +309,4 @@ const card = CardFactory.oauthCard( | ||
this.settings.text, | ||
link | ||
link, | ||
signInResource.tokenExchangeResource | ||
); | ||
@@ -289,6 +322,6 @@ | ||
// Append signin card | ||
const link: any = await (context.adapter as any).getSignInLink(context, this.settings.connectionName); | ||
const signInResource = await (context.adapter as ExtendedUserTokenProvider).getSignInResource(context, this.settings.connectionName, context.activity.from.id, null, this.settings.oAuthAppCredentials); | ||
msg.attachments.push(CardFactory.signinCard( | ||
this.settings.title, | ||
link, | ||
signInResource.signInLink, | ||
this.settings.text | ||
@@ -317,3 +350,3 @@ )); | ||
if (token !== undefined) { | ||
await context.sendActivity({ type: 'invokeResponse', value: { status: 200 }}); | ||
await context.sendActivity({ type: 'invokeResponse', value: { status: StatusCodes.OK }}); | ||
} else { | ||
@@ -327,2 +360,40 @@ await context.sendActivity({ type: 'invokeResponse', value: { status: 404 }}); | ||
} | ||
} else if (this.isTokenExchangeRequestInvoke(context)) { | ||
// Received activity is not a token exchange request | ||
if(!(context.activity.value && this.isTokenExchangeRequest(context.activity.value))) { | ||
await context.sendActivity(this.getTokenExchangeInvokeResponse( | ||
StatusCodes.BAD_REQUEST, | ||
'The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.')); | ||
} else if (context.activity.value.connectionName != this.settings.connectionName) { | ||
// Connection name on activity does not match that of setting | ||
await context.sendActivity(this.getTokenExchangeInvokeResponse( | ||
StatusCodes.BAD_REQUEST, | ||
'The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a ConnectionName that does not match the ConnectionName' + | ||
'expected by the bots active OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid ConnectionName in the TokenExchangeInvokeRequest')); | ||
} | ||
else if (!('exchangeToken' in context.adapter)) { | ||
// Token Exchange not supported in the adapter | ||
await context.sendActivity(this.getTokenExchangeInvokeResponse( | ||
StatusCodes.BAD_GATEWAY, | ||
'The bot\'s BotAdapter does not support token exchange operations. Ensure the bot\'s Adapter supports the ITokenExchangeProvider interface.')); | ||
throw new Error('OAuthPrompt.recognize(): not supported by the current adapter'); | ||
} else { | ||
// No errors. Proceed with token exchange | ||
const extendedUserTokenProvider : ExtendedUserTokenProvider = context.adapter as ExtendedUserTokenProvider; | ||
const tokenExchangeResponse = await extendedUserTokenProvider.exchangeToken(context, this.settings.connectionName, context.activity.from.id, {token: context.activity.value.token}); | ||
if(!tokenExchangeResponse || !tokenExchangeResponse.token) { | ||
await context.sendActivity(this.getTokenExchangeInvokeResponse( | ||
StatusCodes.CONFLICT, | ||
'The bot is unable to exchange token. Proceed with regular login.')); | ||
} else { | ||
await context.sendActivity(this.getTokenExchangeInvokeResponse(StatusCodes.OK, null, context.activity.value.id)); | ||
token = { | ||
channelId: tokenExchangeResponse.channelId, | ||
connectionName: tokenExchangeResponse.connectionName, | ||
token : tokenExchangeResponse.token, | ||
expiration: null | ||
}; | ||
} | ||
} | ||
} else if (context.activity.type === ActivityTypes.Message) { | ||
@@ -338,2 +409,10 @@ const matched: RegExpExecArray = /(\d{6})/.exec(context.activity.text); | ||
private getTokenExchangeInvokeResponse(status: number, failureDetail: string, id?: string): Activity { | ||
const invokeResponse: Partial<Activity> = { | ||
type: 'invokeResponse', | ||
value: { status, body: new TokenExchangeInvokeResponse(id, this.settings.connectionName, failureDetail)} | ||
}; | ||
return invokeResponse as Activity; | ||
} | ||
private static isFromStreamingConnection(activity: Activity): boolean { | ||
@@ -346,13 +425,44 @@ return activity && activity.serviceUrl && !activity.serviceUrl.toLowerCase().startsWith('http'); | ||
return activity.type === ActivityTypes.Event && activity.name === 'tokens/response'; | ||
return activity.type === ActivityTypes.Event && activity.name === tokenResponseEventName; | ||
} | ||
private isTeamsVerificationInvoke(context: TurnContext): boolean { | ||
const activity: Activity = context.activity; | ||
return activity.type === ActivityTypes.Invoke && activity.name === verifyStateOperationName; | ||
} | ||
private isTeamsVerificationInvoke(context: TurnContext): boolean { | ||
private isOAuthCardSupported(context: TurnContext): boolean { | ||
// Azure Bot Service OAuth cards are not supported in the community adapters. Since community adapters | ||
// have a 'name' in them, we cast the adapter to 'any' to check for the name. | ||
const adapter: any = context.adapter; | ||
if (adapter.name) { | ||
switch(adapter.name) { | ||
case 'Facebook Adapter': | ||
case 'Google Hangouts Adapter': | ||
case 'Slack Adapter': | ||
case 'Twilio SMS Adapter': | ||
case 'Web Adapter': | ||
case 'Webex Adapter': | ||
case 'Botkit CMS': | ||
return false; | ||
default: | ||
} | ||
} | ||
return this.channelSupportsOAuthCard(context.activity.channelId); | ||
} | ||
private isTokenExchangeRequestInvoke(context: TurnContext): boolean { | ||
const activity: Activity = context.activity; | ||
return activity.type === ActivityTypes.Invoke && activity.name === 'signin/verifyState'; | ||
return activity.type === ActivityTypes.Invoke && activity.name === tokenExchangeOperationName; | ||
} | ||
private isTokenExchangeRequest(obj: unknown): obj is TokenExchangeInvokeRequest { | ||
if(obj.hasOwnProperty('token')) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
private channelSupportsOAuthCard(channelId: string): boolean { | ||
@@ -359,0 +469,0 @@ switch (channelId) { |
@@ -25,3 +25,3 @@ /** | ||
ToBotFromChannelTokenIssuer: 'https://api.botframework.us' | ||
} | ||
}; | ||
@@ -86,18 +86,18 @@ /** | ||
// Depending on Version, the AppId is either in the | ||
// appid claim (Version 1) or the 'azp' claim (Version 2). | ||
const versionClaim = claims.find(c => c.type === AuthConstants.VersionClaim); | ||
const versionValue = versionClaim && versionClaim.value; | ||
if (!versionValue || versionValue === '1.0') { | ||
// No version or a version of '1.0' means we should look for | ||
// the claim in the 'appid' claim. | ||
const appIdClaim = claims.find(c => c.type === AuthConstants.AppIdClaim); | ||
appId = appIdClaim && appIdClaim.value; | ||
} else if (versionValue === '2.0') { | ||
// Version '2.0' puts the AppId in the 'azp' claim. | ||
const azpClaim = claims.find(c => c.type === AuthConstants.AuthorizedParty); | ||
appId = azpClaim && azpClaim.value; | ||
} | ||
// Depending on Version, the AppId is either in the | ||
// appid claim (Version 1) or the 'azp' claim (Version 2). | ||
const versionClaim = claims.find(c => c.type === AuthConstants.VersionClaim); | ||
const versionValue = versionClaim && versionClaim.value; | ||
if (!versionValue || versionValue === '1.0') { | ||
// No version or a version of '1.0' means we should look for | ||
// the claim in the 'appid' claim. | ||
const appIdClaim = claims.find(c => c.type === AuthConstants.AppIdClaim); | ||
appId = appIdClaim && appIdClaim.value; | ||
} else if (versionValue === '2.0') { | ||
// Version '2.0' puts the AppId in the 'azp' claim. | ||
const azpClaim = claims.find(c => c.type === AuthConstants.AuthorizedParty); | ||
appId = azpClaim && azpClaim.value; | ||
} | ||
return appId; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
840702
294
17367
1
+ Addedbotbuilder-core@4.8.0(transitive)
+ Addedbotframework-schema@4.8.0(transitive)
- Removedbotbuilder-core@4.8.0-preview-109324(transitive)
- Removedbotframework-schema@4.8.0-preview-109324(transitive)
Updatedbotbuilder-core@4.8.0