Mathieu A.
Mathieu A.

Reputation: 103

Dynamics CRM api HttpClient Request Authentication with ADFS 3.0

I have an on-premise Dynamics CRM (2016) that is configured with ADFS (3.0). When a user want's to Login, they get redirected to the ADFS login page and the user enter their Windows AD credentials.

From a .net core application I need to make request to the CRM api using HttpClient. When I try to send the credentials like I normally would for a Windows Auth CRM it doesnt work. I get a 401 Unauthorized. Like below.

HttpClient client = new HttpClient(new HttpClientHandler() { Credentials = new NetworkCredential("myuser", "mypassword", "mydomain") });
var result = client.GetAsync("https://mycrmaddress/api/data/v8.0/accounts");

I also tried using Adal to retrieve a token and attach it as a bearer token to the request but I'm unable to get a token with adal. When I try I receive the following:

The authorization server does not support the requested 'grant_type'. The authorization server only supports 'authorization_code'

ADFS 3.0 doesn't support this flow.

I cannot upgrade to ADFS 4.0 so I would like to know what are my options to make an authenticated call to CRM api (without prompting a login window as this application is a service).

Is there any configuration I can do on ADFS so my first example work? Or is it possible to do it with Adal even if it's ADFS 3.0? Or any other solution...

Upvotes: 3

Views: 2746

Answers (1)

Mathieu A.
Mathieu A.

Reputation: 103

I found the answer to my question. It's kinda hackish, but I tested it myself and it works. As a temporary solution this will do the trick.

Details are available here: https://community.dynamics.com/crm/f/117/t/255985


ADFS 3.0 supports the Authorization Code flow and this what we will use in this case.

  • We need to retrieve an authorization code. Normally at this steps a windows is prompted to the user to enter its credentials. By doing a POST and sending the user/password it's possible to retrieve an authorization code.

    • {authProvider} - ADFS Uri - something like https://adfs.mycompany.com/adfs/oauth2/
    • {ClientId} - The Guid used to by your infrastructure team to add your application to ADFS
    • {RedirectUri} - The IFD Uri for dynamics - should match the redirect Url used to by your infrastructure team to add your application to ADFS
    • username - The User set up on ADFS and in Dynamics
    • password - The password for the above user

Then we make the following call with these information using HttpClient.

var uri = $"{authProvider}authorize?response_type=code&client_id={clientId}&resource={redirectUri}&redirect_uri={redirectUri}";

var content = new FormUrlEncodedContent(new[] {
    new KeyValuePair<string,string>("username",username),
    new KeyValuePair<string,string>("password",password),
});

var responseResult = _httpManager.PostAsync(uri, content).Result;

The response content will be an html page (Remember normally this flow prompts a login page to the user). In this page there will be a form that contains the authorization code. using a library like HtmlAgilityPack retrieve the token. This is the hackish part of the solution.

  1. Now that we have an authorization code we need to retrieve an access token.

For that we need to make the following call

var uri = $"{authProvider}token";
            var content = new FormUrlEncodedContent(new[] {
                new KeyValuePair<string,string>("grant_type","authorization_code"),
                new KeyValuePair<string,string>("client_id",clientId),
                new KeyValuePair<string,string>("redirect_uri",redirectUri),
                new KeyValuePair<string,string>("code",code)
            });

var response = await _httpManager.PostAsync(uri, content);

The response content will be a json string that will contain the access token.

  1. With the access token, make the call to CRM rest API.

You will need to attach the token to the HttpClient in the header as a bearer token.

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");

From now on you can make calls to CRM api and you will be authorized. However be carefull normally access token are short lived. You will either need to increase their lifetime or request a new token everytime it's expired.

Upvotes: 3

Related Questions