Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :

Using the Microsoft Authentication Library (MSAL) with D365 Finance and Operations apps

Chris Roehrich Profile Picture Chris Roehrich 289

This post is for those that are interested in developing .Net integrations with D365FO. In order to authenticate an account that has access inside a D365FO instance for your Azure application registration, you will need to request a token. The preferred library from Microsoft to use for the process of authenticating with the Microsoft Identity platform is using the MSAL.Net library. There is information on the benefits and migrating samples to MSAL.Net here.

I have recently been working with customers that are using D365FO Custom service integrations. In doing review of the documentation and our GitHub integration samples, the code to retrieve the token using Azure AD Active Directory (ADAL) is becoming outdated. 

Here is a screen shot of the Nuget Package Manager from working in a newly downloaded C# library project named AuthenticationUtility from the ServicesSamples solution in our GitHub integration samples. 

7215.ADAL-message-in-VS.png

Note the following message: There is a newer version of this library available here: https://www.nuget.org/packages/Microsoft.Identity.Client/
Migration guide: https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-net-migration
ADAL no longer receives new feature improvements.

If your integration code to retrieve tokens is using ADAL, now is the time to start making the change to MSAL. Using this ServicesSample project, here are the quick steps I did to move to MSAL and confirm the JsonConsoleApplication works successfully:

1. Uninstall ADAL (Microsoft.IdentityModel.Clients.ActiveDirectory) from the JsonConsoleApplication and AuthenticationUtility projects. Right click on the projects and choose Manage NuGet Packages...

1586.Remove-ADAL-from-AuthUtil.png

2. Select the Microsoft.IdentityModel.Clients.ActiveDirectory package and Uninstall button.

3022.Uninstall-ADAL.png

3. Install MSAL in the AuthenticationUtility project. The JsonConsoleApplication has a reference to AuthenticationUtility so you do not need to add MSAL into the JsonConsoleApplication project. Right click on the AuthenticationUtility project and choose Manage Nuget Packages... . Under the Browse tab, enter the search term microsoft.identity.client like below and you can then select it to install.

7824.Install-MSAL.png

4. Now we need to update the two class files in the AuthenticationUtility project. First, the following can be used for the ClientConfiguration.cs file which has the environment information like the D365FO URL and the client secret value and App Id for the application registration in Azure and D365FO:

using System;

namespace AuthenticationUtility
{
    public partial class ClientConfiguration
    {
        public static ClientConfiguration Default { get { return ClientConfiguration.OneBox; } }

        public static ClientConfiguration OneBox = new ClientConfiguration()
        {
            // You only need to populate this section if you are logging on via a native app. For Service to Service scenarios in which you e.g. use a service principal you don't need that.
            UriString = "https://cpr-dev.axcloud.dynamics.com",
            UserName = "tusr1@TAEOfficial.ccsctp.net",            
            // Insert the correct password here for the actual test.
            Password = "",

            // You need this only if you logon via service principal using a client secret. See: https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/data-entities/services-home-page to get more data on how to populate those fields.
            // You can find that under AAD in the azure portal
            ActiveDirectoryResource = "https://cpr-dev.axcloud.dynamics.com", // Don't have a trailing "/". Note: Some of the sample code handles that issue.
            ActiveDirectoryTenant = "https://login.windows.net/CPRDevonmicrosoft.com", // Some samples: https://login.windows.net/yourtenant.onmicrosoft.com, https://login.windows.net/microsoft.com
            ActiveDirectoryClientAppId = "30580095-eeee-4fdf-8a66-5fb1da90d25b",
            // Insert here the application secret when authenticate with AAD by the application
            ActiveDirectoryClientAppSecret = "3Lc7Q~dMvXC7FZfdnzRbP8YVO2jg~qpWGXxtM",

        // Change TLS version of HTTP request from the client here
        // Ex: TLSVersion = "1.2"
        // Leave it empty if want to use the default version
        TLSVersion = "",
        };

