Skip to main content
Post a question

Notifications

Community site session details

Community site session details

Session Id : 6tQk+e2hFc16W/pi/m0Fz6

{Solved} Call Web API from within a plugin in Dynamics 365 online

Debajit Dutta Profile Picture Debajit Dutta 2,702

Well, this is one question I cam across countless number of times. And in a recently conducted training boot camp, i was asked the same question. To be honest after understanding OAuth and how various types of grants work in Azure Active Directory, I was thinking this should not be a very tough problem.

To my surprise, when I started searching the community I came across many links which suggested it can’t be done and many links which suggested ways but no complete guidance on how to do it.

So decided to challenge myself in it for my blog readers and came out pretty quick within couple of hours. So here we start out journey.

Before we go ahead and discuss on how to do this, let’s understand the problem faced.

The first thing that we need to connect to Web API instance is an access token. And getting an access token now is just piece of cake. You reference Active Directory Authentication Library (you can easily get this from Nuget with the keyword : ADAL) in your project and then use some 4-5 pre-defined lines to get the token.

So what’s the problem with that? The problem is when we try to run the same code in our plugins and try to access the token, we get a security exception. The problem is our online plugins runs in sandbox mode.

So the solution to this would be not to use ADAL. So what are the other options? Off-course we can use plain simple HttpRequest to get the token right? After all internally ADAL might be doing the same right? Not sure about ADAL but certainly this is harder than you think.

The first point to consider here is when the plugin is executing, we do not have the password for the user.

In my blog link – https://debajmecrm.com/2018/04/30/headless-authentication-with-dynamics-crm-online-and-external-web-app-which-requires-client-secret/ I have explained in detail on how to get the access token without using ADAL. The problem here is, I am  using the password here to get the token. In plugins I will not have that.

So how to tackle this.

If you observe carefully, there are various types of grants in Oauth and one of them “grant_type=password” I have used in the above link. Well since I can’t use that, there is another type called grant_type=client_credentials”. And this is what we are going to use here.

So let’s now jump to the solution.

Step 1:

Register an application of type WebApp/Api in your Azure active directory with proper permission to Dynamics Application and then create an application user in Dynamics with the Application ID of your registered App. Give access to your application user with proper security roles to gain necessary access to entities.

If you are not aware of registering App or the concept of Application User in Dynamics, it’s explained in great detail in the below link

https://debajmecrm.com/2018/08/16/step-by-step-guide-query-dynamics-crm-web-api-using-server-to-server-authentication-with-application-user/

Just set-up the APP and the application user. Don’t try to use the code since it will not work in plugins.

Step 2:

Plugin code

public class WebApiSample : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            // getting the pipeline context.
             var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var ent = (Entity)context.InputParameters["Target"];

            var accessToken = GetTokenWithoutADAL().GetAwaiter().GetResult();
             var accountJson = RetrieveAccounts(accessToken).GetAwaiter().GetResult();

            ent["vvbv_description"] = accountJson;
        }

        private async Task<string> GetTokenWithoutADAL()
        {
            string azureAdDomain = "xrmforyou62.onmicrosoft.com";
            string loginUrl = $"
https://login.microsoftonline.com/{azureAdDomain}/oauth2/token";
             string resource = "https://xrmforyou62.crm.dynamics.com";
              string clientId = "9a3590d8-c89e-4de5-bb50-8dfa3d9796a9";
            string clientSecret = "<put your client secret here>";

            HttpClient client = new HttpClient();
            var postData = $"client_id={clientId}&client_secret={clientSecret}&resource={resource}&grant_type=client_credentials";

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, loginUrl);
            request.Content = new StringContent(postData, Encoding.UTF8);
            request.Content.Headers.Remove("Content-Type");
            request.Content.Headers.TryAddWithoutValidation("Content-Type", $"application/x-www-form-urlencoded");

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var responseMessage = await client.SendAsync(request);
            var jsonResponseString = await responseMessage.Content.ReadAsStringAsync();

           var jsonContent = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonResponseString);

            return jsonContent["access_token"];
        }

        private async Task<string> RetrieveAccounts(string token)
        {
             string webApiUrl = "
https://xrmforyou62.api.crm.dynamics.com/api/data/v9.1";
             string url = $"{webApiUrl}/accounts?$select=name";

            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
           
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            HttpResponseMessage response = await client.GetAsync(url);

            string jsonContent = await response.Content.ReadAsStringAsync();

            return jsonContent;
        }
    }

You can do a better job at handling the client_id and client_secret. But this is just sample code and hence I am using this directly.

Also to parse the JSON, I have used the Newtonsoft.json dll which you need to ilmerge with the plugin assembly before uploading to the plugin registration tool. Check for the highlighted code.

And I have registered this plugin on the Pre-create of a test entity. I set the description of the field in this plugin to the account JSON and voila it works!

image

Eureka! I can see the account JSON in the description field after I create the record.

Isn’t it great guys.

Hope this helps. And hope you just had something interesting to read in CRM world today.

Debajit Dutta

(Dynamics MVP)

For consultation/ training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com


This was originally posted here.

Comments

*This post is locked for comments