OAuth 2.0 with Dynamics 365 CE Web API

OAuth can be defined as delegation or authorization protocol.

The resource owner who owns the resource is allowing an application to access that resource on their behalf without impersonating the resource owner.

The application first requests for the authorization from the resource owner and receives the token using which it can access the resource.

Here the token specifies the delegated rights that the client application will have.

Below are the main components that are involved in OAuth.

Resource Owner

In most of the cases it is the user who has access and can delegate access to the Web API. In Dynamics CRM context it can be the CRM User.

Protected Resource

Web API of Dynamics 365 CE

Authorization Server

Trusted by protected resource (Dynamics CRM) to issue Access Tokens to Clients, which the client can then use to access protected resource (Web API). Azure Active Directory (Azure AD) in this case. Resource Owner authenticates to the Authorization server, so the credentials are not exposed to the client.

Client

3rd party application that wants to use the Dynamics 365 Web API on behalf of the Resource Owner (CRM User).

There are several different kinds of grant type or flow that exist in OAuth for getting the access token.

  • OAuth 2.0 Authorization Grant Type

The below image wonderfully explains it in the context of Dynamic 365 Customer Engagement.

  • The resource owner (CRM User) opens the Client Application in the User Agent (Browser)
  • Client Application asks the resource owner to authorize at the authorization server.
  • Resource Owner authenticates and grants authorization to the Client. Authorization server gives authorization code to the Client.
  • Client uses this authorization code along with its own credentials to request access token from authorization server.
  • Client receives the access token that it uses to use the protected resource i.e. Web API.

As the user’s authenticates using his credentials with the authorization server, this information is not accessible to the client application, thus protecting user from sharing the credentials with the Client Application. Along with Access Token, we have Scope in OAuth 2.0 basically set of rights for the protected resource, which provides the mechanism for limiting the access granted to the client application. Also Refresh Token which is to be used when the access token has expired or stopped working. Client asks for the Refresh token from the Authorization Server and then uses this new token to access the protected resource.

Check out the below blog post for further details

https://nishantrana.me/2019/08/25/connect-to-dynamics-365-web-api-using-oauth-2-0-authorization-code-grant-type/

  • Implicit Grant Type

Implicit Grant Type is for the client applications that can’t keep any secrets from the browser, a JavaScript Application running completely inside the browser. So instead of authorization code, in implicit grant, the authorization server returns the access token.

Few limitations of this flow are first it can’t keep the secret as it will be available to the browser, also this flow cannot be used to get the refresh token as the session is limited to the browser context.

  • The client sends the request with request_type as token (instead of code in case of Authorization Grant) to the Authorization Endpoint of the Authorization Server.
  • The resource owner authenticates and authorizes the client.
  • The authorization server generates the token, attaching it to URI fragment of the response.

https://nishantrana.me/2019/08/27/connect-to-dynamics-365-web-api-using-oauth-2-0-implicit-grant-type-through-postman/

https://nishantrana.me/2019/08/28/connect-to-dynamics-365-web-api-using-oauth-2-0-implicit-grant-type-through-single-page-apps/

  • Client Credentials Grant Type

Client Credentials Grant Type is for scenario when we do not have any explicit resource owner (or user) like a back-end system or CRON job running on the server. As there is no user involved to delegate the authorization, client acts as the resource owner.

  1. The client application requests the access token directly from the token endpoint of the Authorization Server with grant_type as client_credentials.
  2. The authorization server issues the access token to the client.

https://nishantrana.me/2019/08/24/connect-to-dynamics-365-web-api-using-oauth-2-0-client-credentials/

  • Resource owner credentials grant type
  1. The client application prompts Resource Owner for username and password.
  2. Client application uses this information to request access token (with grant_type as password) from the Token Endpoint of the Authorization Server.

Here the disadvantages are that the credentials are exposed to the client application, and also the same is passed in plain text to the authorization server. This grant type is only recommended to be used for the legacy applications to bring them into the OAuth world, wherein they can work with access token, scopes etc. One good thing about this flow is that the password is exchanged only first time when requesting the access tokens, instead of every request.

https://nishantrana.me/2019/08/23/connect-to-dynamics-365-web-api-using-oauth-2-0-resource-owner-password-credential-ropc/

Decision Tree for the choosing the right grant type

Recommend read –

https://auth0.com/docs/api-auth/which-oauth-flow-to-use

https://docs.microsoft.com/en-us/azure/active-directory/develop/authentication-flows-app-scenarios#scenarios-and-supported-authentication-flows

https://debajmecrm.com/?s=oauth

Hope it helps..

Connect to Dynamics 365 Web API using OAuth 2.0 – Implicit Grant Type (through Single Page Apps)

