/**
 * Copyright(c) Microsoft Corporation.All rights reserved.
 * Licensed under the MIT License.
 */ import { Activity , ActivityHandlerBase , ActivityTypes , BotAdapter , ExtendedUserTokenProvider , ResourceResponse , TurnContext , SkillConversationReference , InvokeResponse , SkillConversationIdFactoryBase } from 'botbuilder-core' ; import { AuthenticationConfiguration , ClaimsIdentity , JwtTokenValidation , SimpleCredentialProvider } from 'botframework-connector' ; import { ITokenExchangeConfig } from './' ; import { ActivityEx , SkillsConfiguration , IEnhancedBotFrameworkSkill } from 'bot-solutions' ; import { SkillHandler , SkillHttpClient , BotFrameworkSkill , BotFrameworkAdapter } from 'botbuilder' ; import { tokenExchangeOperationName } from 'botbuilder-core' ; import { OAuthCard , Attachment } from 'botframework-schema' ; import { uuid } from '../utils' ; import { IBotSettings } from '../services' ; export class TokenExchangeSkillHandler extends SkillHandler { private readonly tokenExchangeSkillHandlerAdapter : BotAdapter ; private readonly tokenExchangeProvider : ExtendedUserTokenProvider ; private readonly tokenExchangeConfig : ITokenExchangeConfig ; private readonly skillsConfig : SkillsConfiguration ; private readonly skillClient : SkillHttpClient ; private readonly botId : string ; private readonly tokenExchangeSkillHandlerConversationIdFactory : SkillConversationIdFactoryBase ; private readonly oAuthCardContentType : string = 'application/vnd.microsoft.card.oauth' ; public constructor ( adapter , bot , configuration , conversationIdFactory , skillsConfig , skillClient , credentialProvider , authConfig , tokenExchangeConfig , channelService = '' ) { super ( adapter , bot , conversationIdFactory , credentialProvider , authConfig , channelService ) ; this . tokenExchangeSkillHandlerAdapter = adapter ; this . tokenExchangeProvider = adapter as BotFrameworkAdapter ; this . tokenExchangeConfig = tokenExchangeConfig ; this . skillsConfig = skillsConfig ; this . skillClient = skillClient ; this . tokenExchangeSkillHandlerConversationIdFactory = conversationIdFactory ; this . botId = configuration . microsoftAppId ; } protected async onSendToConversation ( claimsIdentity , conversationId , activity ) { if ( this . tokenExchangeConfig !== undefined && await this . interceptOAuthCards ( claimsIdentity , activity ) ) { return { id : uuid ( ) . toString ( ) } ; } return await super . onSendToConversation ( claimsIdentity , conversationId , activity ) ; } protected async onReplyToActivity ( claimsIdentity , conversationId , activityId , activity ) { if ( this . tokenExchangeConfig !== undefined && await this . interceptOAuthCards ( claimsIdentity , activity ) ) { return { id : uuid ( ) . toString ( ) } ; } return await super . onReplyToActivity ( claimsIdentity , conversationId , activityId , activity ) ; } private getCallingSkill ( claimsIdentity ) { const appId = JwtTokenValidation . getAppIdFromClaims ( claimsIdentity . claims ) ; if ( appId === undefined || appId . trim ( ) . length === 0 ) { return undefined ; } return Array . from ( this . skillsConfig . skills . values ( ) ) . find ( ( s ) => { return s . appId . toLowerCase ( ) === appId . toLowerCase ( ) ; } ) ; } private async interceptOAuthCards ( claimsIdentity , activity ) { if ( activity . attachments !== undefined ) { let targetSkill ; activity . attachments . filter ( a => a . contentType === this . oAuthCardContentType ) . forEach ( async ( attachment ) => { if ( targetSkill === undefined ) { targetSkill = this . getCallingSkill ( claimsIdentity ) ; } if ( targetSkill !== undefined ) { const oauthCard = attachment . content ; if ( oauthCard !== undefined && oauthCard . tokenExchangeResource !== undefined && this . tokenExchangeConfig !== undefined && this . tokenExchangeConfig . provider !== undefined && this . tokenExchangeConfig . provider . trim ( ) . length > 0 && this . tokenExchangeConfig . provider === oauthCard . tokenExchangeResource . providerId ) { const context = new TurnContext ( this . tokenExchangeSkillHandlerAdapter , activity ) ; context . turnState . set ( this . tokenExchangeSkillHandlerAdapter . BotIdentityKey , claimsIdentity ) ; const result = await this . tokenExchangeProvider . exchangeToken ( context , this . tokenExchangeConfig . connectionName , activity . recipient ?. id , { uri : oauthCard . tokenExchangeResource . uri } ) ; if ( result . token !== undefined && result . token . trim ( ) . length > 0 ) { return await this . sendTokenExchangeInvokeToSkill ( activity , oauthCard . tokenExchangeResource . id as string , result . token , oauthCard . connectionName , targetSkill ) ; } return false ; } } } ) ; } return false ; } private async sendTokenExchangeInvokeToSkill ( incomingActivity , id , token , connectionName , targetSkill ) { const activity = ActivityEx . createReply ( incomingActivity ) ; activity . type = ActivityTypes . Invoke ; activity . name = tokenExchangeOperationName ; activity . value = { id : id , token : token , connectionName : connectionName } ; const conversationReference = await this . tokenExchangeSkillHandlerConversationIdFactory . getSkillConversationReference ( incomingActivity . conversation . id ) ; activity . conversation = conversationReference . conversationReference . conversation ; const response = await this . skillClient . postActivity ( this . botId , targetSkill . appId , targetSkill . skillEndpoint , this . skillsConfig . skillHostEndpoint , activity . conversation . id , activity ) ; return response . status >= 200 && response . status <= 299 ; } }