Пример #1
0
        private async Task <bool> InterceptOAuthCardsAsync(ClaimsIdentity claimsIdentity, Activity activity, CancellationToken cancellationToken)
        {
            var oauthCardAttachment = activity.Attachments?.FirstOrDefault(a => a?.ContentType == OAuthCard.ContentType);

            if (oauthCardAttachment == null)
            {
                return(false);
            }

            var targetSkill = GetCallingSkill(claimsIdentity);

            if (targetSkill == null)
            {
                return(false);
            }

            var oauthCard = ((JObject)oauthCardAttachment.Content).ToObject <OAuthCard>();

            if (string.IsNullOrWhiteSpace(oauthCard?.TokenExchangeResource?.Uri))
            {
                return(false);
            }

            using var tokenClient = await _auth.CreateUserTokenClientAsync(claimsIdentity, cancellationToken).ConfigureAwait(false);

            using var context = new TurnContext(_adapter, activity);
            context.TurnState.Add <IIdentity>(BotAdapter.BotIdentityKey, claimsIdentity);

            // Azure AD token exchange
            try
            {
                var result = await tokenClient.ExchangeTokenAsync(
                    activity.Recipient.Id,
                    _connectionName,
                    activity.ChannelId,
                    new TokenExchangeRequest { Uri = oauthCard.TokenExchangeResource.Uri },
                    cancellationToken).ConfigureAwait(false);

                if (!string.IsNullOrEmpty(result?.Token))
                {
                    // If token above is null, then SSO has failed and hence we return false.
                    // If not, send an invoke to the skill with the token.
                    return(await SendTokenExchangeInvokeToSkill(activity, oauthCard.TokenExchangeResource.Id, result.Token, oauthCard.ConnectionName, targetSkill, default).ConfigureAwait(false));
                }
            }
            catch (InvalidOperationException ex)
            {
                // This will show oauth card if token exchange fails.
                // A common cause for hitting this section of the code is when then user hasn't provided consent to the skill app.
                _logger.LogWarning($"Unable to get SSO token for OAuthCard, exception was {ex}");
            }
            catch (Exception ex)
            {
                // This will show oauth card if token exchange fails.
                _logger.LogInformation($"Unable to get SSO token for OAuthCard, exception was {ex}");
            }

            return(false);
        }
Пример #2
0
        private async Task <bool> InterceptOAuthCards(ClaimsIdentity claimsIdentity, Activity activity, CancellationToken cancellationToken)
        {
            var oauthCardAttachment = activity.Attachments?.FirstOrDefault(a => a?.ContentType == OAuthCard.ContentType);

            if (oauthCardAttachment == null)
            {
                return(false);
            }

            var targetSkill = GetCallingSkill(claimsIdentity);

            if (targetSkill == null)
            {
                return(false);
            }

            var oauthCard = ((JObject)oauthCardAttachment.Content).ToObject <OAuthCard>();

            if (string.IsNullOrWhiteSpace(oauthCard?.TokenExchangeResource?.Uri))
            {
                return(false);
            }

            using var tokenClient = await _auth.CreateUserTokenClientAsync(claimsIdentity, cancellationToken).ConfigureAwait(false);

            using var context = new TurnContext(_adapter, activity);
            context.TurnState.Add <IIdentity>(BotAdapter.BotIdentityKey, claimsIdentity);

            // We need to know what connection name to use for the token exchange so we figure that out here
            var connectionName = targetSkill.Id.Contains(WaterfallSkillBot) || targetSkill.Id.Contains(ComposerSkillBot) ? _configuration.GetSection("SsoConnectionName").Value : _configuration.GetSection("SsoConnectionNameTeams").Value;

            // AAD token exchange
            try
            {
                var result = await tokenClient.ExchangeTokenAsync(
                    activity.Recipient.Id,
                    connectionName,
                    activity.ChannelId,
                    new TokenExchangeRequest { Uri = oauthCard.TokenExchangeResource.Uri },
                    cancellationToken).ConfigureAwait(false);

                if (!string.IsNullOrEmpty(result?.Token))
                {
                    // If token above is null, then SSO has failed and hence we return false.
                    // If not, send an invoke to the skill with the token.
                    return(await SendTokenExchangeInvokeToSkill(activity, oauthCard.TokenExchangeResource.Id, result.Token, oauthCard.ConnectionName, targetSkill, default).ConfigureAwait(false));
                }
            }
            catch (InvalidOperationException ex)
            {
                // Show oauth card if token exchange fails.
                _logger.LogWarning("Unable to exchange token.", ex);
            }

            return(false);
        }