In previous post we saw how to connect to Dynamics 365 Web API using Postman and Implicit Grant type, in this post we will be creating a single page html application and will use ADAL library in our JavaScript to call Web API using Implicit Grant Type.

While writing the single page application, we need to consider CORS. CORS – Cross-Origin Resource Sharing basically enables us to access resources that are in different domain and here the CORS support is only enabled for the Web API and not for the Organization Service. The Azure Active Directory Authentication Library (adal.js) that we will be using in our Single Page App will takes cares of all the CORS complexity for us.

To get started,

Create a new ASP.NET Web Application of type Empty Project.

Add an HTML page to it.

Add the reference to ADAL JS i.e. Azure Active Directory Library for JavaScript, which allows client application to get tokens on behalf of users using OAuth 2.0 Implicit Flow.


To make example easy to understand, let us add just 3 buttons and 2 DIV


Before we proceed further, login to Azure Portal and register the client application.

Copy the Client Id.

Specify the URL of the page that we are working on as the REDIRECT URI and also enable Access Tokens and ID Tokens required for implicit grant to work.


Back in our html page,

First step is to get the AuthenticationContext object.

To get the authentication context we can use the below line of code


Below is how we will specify the configuration details required by the AuthenticationContext.


  • postLogoutRedirectUri – ADAL redirects the user to postLogoutRedirectUri after logout. Defaults is ‘redirectUri’.
  • cacheLocation – ADAL caches tokens in the browser storage which defaults to ‘sessionStorage’. You can set this to either ‘localStorage’ or ‘sessionStorage’.

Read about all the configuration options here

https://github.com/AzureAD/azure-activedirectory-library-for-js/wiki/Config-authentication-context

Login and Logout methods are straightforward à

Login method will redirect the user to a new page for entering the credentials. If we want to remain in the same page and use popup instead, we can specify it in the popUp property of config.

To get the user details use the GetCachedUser() method of Authentication Context. The sign-in flow not only gets the token but also authenticates the user with Azure AD. The user.profile property contains the claims about the user.

User Profile è

AcquireToken method of AuthenticationContext to get the token and make the request

The getUserDetails method

The basic flow of the application à

Click on Login to authenticate. The message div indicates that there is no user information present in the cache.

Enter the credentials

This redirects back to the page, showing logout button and Get User GUID button and hiding login button. It also shows the Name of the user fetched from the profile.

user.profile.name

Click on Get User GUID to call the WhoAmI request and display the GUID of the user.


Finally click on Logout and select the account to sign out of.

On successful sign out we will be redirected to our page.

Back in our Fiddler below is the Request being passed to the Authorization end point.

response_type value of id_token value has been added by OpenID Connect. OpenID connect extends OAuth 2.0, by adding the authentication layer to it, by allowing the verification of the identity of the end user as well as to obtain basic information about the end user.

More on OpenId  – https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660

Source code 

Hope it helps..


Connect to Dynamics 365 Web API using OAuth 2.0 – Implicit Grant Type (through Postman)

In the previous post we covered below grant type

here we’d be looking at the Implicit Grant Type.

Implicit Grant Type is for the “Public Clients”, client application that cannot keep the Client Secret, HTML or Angular app that communicates from the browser (through JavaScript) and have no server involved, therefore used for Single Page Application (SPA).

Instead of getting the authorization code from the Authorization Server like in case of Authorization Grant and then using the authorization code (along with Client Secret) to get the access token. In case of Implicit Grant, the client application directly requests for the access token from the Authorization Endpoint.

We need to pass the below details

response_type token
client_id Application ID
redirect_uri http://%5Blocalhost%5D

to the Authorization URL à

https://login.microsoftonline.com/[tenantid]/oauth2/authorize

The redirect_uri must match against the one registered, this way the Authorization Server, makes sure that there are no unauthorized client applications requesting the token.

Some of the drawbacks are that the Access Tokens are exposed to resource owner in the URL and also there is no validation that the access token is meant for that particular client.

To get started à

Register your application with the Azure Active Directory tenant. Copy the Client Id.

For Redirect URI we will set the URL of the single page application which we will be developing later. So specify any valid URL there.

Enable the application for the Implicit Flow by setting oauth2AllowImplicitFlow as true from Manifest of the application.


Or from the Authentication section.


From Postman à

Go to Authorization tab and click on Get New Access Token button


Specify Grant Type as implicit, along with CallBack Url i.e. redirect_uri and the client id.

Here for the Auth URL, we should have the resource query parameter specified in the Authorization Endpoint which refers to our Dynamics CE Organization.

https://login.microsoftonline.com/bd88124a-ddca-4a9e-bd25-f11bdefb3f18/oauth2/authorize?resource=https://[org].crm.dynamics.com


