- Blog
#Azure #Technology
Unleashing the Power of Cloud: A tale of four Cloud Adoption Strategies
- 25/09/2023
Reading time 4 minutes
We had requirement to integrate with Microsoft Dynamics CRM Online in one of our projects. Dynamics CRM is using Azure AD for authentication.
Natural decision was to use Dynamics REST API since Web Services interface is older and will become obsolete sooner more than later. But before REST API could be used, we need to authenticate with Azure AD to get token for REST API calls.
Biggest challenge was the fact that we needed to do integration from server process running in Azure without user logged in or any kind of user interface at all. It’s pretty straightforward to integrate with Azure AD if you have Azure Web App, and user credentials can be asked from user when he or she is using the application. Or even using SSO if user is already logged in into Azure AD.
Customer gave us Azure AD account details (username and password) to be used when accessing Dynamics CRM.
Follow these steps to add your application to Azure AD and give required permissions to it:
Step 2
Step 3
Step 4
Step 5
Step 6
Step 7
Step 8
Step 9
NOTE: Copy the key immediately, there is no way to get it after this window is closed!
Step 10
Step 11
Step 12
Step 13
ADAL is Microsoft Azure Active Directory Authentication Libraries, available for all popular platforms including .NET, NodeJS, JavaScript and more.
We tried number of methods in ADAL 2.x and ADAL 3.x versions to get token from Azure AD. We even managed to get our application authenticated with Azure AD, but when we tried to access Dynamics CRM with REST API, we always got 401 Unauthorized as response.
Finally we decided to throw ADAL away, and tried to do authentication with using OAuth2 on top of pure HTTPS.
To authenticate we need the following parameters:
“`c# namespace Security.Authentication { public class AadPasswordGrantAuthenticationServiceSettings { public string CliendId { get; set; } public string ClientSecret { get; set; } public string Resource { get; set; } public string User { get; set; } public string Password { get; set; } public string Tenant { get; set; }
public AadPasswordGrantAuthenticationServiceSettings() { CliendId = "f0b6f6ad-4a38-4a27-a01d-650c78e49afd"; ClientSecret = "IR9bhMuzpRoElBmaen10XQUeXqpDGYnLVr3YEMIxJzY="; Resource = "https://customercrm.crm.dynamics.com/"; User = "integration.crm@customerdomain.com"; Password = "50meveryCrypticPassw0@dInHere_## 55%¤"; Tenant = "customertenant.onmicrosoft.com"; }
} } “`
Where * ClientId: Application ID from Azure AD * ClientSecret: Key from Azure AD Keys configuration (CrmIntegration in this example) * Resource: Dynamics CRM URL * User: Azure AD username * Password: Azure AD password * Tenant: Azure AD tenant address
We also need to define Token that is returned from Azure AD in case of successful authentication:
c# namespace Security.Authentication { public class Token { public string token_type { get; set; } public string scope { get; set; } public int expires_in { get; set; } public int expires_on { get; set; } public int not_before { get; set; } public string resource { get; set; } public string access_token { get; set; } public string refresh_token { get; set; } public string id_token { get; set; } } }
Finally we created a class that can be used to acquire tokens from Azure AD:
“`c# using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Newtonsoft.Json;
namespace Security.Authentication { public class AadPasswordGrantAuthenticationService { private readonly ILogger logger; private readonly string url; private readonly List<KeyValuePair<string, string>> _requestParams;
public AadPasswordGrantAuthenticationService( AadPasswordGrantAuthenticationServiceSettings settings) { _requestParams = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>( "client_id", settings.CliendId), new KeyValuePair<string, string>( "client_secret", settings.ClientSecret), new KeyValuePair<string, string>( "resource", settings.Resource), new KeyValuePair<string, string>( "grant_type", "password"), new KeyValuePair<string, string>( "username", settings.User), new KeyValuePair<string, string>( "password", settings.Password) }; _url = string.Format( "https://login.windows.net/{0}/oauth2/token", settings.Tenant); } public async Task<Token> AcquireTokenAsync() { using (var httpClient = new System.Net.Http.HttpClient()) { httpClient.DefaultRequestHeaders.Add( "Cache-Control", "no-cache"); using (var httpContent = new FormUrlEncodedContent(_requestParams)) { using (var response = await httpClient .PostAsync(_url, httpContent)) { if (!response.IsSuccessStatusCode) { return null; } return JsonConvert.DeserializeObject<Token>( await response.Content.ReadAsStringAsync()); } } } }
} } “`
That’s it!
Now we can use AadPasswordGrantAuthenticationService class to get token before sending request to Dynamics CRM REST API. This is how token is included in REST API calls:
“`c# private async Task SendReceiveAsync(HttpMethod httpMethod, string query) { // Renew token before each request var token = await _aadPasswordGrantAuthenticationService .AcquireTokenAsync(); if (token == null) { return string.Empty; }
using (var request = new HttpRequestMessage(httpMethod, query)) { crmHttpClient.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( token.tokentype, token.access_token);
using (var response = await _crmHttpClient.HttpClient .SendAsync(request)) { if (!response.IsSuccessStatusCode) { return string.Empty; } return await response.Content.ReadAsStringAsync(); }
} } “`
I’m sure there are other ways to do authentication for Dynamics CRM Online, even somehow with ADAL library. But it’s pretty difficult to debug authentication problems, you never know what is wrong in a way you tried to authenticate. I hope solution we came up will help you to use your time in solving real problem, not fighting with authentication.
Sami Ovaska @samiovaska
Our newsletters contain stuff our crew is interested in: the articles we read, Azure news, Zure job opportunities, and so forth.
Please let us know what kind of content you are most interested about. Thank you!