Generate Access Token for Dynamics 365 Single Tenant Server to Server Authentication

Generate Access Token for Dynamics 365 Single Tenant Server to Server Authentication

In Dynamics 365 integration scenarios most of the times we need to authenticate only single tenant. Since now Dynamics 365 authentication only through Azure AD (for online instances) is recommended let’s see how to do it. To achieve this first of all we need to create App in Azure Active Directory and good news is that you don’t need Azure subscription to try this out, your Dynamics 365 free trial is enough.

These 4 steps are involved to implement this, you can always jump to next step if know current step. This topic may not be very new to everyone, but when I got to implement the same recently it took me few hours to make it work, because of recent of frequent Azure updates or outdated content maybe. which leads me to write this.

  1. Create & configureApp in Azure Active Directory
  2. Create User in Azure AD and Configure it as Application User in Dynamics 365
  3. Write C# code with ADAL(Active Directory Authentication Library) to generate Access Token
  4. Making requests to Dynamics 365 with above generated Access Token

Step 1: Create Azure AD App

Now your App is created, we need three things to use further from here.

  1. Application Id aka Client Id
  2. Tenant Id
  3. Client Secret

Step 2: Create Application User

new-app-user

Step 3: Get Access Token with ADAL

Step 4: Consuming Access Token

public static async Task<HttpResponseMessage> CrmRequest(HttpMethod httpMethod, string requestUri, string body = null)
{
    var accessToken = await AccessTokenGenerator();
    var client = new HttpClient();
    var msg = new HttpRequestMessage(httpMethod, requestUri);
    msg.Headers.Add("OData-MaxVersion", "4.0");
    msg.Headers.Add("OData-Version", "4.0");
    msg.Headers.Add("Prefer", "odata.include-annotations=\"*\"");

    // Passing AccessToken in Authentication header
    msg.Headers.Add("Authentication", $"Bearer {accessToken}");

    if (body != null)
        msg.Content = new StringContent(body, UnicodeEncoding.UTF8, "application/json");

    return await client.SendAsync(msg);
}
var contacts = CrmRequest(
    HttpMethod.Get, 
    "https://efrig.api.crm8.dynamics.com/api/data/v9.1/contacts")
    .Result.Content.ReadAsStringAsync();

Full Code (Replace your Azure Credentials before executing)

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace D365S2S
{
    class Program
    {
        static void Main(string[] args)
        {
            var contacts = CrmRequest(
                HttpMethod.Get,
                "https://efrig.api.crm8.dynamics.com/api/data/v9.1/contacts")
                .Result.Content.ReadAsStringAsync();
            // Similarly you can make POST, PATCH & DELETE requests
        }

        public static async Task<string> AccessTokenGenerator()
        {
            string clientId = "13950f0e-0000-4e2f-0000-b923302c4338"; // Your Azure AD Application ID
            string clientSecret = "0^C#%0000DR7/#Z[-.m5aYO00000000$"; // Client secret generated in your App
            string authority = "https://login.microsoftonline.com/ceb48f70-0000-1111-0000-9170f6a706a6"; // Azure AD App Tenant ID
            string resourceUrl = "https://efrig.crm8.dynamics.com"; // Your Dynamics 365 Organization URL

            var credentials = new ClientCredential(clientId, clientSecret);
            var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
            var result = await authContext.AcquireTokenAsync(resourceUrl, credentials);
            return result.AccessToken;
        }

        public static async Task<HttpResponseMessage> CrmRequest(HttpMethod httpMethod, string requestUri, string body = null)
        {
            // Acquiring Access Token
            var accessToken = await AccessTokenGenerator();

            var client = new HttpClient();
            var message = new HttpRequestMessage(httpMethod, requestUri);

            // OData related headers
            message.Headers.Add("OData-MaxVersion", "4.0");
            message.Headers.Add("OData-Version", "4.0");
            message.Headers.Add("Prefer", "odata.include-annotations=\"*\"");

            // Passing AccessToken in Authentication header
            message.Headers.Add("Authorization", $"Bearer {accessToken}");
            
            // Adding body content in HTTP request 
            if (body != null)
                message.Content = new StringContent(body, UnicodeEncoding.UTF8, "application/json");

            return await client.SendAsync(message);
        }
    }
}

Hope it helps, feel free to get in touch for any query/suggestions.


Buy Me A Coffee