Search code examples
c#impersonationdynamics-365asp.net-core-8

How to call Dynamics 365 and impersonate the calling user?


I have a custom ASP.NET Core 8.0 web application from where I want to call Dynamics 365 and impersonate the currently logged in Windows user.

This code works when calling an on-premise installation of Dynamics v9.1, but when using the same code to call Dynamics365 in the cloud, I get "unauthorized". I'm guessing something does not work the same with the cloud-version of Dynamics but I can't figure out how to fix that so I'm hoping for some suggestions from you guys.

I'm running the code locally now for development so I'm impersonating myself and I am system administrator both on-premise and in cloud.

The code (where the method Get() is the starting point and is called earlier in my application):

protected virtual async Task<string> Get()
{
    var handler = new HttpClientHandler { UseDefaultCredentials = true };

    // The guid is a system administrator
    return await WindowsIdentity.RunImpersonated(_user.AccessToken, () => 
       GetAsync("https://myorganizationname.crm4.dynamics.com/api/data/v9.2/", 
                "systemusers(E8F2BDBD-540C-EF11-9F89-000D3A4C32E3)", handler)); 
}

private async Task<string> GetAsync(string baseUrl, string url, H (ttpClientHandler handler)
{
    using (var client = new HttpClient(handler))
    {
        client.BaseAddress = new Uri(baseUrl);        
        client.DefaultRequestHeaders.Add("Accept", "application/json");

        try
        {
            var request = new HttpRequestMessage(HttpMethod.Get, url);
            var response = await client.SendAsync(request);
            var json = await response.Content.ReadAsStringAsync();

            if (!response.IsSuccessStatusCode)
            {
                if (response.GetType() == typeof(HttpResponseMessage))
                {
                    throw new Exception(response.ReasonPhrase); // I always get "Unauthorized" as ReasonPhrase...
                }

                throw new Exception(json);
            }

            return json;
        }
        catch (System.Net.WebException ex)
        {
            // Just some error handling...
        }
    }
}

As mentioned, this code works fine when calling an on-premise instance of Dynamics...

What can I do to make it successfully call a cloud instance?


Solution

  • Your code attempts to use Windows impersonation to access the cloud-based Dynamics 365, which is totally different from on-premises Dynamics. Cloud-based Dynamics 365 does not support Windows Authentication or Windows Identity-based impersonation for authentication. Instead, it relies on OAuth 2.0 for authentication, which requires an Azure Active Directory (AAD) registered application, so you have to use one of the authentication mechanism mentioned here: https://learn.microsoft.com/en-us/power-apps/developer/data-platform/authenticate-oauth as follows:

     using (HttpClient httpClient2 = new HttpClient())
            {
                var formContent = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string, string>("resource", Resource),
                    new KeyValuePair<string, string>("client_id", ClientId),
                    new KeyValuePair<string, string>("client_secret", ClientSecret),
                    new KeyValuePair<string, string>("grant_type", "client_credentials")
                });
                HttpResponseMessage response = await httpClient2.PostAsync(Authority, formContent);
                return !response.IsSuccessStatusCode ? null : response.Content.ReadAsStringAsync().Result;
            }
    

    To impersonate a specific user in Dynamics 365 Online from a web application, you need to authenticate using Azure Active Directory (Azure AD) and set the MSCRMCallerID header in your HTTP requests. The MSCRMCallerID header allows you to perform operations on behalf of another user by specifying their user ID (GUID).

     using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            client.DefaultRequestHeaders.Add("Accept", "application/json");
            
            // Set the MSCRMCallerID header for impersonation
            client.DefaultRequestHeaders.Add("MSCRMCallerID", userId);
    
            var response = await client.GetAsync(url);
            var content = await response.Content.ReadAsStringAsync();
    
            if (!response.IsSuccessStatusCode)
            {
                throw new Exception($"Error: {response.StatusCode}, {response.ReasonPhrase}");
            }
    
            return content;
        }
    

    Please mark this answer as verified if you are ok with answer and let me know if you have any other questions. thanks