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"));