14

I have a web application that needs to speak with Dynamics CRM 365 Web API. The Dynamics CRM is configured as a Relying Party on ADFS. The server is Windows Server 2016 and everything is on premise and not on Azure.

What I did to acquire a valid token are the following:

1) In ADFS went to Application Groups and add an new Server Application, took the ClientID and also generate a Client Secret for my web application.

enter image description here

2) Add new new user in Active Directory the webAppUser

3) Add this user as an Application User in CRM with Application ID the ClientID I got earlier when I registered my web app to ADFS. Also created a new Role with full permissions on Entity Account and assign this role to this Application User

4) I'm using the below code to retrieve a bearer token and add it to my HttpClient Authorization header.

public class CrmWebApiClient
{
    private HttpClient _httpClient;

    public CrmWebApiClient()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("https://crmbaseaddress.com");            
    }

    internal async Task Initialize()
    {
        try
        {               
            var authority = "https://adfsServerUrl/adfs/";
            var authContext = new AuthenticationContext(authority,false);
            var credentials = new ClientCredential(clientID,clientSecret);

            var authResult = await authContext.AcquireTokenAsync("https://crmbaseaddress.com", credentials);

            _httpClient.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
        }
        catch (Exception ex)
        {
            var error = ex;
        }

    }

    internal async Task<string> GetValuesAsync()
    {
        var result = string.Empty;
        try
        {
            result = await _httpClient.GetStringAsync("api/data/v8.1/accounts");
        }
        catch (Exception ex)
        {
            var error = ex;
        }

        return result;
    }
}

5) I manage to get a token but when I call the CRM's Web Api I still get 401 Unauthorized.

Arun Vinoth
  • 20,360
  • 14
  • 48
  • 135
Ricky Stam
  • 2,018
  • 19
  • 25

1 Answers1

3

At the end i had to use a system user and send it's credentials in my oAUth request using the code below in order to acquire a valid token:

namespace TestApp.App_Start {
public class CrmWebApiClient
{
    private HttpClient _httpClient;

    public CrmWebApiClient()
    {       
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("https://crmUrl");
        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        _httpClient.DefaultRequestHeaders.Add("OData-MaxVersion","4.0");
        _httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
    }

    internal async Task Initilize()
    {
        try
        {

            var tokenClient = new HttpClient();             
            var content = new FormUrlEncodedContent(new[] {
                new KeyValuePair<string,string>("client_id",_clientID),
                new KeyValuePair<string,string>("client_secret",_clientSecret),
                new KeyValuePair<string,string>("resource",_urlOfResource),
                new KeyValuePair<string,string>("username",_usernameOfSystemUser),
                new KeyValuePair<string,string>("password",_passwordOfSystemUser),
                new KeyValuePair<string,string>("grant_type","password"),
            });
            var res = tokenClient.PostAsync("https://adfsUrl/adfs/oauth2/token", content);
            var respo = res.Result.Content.ReadAsStringAsync().Result;
            var accesstoken = JObject.Parse(respo).GetValue("access_token").ToString();

            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);

        }
        catch (Exception ex)
        {
            Trace.WriteLine($"Exception when requesting the bearer token from ADFS: {ex.Message} - {ex.InnerException?.Message}");
        }

    }

    internal async Task<string> GetAccountsAsync()
    {
        var result = string.Empty;
        try
        {
            result = _httpClient.GetStringAsync("/api/data/v8.0/accounts").Result;

        }
        catch (Exception ex)
        {
            Trace.WriteLine($"Exception when calling the CRM api: {ex.Message} - {ex.InnerException?.Message}");
        }
        return result;
    }
}
}


// Use the above class like that
var httpClient = new CrmWebApiClient();
httpClient.Initilize().Wait();
var result = httpClient.GetAccountsAsync().Result;
Ricky Stam
  • 2,018
  • 19
  • 25
  • What version of ADFS is this? Are password grants supported..obviously there are, but could you clarify a little bit more. – davidcarr Oct 25 '17 at 14:54
  • 1
    @davidcarr it's Windows Server 2016 with the latest ADFS – Ricky Stam Oct 26 '17 at 07:26
  • @RickyStam I have a similar situation, could you clarify what you mean by system user? Aren't all users user created? – tracer tong Nov 14 '17 at 12:46
  • @tracertong i mean that i created a user in AD that doesn't belong to any real user, but only use his username and password in my application. That's why i call him system user. – Ricky Stam Nov 14 '17 at 15:42