Integrating Bot with Dynamics CRM (OAuth 2.0 Authentication)

Let us continue with our previous posts on understanding and implementing a simple bot that interact with Dynamics CRM using Microsoft Bot Framework

Till now we had hard coded our connection to CRM inside the bot application which was used to create lead records in CRM.

In this post, we will use OAuth2 authentication to connect to CRM Service (Web API).

We’d update our bot to use Sign-In Card. It will launch a web browser (web site which redirects user to authenticate to office 365) where user will enter the credentials and on successful authentication it will get the authentication token which it would then use to interact with CRM.

Here we would be using Web Site deployed in Azure that takes care of all the plumbing part.

We will be using Bot State Service here for saving Bot State. User can save bot state in this bot state service and can retrieve it. So, we would be passing the user id to the web site hosted to the azure and after we get the authentication token on successful authorization, we save this information in the bot in the bot state using SetUserData method. Back in our Bot app we will retrieve this authentication token saved in session state using GetUserData method and use it for interacting with CRM Web API.

Let us first create a ASP.NET Web Application which would be use for redirecting the user to authentication and saves the authentication token to the Bot State Service.

This creates our Web Application.

Add the following Microsoft.Bot.Builder Nuget Package in the project.


Also add a View named Authorize, which we will use are redirect URI for our Dynamics 365 App that will be registered to Azure Active Directory.


Before we start writing the code in our controller, we need to register dynamics 365 app with Azure Active Directory.

Follow the below post for that.

https://nishantrana.me/2016/11/13/register-a-dynamics-365-app-with-azure-active-directory/

Now we have our required values i.e. client id, client key and end point URL

Add the following keys in web.config.


Here Client Id, Client Secret and EndPoint Url are the one we got when we registered our Dynamics 365 App. Here Microsoft App Id and Password are for our Bot Application.

https://dev.botframework.com

Update the HomeController.cs and add below action methods Login and Authorize.

</p>
using Microsoft.Bot.Connector;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Configuration;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace AzureAuthWebApplication.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Login(string userid)
{
// string userid in session
Session["botuserid"] = userid;
// CRM Url
string Resource = "https://nishutrial.crm.dynamics.com";

AuthenticationContext authContext = new AuthenticationContext(ConfigurationManager.AppSettings["Authority"]);
var authUri = authContext.GetAuthorizationRequestUrlAsync(Resource, ConfigurationManager.AppSettings["ClientId"],
new Uri(ConfigurationManager.AppSettings["RedirectUri"]), UserIdentifier.AnyUser, null);
return Redirect(authUri.Result.ToString());
}

public async Task<ActionResult> Authorize(string code)
{
AuthenticationContext authContext = new AuthenticationContext(ConfigurationManager.AppSettings["Authority"]);
var authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(ConfigurationManager.AppSettings["RedirectUri"]),
new ClientCredential(ConfigurationManager.AppSettings["ClientId"],
ConfigurationManager.AppSettings["ClientSecret"]));

// Saving token in Bot State
var botCredentials = new MicrosoftAppCredentials(ConfigurationManager.AppSettings["MicrosoftAppId"],
ConfigurationManager.AppSettings["MicrosoftAppPassword"]);
var stateClient = new StateClient(botCredentials);
BotState botState = new BotState(stateClient);
BotData botData = new BotData(eTag: "*");
botData.SetProperty<string>("AccessToken", authResult.AccessToken);

// webchat is the channel id. Make sure it is same in the bot application when we get the user data
await stateClient.BotState.SetUserDataAsync("webchat", Session["botuserid"].ToString(), botData);
ViewBag.Message = "Your Token -" + authResult.AccessToken + " User Id - " + Session["botuserid"].ToString();
return View();
}

public ActionResult About()
{
ViewBag.Message = "Your application description page.";

return View();
}

public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";

return View();
}
}
}
<p style="text-align: justify;">

Publish the Web Application to Azure.

Now let us go back to our Bot Application and update the messagecontroller.cs class.