        public string TLSVersion { get; set; }
        public string UriString { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string ActiveDirectoryResource { get; set; }
        public String ActiveDirectoryTenant { get; set; }
        public String ActiveDirectoryClientAppId { get; set; }
        public string ActiveDirectoryClientAppSecret { get; set; }
    }
}

Next update the OAuthHelper.cs file with the below. This contains the code for one of the ways to use MSAL.Net to generate the token. A note here is that you will need to use the async and await keywords to use many of the MSAL library methods.

using Microsoft.Identity.Client;
using System;
using System.Threading.Tasks;

namespace AuthenticationUtility
{
    public class OAuthHelper
    {
        /// 
        /// The header to use for OAuth authentication.
        /// 
        public const string OAuthHeader = "Authorization";

        public static async Task GetAuthenticationHeaderAsync()
        {
            string aadTenant = ClientConfiguration.Default.ActiveDirectoryTenant;
            string aadClientAppId = ClientConfiguration.Default.ActiveDirectoryClientAppId;
            string aadClientAppSecret = ClientConfiguration.Default.ActiveDirectoryClientAppSecret;
            string aadResource = ClientConfiguration.Default.ActiveDirectoryResource;

            IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(aadClientAppId)
                    .WithClientSecret(aadClientAppSecret)
                    .WithAuthority(new Uri(aadTenant))
                    .Build();
            string[] scopes = new string[] { $"{aadResource}/.default" };

            AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
            return result.CreateAuthorizationHeader();
        }        
    }
}

Finally update the Program.cs file in the JsonConsoleApplication project.

using AuthenticationUtility;
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;

namespace OAuthXppConsoleApplication
{
    class Program
    {
        // In the AOT you will find UserSessionService in Service Groups and AifUserSessionService under Services.
        public static string sessionUrl = "/api/services/UserSessionService/AifUserSessionService/GetUserSessionInfo";
        
        static async Task Main(string[] args)
        {
            string GetUserSessionOperationPath = string.Format("{0}{1}", ClientConfiguration.Default.UriString.TrimEnd('/'), sessionUrl);
            
            var request = HttpWebRequest.Create(GetUserSessionOperationPath);     
            
            request.Headers[OAuthHelper.OAuthHeader] = await OAuthHelper.GetAuthenticationHeaderAsync();
            request.Method = "POST";
            request.ContentLength = 0;

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (StreamReader streamReader = new StreamReader(responseStream))
                    {
                        string responseString = streamReader.ReadToEnd();

                        Console.WriteLine(responseString);
                    }
                }
            }

            Console.ReadLine();
        }

    }
}

5. Now the application can be built and ran successful! Note this C# Console application is making a web request to the UserSessionService. This service is deployed like all other custom services for D365FO under the D365FO URL /api/services. In order to make that call successful, we need to authenticate using a token.

run-console-app.png

Hopefully if you went through the steps, you had success! One interesting take away I will end with is that with using a confidential client application like in this example, we are using what is called Client credentials flow. This method takes care of verifying the token cache before sending a request. More information on caching tokens with MSAL is here that describes this useful behavior.

Comments

*This post is locked for comments

  • Alexander1111 Profile Picture Alexander1111 10
    Posted at
    Thank you. Alexander
  • Alexander1111 Profile Picture Alexander1111 10
    Posted at
    Hi Chris, Thank you for the post. A few questions/issues: 1) OAuthHelper class gives an error on the line return result.CreateAuthorizationHeader(); saying CS1997 Since OAuthHeader.GetAuthenticationHeaderAsync() is an Async method that returns Task, a return method must not be followed by an object expression; 2) Program.cs gives an error in a line request.Headers[OAuthHelper.OAuthHeader] = await OAuthHelper.GetAuthenticationHeaderAsync(); saying CS0029: cannot implicitly convert type void to string 3) Which address should be used in a line: ActiveDirectoryResource = "">cpr-dev.axcloud.dynamics.com"