Dynamics 365 authentication is recommended only through Azure AD (for online instances). To achieve this, first of all, we need to create an app in Azure Active Directory and the good news is that you don’t need an Azure subscription to try this out; your free trial of Dynamics 365 is enough.

The following 4 steps are involved in the implementation of this. You can always jump to the next step if you have already implemented (or you know) the current step. This topic may not be very new to everyone, but when I went to implement the same recently, it took me a few hours to make it work, perhaps because of the recent frequent Azure updates or the outdated content. This led me to write this.
  1. Create and configure the app in Azure Active Directory.
  2. Create a user in Azure AD and configure it as an application user in Dynamics 365
  3. Write C# code with ADAL (Active Directory Authentication Library) to generate the Access Token
  4. Make requests to Dynamics 365 with the above-generated Access Token

Step 1 - Create Azure AD App

Navigate to Azure (https://portal.azure.com).
On the left menu, click on Azure Active Directory -> App registrations (Preview) => + New registration. We will use the Preview version only because it has more straight-forward options and going forward, this only is going to be the default option.

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication
Give some name to your app, and for account type, select "Accounts in this organizational directory only" because we need it for a single tenant only.

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication

Once the app is created, click on "API permissions" to add a new permission to your app.

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication
Select Dynamics CRM here.

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication
Check the user_impersonation box on the upcoming screen and click "Add permissions".

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication
These permissions require admin's consent. Click on the "Grant admin consent for D365In" button and confirm it. You need the administrator role to do it.

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication

Now, your app is created. We need to use three things:

  1. Application Id, aka, Client Id
  2. Tenant Id
  3. Client Secret
The Application Id and Tenant Id can be grabbed from the "App Overview".

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication

To generate the client secret, go to "Certificates & secrets" and then "+ New client secret". Give some description and select the validity of your secret. Then, click "Add".

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication
Client Secret is something that you should keep secret; that is why you can see this only once after generation. Copy it and keep it safe to use later.

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication
Step 2 - Create Application User

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication

Step 3 - Get Access Token with ADAL

Create a new project in Visual Studio and add the ADAL package via NuGet.

Generate Access Token For Dynamics 365 Single Tenant Server To Server Authentication
Create the below-shown method and replace the Application Id, Client Secret, Tenant Id, and your organization's URL at appropriate places.
  1. /// Required-Namespaces
  2. using Microsoft.IdentityModel.Clients.ActiveDirectory;
  3. using System.Threading.Tasks;
  4. /// Method-to-generate-Access-Token
  5. public static async Task<string> AccessTokenGenerator()
  6. {
  7. string clientId = "Azure AD App Id";
  8. string clientSecret = "Client Secret Generated for App";
  9. string authority = "https://login.microsoftonline.com/< your app tenant guid >";
  10. string resourceUrl = "https://< your D365 org>.< crm instance location e.g crm, crm8 >.dynamics.com"; // Org URL
  11. ClientCredential credentials = new ClientCredential(clientId, clientSecret);
  12. var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
  13. var result = await authContext.AcquireTokenAsync(resourceUrl, credentials);
  14. return result.AccessToken;
  15. }
Step 4 - Consuming Access Token
You have the access token. Now, you can make a request to your Dynamics 365 by including this to your HTTP requests, as shown in the below method.
  1. public static async Task<HttpResponseMessage> CrmRequest(HttpMethod httpMethod, string requestUri, string body = null)
  2. {
  3. var accessToken = await AccessTokenGenerator();
  4. var client = new HttpClient();
  5. var msg = new HttpRequestMessage(httpMethod, requestUri);
  6. msg.Headers.Add("OData-MaxVersion", "4.0");
  7. msg.Headers.Add("OData-Version", "4.0");
  8. msg.Headers.Add("Prefer", "odata.include-annotations=\"*\"");
  9. // Passing AccessToken in Authentication header
  10. msg.Headers.Add("Authorization", $"Bearer {accessToken}");
  11. if (body != null)
  12. msg.Content = new StringContent(body, UnicodeEncoding.UTF8, "application/json");
  13. return await client.SendAsync(msg);
  14. }
You can make different requests using the above method. Here is an example for retrieving all contacts with GET.
  1. var contacts = CrmRequest(
  2. HttpMethod.Get,
  3. "https://efrig.api.crm8.dynamics.com/api/data/v9.1/contacts")
  4. .Result.Content.ReadAsStringAsync();

Full Code (Replace your Azure credentials before executing)

  1. using Microsoft.IdentityModel.Clients.ActiveDirectory;
  2. using System.Net.Http;
  3. using System.Text;
  4. using System.Threading.Tasks;
  5. namespace D365S2S
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. var contacts = CrmRequest(
  12. HttpMethod.Get,
  13. "https://efrig.api.crm8.dynamics.com/api/data/v9.1/contacts")
  14. .Result.Content.ReadAsStringAsync();
  15. // Similarly you can make POST, PATCH & DELETE requests
  16. }
  17. public static async Task<string> AccessTokenGenerator()
  18. {
  19. string clientId = "13950f0e-0000-4e2f-0000-b923302c4338"; // Your Azure AD Application ID
  20. string clientSecret = "0^C#%0000DR7/#Z[-.m5aYO00000000$"; // Client secret generated in your App
  21. string authority = "https://login.microsoftonline.com/ceb48f70-0000-1111-0000-9170f6a706a6"; // Azure AD App Tenant ID
  22. string resourceUrl = "https://efrig.crm8.dynamics.com"; // Your Dynamics 365 Organization URL
  23. var credentials = new ClientCredential(clientId, clientSecret);
  24. var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
  25. var result = await authContext.AcquireTokenAsync(resourceUrl, credentials);
  26. return result.AccessToken;
  27. }
  28. public static async Task<HttpResponseMessage> CrmRequest(HttpMethod httpMethod, string requestUri, string body = null)
  29. {
  30. // Acquiring Access Token
  31. var accessToken = await AccessTokenGenerator();
  32. var client = new HttpClient();
  33. var message = new HttpRequestMessage(httpMethod, requestUri);
  34. // OData related headers
  35. message.Headers.Add("OData-MaxVersion", "4.0");
  36. message.Headers.Add("OData-Version", "4.0");
  37. message.Headers.Add("Prefer", "odata.include-annotations=\"*\"");
  38. // Passing AccessToken in Authentication header
  39. message.Headers.Add("Authorization", $"Bearer {accessToken}");
  40. // Adding body content in HTTP request
  41. if (body != null)
  42. message.Content = new StringContent(body, UnicodeEncoding.UTF8, "application/json");
  43. return await client.SendAsync(message);
  44. }
  45. }
  46. }

I hope it helps. Feel free to get in touch for any query or suggestion.