</p>
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Builder.FormFlow;
using Bot_Application1.Models;
using System;
using Bot_Application1.Dialogs;
using System.Collections.Generic;
using System.Web;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Bot_Application1
{
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
if (activity.Text.ToUpper() == "LOGIN")
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
Activity replyToConversation = activity.CreateReply();
replyToConversation.Recipient = activity.From;
replyToConversation.Type = "message";
replyToConversation.Attachments = new List<Attachment>();

List<CardAction> cardButtons = new List<CardAction>();
CardAction plButton = new CardAction()
{
// ASP.NET Web Application Hosted in Azure
// Pass the user id
Value = "http://azureauthwebapplication20170421122618.azurewebsites.net/Home/Login?userid=" + HttpUtility.UrlEncode(activity.From.Id),
Type = "signin",
Title = "Connect"
};

cardButtons.Add(plButton);

SigninCard plCard = new SigninCard("Please login to Office 365", new List<CardAction>() { plButton });
Attachment plAttachment = plCard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
var reply = await connector.Conversations.SendToConversationAsync(replyToConversation);
}
else if (activity.Text.ToUpper() == "GETUSERS")
{
// Get access token from bot state
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
StateClient stateClient = activity.GetStateClient();
BotState botState = new BotState(stateClient);
BotData botData = await botState.GetUserDataAsync(activity.ChannelId, activity.From.Id);
string token = botData.GetProperty<string>("AccessToken");

var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

var retrieveResponse =
await httpClient.GetAsync("https://nishutrial.crm.dynamics.com/api/data/v8.1/systemusers?$select=fullname");
if (retrieveResponse.IsSuccessStatusCode)
{
var jRetrieveResponse =
JObject.Parse(retrieveResponse.Content.ReadAsStringAsync().Result);

dynamic systemUserObject = JsonConvert.DeserializeObject(jRetrieveResponse.ToString());

foreach (var data in systemUserObject.value)
{
Activity jsonReply = activity.CreateReply($"System User = {data.fullname.Value}");
await connector.Conversations.ReplyToActivityAsync(jsonReply);
}
}
else
{
Activity reply = activity.CreateReply("Failed to get users.\n\nPlease type \"login\" before you get users.");
await connector.Conversations.ReplyToActivityAsync(reply);
}
}
else
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
Activity reply = activity.CreateReply("# CRM BOT Instructions \n\nlogin --> Login to Office 365\n\ngetusers --> Get all System Users in CRM");
await connector.Conversations.ReplyToActivityAsync(reply);
}
}
else
{
HandleSystemMessage(activity);
}

var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}

private IDialog<LeadModel> MakeLuisDialog()
{
return Chain.From(() => new LUISDialog(LeadModel.BuildForm));
}

internal static IDialog<LeadModel> MakeRootDialog()
{
return Chain.From(() => FormDialog.FromForm(LeadModel.BuildForm));
}

private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}

return null;
}
}
}
<p style="text-align: justify;">

Publish the Bot to Azure.

Now let us test the Bot.

Go to – https://dev.botframework.com/bots

Open the Bot and click on Test.

Let us start the Chat.

On typing login the bot presents User with the Sign In Card. Click on Connect.

Sign in with your credentials.

Give permission to the app.

On successful sign-in –

Now type in getusers

It brings us all the System Users full name from our CRM Organization.

The extremely informative posts from which I learned about it

https://blogs.msdn.microsoft.com/tsmatsuz/2016/09/06/microsoft-bot-framework-bot-with-authentication-and-signin-login/

https://debajmecrm.com/2016/02/29/knowhow-how-to-execute-web-api-calls-to-microsoft-dynamics-crm-from-an-external-asp-net-web-application/

and following pluralsight training that helped in understanding OAuth and JWT concept.

https://www.pluralsight.com/courses/oauth2-json-web-tokens-openid-connect-introduction

Hope it helps..

Publishing Bot to Facebook Messenger

Let us continue with our previous posts on using Microsoft Bot Framework for writing a simple bot.

In our previous post, we published the bot app to Azure and also tested using Skype which is already configured.

In this post, we will be deploying the Bot to Facebook Messenger.

Sign in to Bot Developer Framework site

https://dev.botframework.com/

and open the bot application deployed.

Scroll down and we can see Facebook Messenger as one of the Channel available.

Click on Add.

Here we can see the guidelines and all the steps we need to do follow to configure the Facebook messenger

As a first step, we need to create a Facebook page for the bot

Click on the link and create a Facebook page

https://www.facebook.com/bookmarks/pages

Next step is to create a Facebook App for the bot

Click on the below link to create the Facebook app.

https://developers.facebook.com/quickstarts/?platform=web

Next step is to copy App ID and App Secret

Go to App Dashboard and copy these values. These values be used in the last step where we need to enter the credentials to authorize the app.

Next step is to enable the Messenger

Go to Dashboard and select Add Product.

Click on Get Started for Messenger.

Select the page from the dropdown and copy the token generated.

Next step is to set the Web Hook

Go to Messenger – Settings in Facebook App Dashboard.

And click on Setup
Webhooks.

Go back to Configure Facebook Messenger page and copy the url and token from there.

Copy these values and paste it to New Page Subscription. Check the required subscription fields and click on Verify and Save.

It will show the status as complete on successful verification.

Now as a last step we need to enter our credentials

  • Facebook Page Id –

i.e. 403898986632435

All other values we had already copied earlier. So just passt those values.

And Click on Resubmit.

Once the credentials are validated. Click on “I’m done with configuring Facebook Messenger”.

Click on Message Us to start the conversation.

Hope it helps..

Publishing Bot to Azure and adding it to Skype (Microsoft Bot Framework)