Пример #3
0
        private async Task <bool> InterceptOAuthCardsAsync(ClaimsIdentity claimsIdentity, Activity activity)
        {
            var oauthCardAttachment = activity.Attachments?.FirstOrDefault(a => a?.ContentType == OAuthCard.ContentType);

            if (oauthCardAttachment != null)
            {
                var targetSkill = GetCallingSkill(claimsIdentity);
                if (targetSkill != null)
                {
                    var oauthCard = ((JObject)oauthCardAttachment.Content).ToObject <OAuthCard>();

                    if (!string.IsNullOrWhiteSpace(oauthCard?.TokenExchangeResource?.Uri))
                    {
                        using (var context = new TurnContext(_adapter, activity))
                        {
                            context.TurnState.Add <IIdentity>("BotIdentity", claimsIdentity);

                            // AAD token exchange
                            try
                            {
                                var tokenClient = await _botAuth.CreateUserTokenClientAsync(claimsIdentity, CancellationToken.None).ConfigureAwait(false);

                                var result = await tokenClient.ExchangeTokenAsync(
                                    activity.Recipient.Id,
                                    _connectionName,
                                    activity.ChannelId,
                                    new TokenExchangeRequest()
                                {
                                    Uri = oauthCard.TokenExchangeResource.Uri
                                },
                                    CancellationToken.None).ConfigureAwait(false);

                                if (!string.IsNullOrEmpty(result?.Token))
                                {
                                    // If token above is null, then SSO has failed and hence we return false.
                                    // If not, send an invoke to the skill with the token.
                                    return(await SendTokenExchangeInvokeToSkillAsync(activity, oauthCard.TokenExchangeResource.Id, result.Token, oauthCard.ConnectionName, targetSkill, default).ConfigureAwait(false));
                                }
                            }
#pragma warning disable CA1031 // Do not catch general exception types
                            catch
#pragma warning restore CA1031 // Do not catch general exception types
                            {
                                return(false);
                            }

                            return(false);
                        }
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// The implementation for continue conversation.
        /// </summary>
        /// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
        /// <param name="continuationActivity">The continuation <see cref="Activity"/> used to create the <see cref="ITurnContext" />.</param>
        /// <param name="audience">The audience for the call.</param>
        /// <param name="callback">The method to call for the resulting bot turn.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        protected async Task ProcessProactiveAsync(ClaimsIdentity claimsIdentity, Activity continuationActivity, string audience, BotCallbackHandler callback, CancellationToken cancellationToken)
        {
            _logger.LogInformation($"ProcessProactiveAsync for Conversation Id: {continuationActivity.Conversation.Id}");

            // Create the connector factory and  the inbound request, extracting parameters and then create a connector for outbound requests.
            var connectorFactory = _botFrameworkAuthentication.CreateConnectorFactory(claimsIdentity);

            // Create the connector client to use for outbound requests.
            using (var connectorClient = await connectorFactory.CreateAsync(continuationActivity.ServiceUrl, audience, cancellationToken).ConfigureAwait(false))

                // Create a UserTokenClient instance for the application to use. (For example, in the OAuthPrompt.)
                using (var userTokenClient = await _botFrameworkAuthentication.CreateUserTokenClientAsync(claimsIdentity, cancellationToken).ConfigureAwait(false))

                    // Create a turn context and run the pipeline.
                    using (var context = CreateTurnContext(continuationActivity, claimsIdentity, audience, connectorClient, userTokenClient, callback, connectorFactory))
                    {
                        // Run the pipeline.
                        await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
                    }
        }
        /// <summary>
        /// The implementation for processing an Activity sent to this bot.
        /// </summary>
        /// <param name="authenticateRequestResult">The authentication results for this turn.</param>
        /// <param name="activity">The <see cref="Activity"/> to process.</param>
        /// <param name="callback">The method to call for the resulting bot turn.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>A task that represents the work queued to execute. Containing the InvokeResponse if there is one.</returns>
        protected async Task <InvokeResponse> ProcessActivityAsync(AuthenticateRequestResult authenticateRequestResult, Activity activity, BotCallbackHandler callback, CancellationToken cancellationToken)
        {
            // Set the callerId on the activity.
            activity.CallerId = authenticateRequestResult.CallerId;

            // Create the connector client to use for outbound requests.
            using (var connectorClient = await authenticateRequestResult.ConnectorFactory.CreateAsync(activity.ServiceUrl, authenticateRequestResult.Audience, cancellationToken).ConfigureAwait(false))

                // Create a UserTokenClient instance for the application to use. (For example, it would be used in a sign-in prompt.)
                using (var userTokenClient = await BotFrameworkAuthentication.CreateUserTokenClientAsync(authenticateRequestResult.ClaimsIdentity, cancellationToken).ConfigureAwait(false))

                    // Create a turn context and run the pipeline.
                    using (var context = CreateTurnContext(activity, authenticateRequestResult.ClaimsIdentity, authenticateRequestResult.Audience, connectorClient, userTokenClient, callback, authenticateRequestResult.ConnectorFactory))
                    {
                        // Run the pipeline.
                        await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);

                        // If there are any results they will have been left on the TurnContext.
                        return(ProcessTurnResults(context));
                    }
        }
        /// <inheritdoc/>
        public override async Task CreateConversationAsync(string botAppId, string channelId, string serviceUrl, string audience, ConversationParameters conversationParameters, BotCallbackHandler callback, CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(serviceUrl))
            {
                throw new ArgumentNullException(nameof(serviceUrl));
            }

            _ = conversationParameters ?? throw new ArgumentNullException(nameof(conversationParameters));
            _ = callback ?? throw new ArgumentNullException(nameof(callback));

            Logger.LogInformation($"CreateConversationAsync for channel: {channelId}");

            // Create a ClaimsIdentity, to create the connector and for adding to the turn context.
            var claimsIdentity = CreateClaimsIdentity(botAppId);

            claimsIdentity.AddClaim(new Claim(AuthenticationConstants.ServiceUrlClaim, serviceUrl));

            // Create the connector factory.
            var connectorFactory = BotFrameworkAuthentication.CreateConnectorFactory(claimsIdentity);

            // Create the connector client to use for outbound requests.
            using (var connectorClient = await connectorFactory.CreateAsync(serviceUrl, audience, cancellationToken).ConfigureAwait(false))
            {
                // Make the actual create conversation call using the connector.
                var createConversationResult = await connectorClient.Conversations.CreateConversationAsync(conversationParameters, cancellationToken).ConfigureAwait(false);

                // Create the create activity to communicate the results to the application.
                var createActivity = CreateCreateActivity(createConversationResult, channelId, serviceUrl, conversationParameters);

                // Create a UserTokenClient instance for the application to use. (For example, in the OAuthPrompt.)
                using (var userTokenClient = await BotFrameworkAuthentication.CreateUserTokenClientAsync(claimsIdentity, cancellationToken).ConfigureAwait(false))

                    // Create a turn context and run the pipeline.
                    using (var context = CreateTurnContext(createActivity, claimsIdentity, null, connectorClient, userTokenClient, callback, connectorFactory))
                    {
                        // Run the pipeline.
                        await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
                    }
            }
        }
        public override async Task RouteActivityToExistingHandoff(ITurnContext turnContext, HandoffRecord handoffRecord)
        {
            var serviceNowHandoffRecord = handoffRecord as ServiceNowHandoffRecord;

            // Retrieve an oAuth token for ServiceNow which we'll pass on this turn
            var claimsIdentity  = (ClaimsIdentity)turnContext.TurnState.Get <IIdentity>(BotFrameworkAdapter.BotIdentityKey);
            var userTokenClient = await _botFrameworkAuth.CreateUserTokenClientAsync(claimsIdentity, default(CancellationToken));

            var tokenResponse = await userTokenClient.GetUserTokenAsync(turnContext.Activity.From.Id, _creds.ServiceNowAuthConnectionName, null, null, default(CancellationToken));

            if (tokenResponse != null)
            {
                if (serviceNowHandoffRecord != null)
                {
                    var message = ServiceNowConnector.MakeServiceNowMessage(0,
                                                                            serviceNowHandoffRecord.RemoteConversationId,
                                                                            turnContext.Activity.Text,
                                                                            serviceNowHandoffRecord.ConversationRecord.Timezone,
                                                                            turnContext.Activity.Locale,
                                                                            serviceNowHandoffRecord.ConversationRecord.UserId,
                                                                            serviceNowHandoffRecord.ConversationRecord.EmailId);

                    await ServiceNowConnector.SendMessageToConversationAsync(
                        serviceNowHandoffRecord.ConversationRecord.ServiceNowTenant,
                        tokenResponse.Token,
                        message).ConfigureAwait(false);

                    var traceActivity = Activity.CreateTraceActivity("ServiceNowVirtualAgent", label: "ServiceNowHandoff->Activity forwarded to ServiceNow");
                    await turnContext.SendActivityAsync(traceActivity);
                }
            }
            else
            {
                var traceActivity = Activity.CreateTraceActivity("ServiceNowVirtualAgent", label: "ServiceNowHandoff->No ServiceNow authentication token available.");
                await turnContext.SendActivityAsync(traceActivity);

                throw new Exception("No ServiceNow authentication token available for this user");
            }
        }
Пример #8
0
 /// <inheritdoc/>
 public override Task <UserTokenClient> CreateUserTokenClientAsync(ClaimsIdentity claimsIdentity, CancellationToken cancellationToken)
 {
     return(_inner.CreateUserTokenClientAsync(claimsIdentity, cancellationToken));
 }
Пример #9
0
        public override async Task RouteActivityToExistingHandoff(ITurnContext turnContext, HandoffRecord handoffRecord)
        {
            var serviceNowHandoffRecord = handoffRecord as ServiceNowHandoffRecord;

            // Retrieve an oAuth token for ServiceNow which we'll pass on this turn
            var claimsIdentity  = (ClaimsIdentity)turnContext.TurnState.Get <IIdentity>(BotFrameworkAdapter.BotIdentityKey);
            var userTokenClient = await _botFrameworkAuth.CreateUserTokenClientAsync(claimsIdentity, default(CancellationToken));

            var tokenResponse = await userTokenClient.GetUserTokenAsync(turnContext.Activity.From.Id, _creds.ServiceNowAuthConnectionName, null, null, default(CancellationToken));

            if (tokenResponse != null)
            {
                if (serviceNowHandoffRecord != null)
                {
                    var messageText = turnContext.Activity.Text;

                    // If the incoming activity has a value, it may be a response from an Adaptive Card
                    // which we need to process accordingly.
                    if (turnContext.Activity.Value != null)
                    {
                        try
                        {
                            var activityValue = JObject.Parse(turnContext.Activity.Value.ToString());

                            if (activityValue.ContainsKey("dateVal") && activityValue.ContainsKey("timeVal"))
                            {
                                var dateTimeStr = $"{activityValue["dateVal"]} {activityValue["timeVal"]}";
                                if (DateTime.TryParse(dateTimeStr, out DateTime dateTime))
                                {
                                    var baseDate = new DateTime(1970, 1, 1);
                                    var diff     = dateTime - baseDate;
                                    messageText = diff.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
                                }
                            }
                        }
                        catch (JsonReaderException ex)
                        {
                            // Value is not valid Json so continue
                        }
                    }

                    var message = ServiceNowConnector.MakeServiceNowMessage(0,
                                                                            serviceNowHandoffRecord.RemoteConversationId,
                                                                            messageText,
                                                                            serviceNowHandoffRecord.ConversationRecord.Timezone,
                                                                            serviceNowHandoffRecord.ConversationRecord.UserId,
                                                                            serviceNowHandoffRecord.ConversationRecord.EmailId);

                    await ServiceNowConnector.SendMessageToConversationAsync(
                        serviceNowHandoffRecord.ConversationRecord.ServiceNowTenant,
                        tokenResponse.Token,
                        message).ConfigureAwait(false);

                    var traceActivity = Activity.CreateTraceActivity("ServiceNowVirtualAgent", label: "ServiceNowHandoff->Activity forwarded to ServiceNow");
                    await turnContext.SendActivityAsync(traceActivity);
                }
            }
            else
            {
                var traceActivity = Activity.CreateTraceActivity("ServiceNowVirtualAgent", label: "ServiceNowHandoff->No ServiceNow authentication token available.");
                await turnContext.SendActivityAsync(traceActivity);

                throw new Exception("No ServiceNow authentication token available for this user");
            }
        }