-->

Authorize WebApp to ADFS in order to access Dynami

2019-03-18 08:25发布

问题:

I have a web application that needs to speak with Dynamic CRM 365 Web API. The Dynamic 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.

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.

Can you please help me? Am i on the right path? Should i do anything more?

回答1:

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;