Click on Request Token to get the access token.

Inside Fiddler: We’d see the following parameter being passed to the authorization endpoint.


Clicking on Request Token will open the popup for us to login and provide the consent.


The access token à


Let us try changing the Callback Url and send the request again


We’d get the below error


As was mentioned earlier –

The redirect_uri must match against the one registered in the application, this way the Authorization Server, makes sure that there are no unauthorized client applications requesting the token.

Hope it helps..

Fix – AADSTS700054: response_type ‘id_token’ is not enabled for the application -Dynamics 365 CE (Implicit Grant)

If we have not enabled the application to request an id_token directly from the Authorization Endpoint (while writing Single Page Application that uses Implicit Flow Grant), we would get the below error à


To fix it, go to Manifest of the application registered and set oauth2AllowIdTokenImplicitFlow to true.


Another option of setting this property is through Authentication à Advanced Settings section of the Application.


Similarly, if the application is expecting Access tokens i.e. token and if that is not enabled, we will get the below error

AADSTS700051: response_type ‘token’ is not enabled for the application.

To fix it à

Set oauth2AllowImplicitFlow: true



Hope it helps.


Connect to Dynamics 365 Web API using OAuth 2.0 – Authorization Code Grant Type

In the previous post we covered Password and Client Credentials grant type, here we’d be looking at the Authorization Code Grant Type.

The Authorization Code Grant Type is for the Confidential Clients i.e. basically for the server side web applications that are written in server side language and source code is not available to the public. So these application can use client secret when requesting token with authorization server. We can also have Single-Page Apps, who have their entire source available to the browser, and that cannot maintain the confidentiality of the Client Secret, use the same flow for getting the authorization code and in the step when requesting for access token pass only the client id and authorization code without using client secret.

In Authorization Code Grant Flow

  • The client application redirect the user agent to the Azure AD Authorization Endpoint.

Mainly it passes below values to the

response_type code
client_id Application Id
redirect_uri Redirect URI specified.

To

  • The user authenticates and consents the client application

  • The Azure AD authorization endpoint redirects the user agent back to client application with an authorization code at the redirect URL (i.e. code query parameter)

  • The client application uses this authorization code to request the access token from the authentication token endpoint by passing resource, client_id, grant_type = “authorization_code”, code and redirect_uri as shown below.

  • The Azure AD issues the access token, which the client application can use to call the Web API.

For our sample code to work: –

First Register the Application with Azure Active Directory to get the ClientId.

Get the authorization and token end point. Navigate to Overview and click on Endpoint to get these endpoints.

Also specify a Redirect URI for the application.

Navigate to Authentication and select the suggested Redirect URI.


Below is the sample C# Code: –


static void Main(string[] args)
{
// Dynamics CRM Online Instance URL
string resource = "https://bankfabdemo.crm.dynamics.com";

// application id
var clientId = "eb17e844-adfc-4757-ba6d-5384108e184a";

// redirect URL
var redirectURI = "https://login.microsoftonline.com/common/oauth2/nativeclient";

// Authenticate the registered application with Azure Active Directory.
AuthenticationContext authContext =
new AuthenticationContext("https://login.microsoftonline.com/bd88124a-ddca-4a9e-bd25-f11bdefb3f18/");

AuthenticationResult authResult = authContext.AcquireToken(resource, clientId, new Uri(redirectURI));
var accessToken = authResult.AccessToken;

// use HttpClient to call the Web API
HttpClient 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", authResult.AccessToken);

httpClient.BaseAddress = new Uri("https://bankfabdemo.crm.dynamics.com/api/data/v9.0/");

var response = httpClient.GetAsync("WhoAmI").Result;
if (response.IsSuccessStatusCode)
{
var userDetails = response.Content.ReadAsStringAsync().Result;
}

}

Within Postman :

Click on Request Token, login and give consent à

The token à

Hope it helps..

Connect to Dynamics 365 Web API using OAuth 2.0 – Client Credentials

In the last post we learned about connecting to Dynamics 365 Web API using Resource Owner Password Credential (ROPC), here we’d be covering the Client Credentials grant.

Client Credentials grant is designed for the client applications who are the resource owner and when basically there are no users involved, a batch (cron) job or a service using Web API, running in the background, on the server is one such example.

Here we will not be using the authorization endpoint, and the client application will be sending its own credential, instead of impersonating a user, directly to the token endpoint. The benefit compared to basic authentication or API keys is that credentials are not being sent with every request, it is only sent while requesting the access tokens along with all the other benefits of using access token – stateless, fine-grained access control, access token lifetime etc.