Let us continue with our previous posts on using Microsoft Bot Framework to create a simple bot application that creates a lead in CRM.

In this post, we will be publishing our Bot application to Azure and also test it on Skype. Skype is one of the channel already configured for us.

Open the Azure Portal (Create a free trial if you do not have an account)

https://portal.azure.com

Right click on the application and select Publish.

Select Microsoft Azure App Service as the publish target.

Create the app service

Validate the connection and on successful connection click on Publish.

The published Bot Application –

Now to register our bot go to bot framework developer site

https://dev.botframework.com/

Sign in and Click on Register a bot

Enter all the required details.

Here URL will be the Destination URL of Azure where the Bot Application was published.

Click on Manage Microsoft App ID and Password

Click on Generate an Microsoft App ID and password.

Save the password and app id which will be used for configuration later.

Enter the App Id and click on Register to register the Bot.

We will get the message “Bot created” on successful registration.

Back in our bot application, open its web.config and specify value for app id and password and republish the app.

Once it is published successfully, inside our bot we can click on Test to check the connection.

We’d get the message “Endpoint authorization succeeded”.

Scrolling down we can see two channels web and skype already configured.

Click on Add to Skype to add the bot as a contact to skype.

Click on “Add to Contacts”

Sign in with your Skype Credentials.

Launch Skype.

We can see the Bot added to our contacts.

This is how we can easily publish the Bot to azure and add it to Skype.

Hope it helps..

Step by step – Upload files to Azure Blob storage

Here we will be creating a storage account of type blob storage and a container inside it. Then we will create a console application, add required nuget packages and upload a file to the container.

Log in to Azure Portal

https://portal.azure.com

Click on Add to add a new storage account.

Create a new container in it to store the blob files

Now we’d write a console app to connect to this container and upload a file.

Create a new console application and add references to below Nuget Packages.

  • Windows.Azure.Storage
  • Windows.Azure.ConfigurationManager

In Azure Portal – Storage Account, go to Access Keys and copy the connection strings for the storage account.

Inside console application add an appSettings section and add a key and paste the above copied connection string there.

The source code to upload the Blob file

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

// Retrieve a reference to a container.
CloudBlobContainer container = blobClient.GetContainerReference("myblogcontainer");

// Retrieve reference to a blob named "myblob".
CloudBlockBlob blockBlob = container.GetBlockBlobReference("WeekendChamps.jpg");

// Create or overwrite the "myblob" blob with contents from a local file.
using (var fileStream = System.IO.File.OpenRead(@"C:\Users\Bliss\Downloads\WeekendChamps.jpg"))
{
blockBlob.UploadFromStream(fileStream);
}

The file uploaded in the container

Hope it helps..

List of all blog posts on CRM and Azure Integration


Configure Product Recommendations using Recommendations API in Dynamics 365

For configuring Product Recommendations first we need to enable the preview –

Go to Settings – Administration – System Settings – Previews

Select Yes and Click on OK.

As a next step, we need to create Cognitive Services for Recommendation API and connect it to CRM.

Go to Portal

https://portal.azure.com

Search and select Cognitive Services Accounts

Click on Add

Select Recommendation API and provide other details and click on Create.

From Overview, note down the Endpoint which will be used to configure the connection in CRM.

From Keys, copy value of the Key which will be used for configuring connection to CRM.

In CRM, go to Settings – Administration and click on Azure Machine Learning Recommendation Service Configuration.

Specify the value for the URL and Key we had noted down earlier, save the record and click on Test Connection to test the connection.

On Successful connection, we’d see Success message for Last Connection Status. Click on Activate to enable the connection.

Now we need to define\build the model for recommendations. For this go to Settings – Product Catalog and click on Product Recommendations.

We’d see a recommendation model with default values

The model will have Basked Data Entities already defined. We can edit\add new\ delete these existing configuration records for Basked Data Entities. Basked Data Entities recommendations are based on which products appear together.

For e.g. in below Opportunity record we can see 27 inch and 12 inch monitor opportunity products appearing together. It will look for all such line items records.

Similarly we have recommendation entities records configured which we can update.

Once done with the configuration, we need to click on Build Model Version to build the model.

It will create a corresponding model version record.

We can check its progress by refreshing it.

Here the model that we had defined has successfully build and it took around 6 minutes.

We can click on the Model Version to open the record to get the further details.

Next step would be to check the recommendations. For this click on Test Recommendations.

Pop up opens wherein we can select the Products and model version and click on Show Result to see what are the product recommendations.

Once satisfied with the test result, click on Activate to enable the recommendations.

We might get the below message in case we do not have good enough data in our system.

To see it in action, open an existing opportunity record , go to product sub grid and select a product and select Suggest Products.

A dialog box opens up that shows the Cross-Sell products and other details.

Get all the details here

https://technet.microsoft.com/library/56b35229-72f8-46ca-bebf-eae023f633c2.aspx

Hope it helps..