D365 Authentication – connect my apps

With the constant push to make data more secure D365 now includes Application Users that can be configured to allow access to D365 from external applications. There are numerous articles around explaining how to create your Application User in D365 using Azure AD App Registrations – this one from PowerObjects has a good explanation of steps required to achieve this.

In this post I wanted to briefly describe the next steps for using that authentication method in your app to perform CRUD operations. To demonstrate this I created a simple Console App to connect to a D365 instance and perform some simple operations using both the OrganizationWebProxyClient and the CrmServiceClient. For reference, I added a package from NuGet called Microsoft.CrmSdk.XrmTooling.CoreAssembly which contains the official Microsoft.Xrm.Tooling.Connector assembly. The same NuGet package also contains the Microsoft.CrmSdk.CoreAssemblies dependency which contains the Microsoft.Xrm.Sdk.dll. Within the console app I created a class that manages to authentication.

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;

namespace OrgService.Connect
{
    class AuthHook : Microsoft.Xrm.Tooling.Connector.IOverrideAuthHookWrapper
    {
        // In memory cache of access tokens
        Dictionary<string, AuthenticationResult> accessTokens = new Dictionary<string, Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult>();

        public void AddAccessToken(Uri orgUri, Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult accessToken)
        {
            // Access tokens can be matched on the hostname,
            // different endpoints in the same organization can use the same access token
            accessTokens[orgUri.Host] = accessToken;
        }

        public string GetAuthToken(Uri connectedUri)
        {
            // Check if you have an access token for this host
            if (accessTokens.ContainsKey(connectedUri.Host) && accessTokens[connectedUri.Host].ExpiresOn > DateTime.Now)
            {
                return accessTokens[connectedUri.Host].AccessToken;
            }
            else
            {
                accessTokens[connectedUri.Host] = GetAccessTokenFromAzureAD(connectedUri);
            }
            return accessTokens[connectedUri.Host].AccessToken;
        }

        private AuthenticationResult GetAccessTokenFromAzureAD(Uri orgUrl)
        {
            string organizationUrl = "https://myD365instance.crm6.dynamics.com";
            string clientId = "00000000-0000-0000-0000-000000000001";    // This is the Application ID from your App Registration
            string appKey = "<the client secret for your app>";          // The Client Secret from your App Registration
            string aadInstance = "https://login.microsoftonline.com/";
            string tenantID = "00000000-0000-0000-0000-000000000001";    // The GUID of your Azure Tenant ID. See the article above for details on finding this value.

            ClientCredential clientcred = new ClientCredential(clientId, appKey);
            AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID);
            AuthenticationResult authenticationResult = authenticationContext.AcquireToken(organizationUrl, clientcred);

            accessTokens[new Uri(organizationUrl).Host] = authenticationResult;

            return authenticationResult;
        }
    }
}

To access the D365 instance using the OrganizationWebProxyClient we can use the following code

                string organizationUrl = "https://myD365instance.crm6.dynamics.com";

                var hook = new AuthHook();

                var requestedToken = hook.GetAuthToken(new Uri(organizationUrl));

                using (var webProxyClient = new OrganizationWebProxyClient(new Uri($"{organizationUrl}/XRMServices/2011/Organization.svc/web"), false))
                {
                    webProxyClient.HeaderToken = requestedToken;

                    // Test with a basic WhoAmI request first
                    OrganizationRequest request1 = new OrganizationRequest()
                    {
                        RequestName = "WhoAmI"
                    };
                    OrganizationResponse response1 = webProxyClient.Execute(request1);

                    // We are also able to create an instance of the OrganizationService and run queries against it
                    IOrganizationService organizationService = webProxyClient as IOrganizationService;

                    Entity entity = organizationService.Retrieve("account", new Guid("92348762-0D32-E611-80EC-B38A27891203"), new Microsoft.Xrm.Sdk.Query.ColumnSet("name", "preferredcontactmethodcode"));

                }

Using the Microsoft.Xrm.Tooling.Connector we require a slightly different approach when using the authenticated Application User. It still uses the AuthHook class but through the AuthOverrideHook property. This uses the same AuthHook object we created earlier for the OrganizationWebProxyClient.

                // Register the hook with the CrmServiceClient
                CrmServiceClient.AuthOverrideHook = hook;

                // Create a new instance of CrmServiceClient, pass your organization url and make sure useUniqueInstance = true!
                var client = new CrmServiceClient(new Uri(organizationUrl), useUniqueInstance: true);

                // Test with a basic WhoAmI request first
                OrganizationRequest request2 = new OrganizationRequest()
                {
                    RequestName = "WhoAmI"
                };
                OrganizationResponse response2 = client.Execute(request2);

                Entity entity1 = client.Retrieve("account", new Guid("92348762-0D32-E611-80EC-B38A27891203"), new Microsoft.Xrm.Sdk.Query.ColumnSet("name", "preferredcontactmethodcode"));

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s