Let us see an example of using the Client Credentials grant in our console application. Along with the Client Id that we got when we registered our client application in the Azure Active Directory, we would need the Client Secret.

Follow the below steps to generate the Client Secret

Login to Azure Admin Portal

https://portal.azure.com

Select the application registered and click on Certificates & secrets option


Click on New client secret button to generate the client secret. Copy the generated client secret. Select the expiry as per the need.

Copy the secret generated and save it, as it won’t be available later when we are navigating here.

Also, we can get the Authentication Token Endpoint, for that navigate to Overview à Endpoints

And copy the OAuth 2.0 token endpoint.

Next step is to create the Application User within Dynamics 365 CE corresponding to the client application.

Login to Dynamics 365 CE, Settings à Security à Users àset View as Application Users and click on New button

Set Application Id as the Client Id of the Application registered and specify other mandatory values and save the record.

Assign appropriate security role to the new application user added.

Sample C# Code à


static void Main(string[] args)
{
// Dynamics CRM Online Instance URL
string resource = "https://bankfabdemo.crm.dynamics.com";

// client id and client secret of the application
ClientCredential clientCrendential = new ClientCredential("eb17e844-adfc-4757-ba6d-5384108e184a",
"p.eS+MI9cXkO_gQ02_lMlUXVSVCujyU0");

// Authenticate the registered application with Azure Active Directory.
AuthenticationContext authContext =
new AuthenticationContext("https://login.microsoftonline.com/bd88124a-ddca-4a9e-bd25-f11bdefb3f18/oauth2/v2.0/token");

AuthenticationResult authResult = authContext.AcquireToken(resource, clientCrendential);
var accessToken = authResult.AccessToken;

// use HttpClient to call the Web API
HttpClient 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", authResult.AccessToken);

httpClient.BaseAddress = new Uri("https://bankfabdemo.crm.dynamics.com/api/data/v9.0/");

var response = httpClient.GetAsync("WhoAmI").Result;
if (response.IsSuccessStatusCode)
{
var userDetails = response.Content.ReadAsStringAsync().Result;
}

}

Within Fiddler à

Within Postman à

Hope it helps..


Connect to Dynamics 365 Web API using OAuth 2.0 – Resource Owner Password Credential (ROPC)

The ROPC grant type should only be used in scenario when the Client application is absolutely trusted with user credentials and when redirect based flow are not possible. It was introduced for the Legacy Application for quick migration and is now more or less considered obsolete by OAuth Working group, and ideally should not be used.

In this flow, User enters his credentials (username and password) in the client application, when is then sent to Token Endpoint of the Authorization Server for Access Token request. The client application then gets the access token and call/request the protected resources (Web API) and get response. Here we remove the user from the authorization process and are not using the Authorization endpoint at all. The apps using this flow will lose the benefits of multi-factor authentication MFA and Single Sign-On.

Request à

client_id Client id of the app registered in Azure Active Directory.

We can also use the default client id –

2ad88395-b77d-4561-9441-d0e40824f9bc” –

which is setup against Dynamics 365 Online instances.

https://www.crmviking.com/2017/08/piggybacking-on-msdyn365.html

username User’s username
password User’s password
grant_type password
resource Dynamics 365 URL

Sample C# Code à

Create the console application and add the following Nuget Package

https://docs.microsoft.com/en-in/azure/active-directory/develop/active-directory-authentication-libraries


static void Main(string[] args)
{
// Dynamics CRM Online Instance URL
string resource = "https://bankfabdemo.crm.dynamics.com";

// ID of the Application Registered
// "2ad88395-b77d-4561-9441-d0e40824f9bc" - Default Client Id which is setup against Dynamics 365 Online instances.
string clientId = "2ad88395-b77d-4561-9441-d0e40824f9bc";

// username and password of the user
UserCredential userCrendential = new UserCredential("nishantrana@bankfabdemo.onmicrosoft.com", "*******");

// Authenticate the registered application with Azure Active Directory.
// Token URL - https://login.microsoftonline.com/common/oauth2/token

AuthenticationContext authContext =
new AuthenticationContext("https://login.windows.net/common");

AuthenticationResult authResult = authContext.AcquireToken(resource, clientId, userCrendential);
var accessToken = authResult.AccessToken;

// use HttpClient to call the Web API
HttpClient 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", authResult.AccessToken);

httpClient.BaseAddress = new Uri("https://bankfabdemo.crm.dynamics.com/api/data/v9.0/");

var response = httpClient.GetAsync("WhoAmI").Result;
if (response.IsSuccessStatusCode)
{
var userDetails = response.Content.ReadAsStringAsync().Result;
}

}

The result: –

Inside Fiddler à

Hope it helps..

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..