Yes I did find a solution, in the end I hade to read all the ADFS developer notes and fully understand how the ADFS api worked. It is not possible at all to use the out of the box library provided my Microsoft, I ended up developing my own.
I am assuming you are going to be hitting the Dynamics api from c#?
If you are not using c# the same principles will still apply.
So firstly you will need the following prerequisites
1. An active directory user setting up for use by the api, this user will need to be set up on ADFS with access to the IFD facing dynamics Uri. This user will also need to be set up in dynamics and have the necessary read/ write / create permissions to entities that the api will be manipulating. You will need to know both the user name and password for this user
2. Your infrastructure team will need to add a record to ADFS for your application - this is done through a PowerShell command, a client id will be required (Guid) and redirect Uri will be required (we used the IFD Uri) - There are tech net article on this process
Retrieving an access token from ADFS is a 2 stage process and I would suggest wrapping this process up into at least its own class, I developed and entirely separate c# project which has made the code easily reusable. The 2 stages are Authorize and Token retrieval
Authorize Process
So before you can claim a token form ADFS you need to Authorise the user against it. The ADFS api documentation outlines this as a 2 stage process, a http GET request followed by a http POST request which when completed will give you the authorization code you will need to retrieve the ADFS token . I discovered through a process of making various http requests to AFDS and analysis the response data, that it is actually possible to complete the authorization process with 1 http POST request.
You will need to make a post request to your ADFS server with a very specific query string and content and will need the following
- {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
The following will retrieve the token
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;
N.B the above example uses PostAsync(uri, content).Result using await _httpManager.PostAsync(uri, content); does work but I did encounter some odd behaviour where ADFS did not always return anything.
The Response result is actually a html form which you will need to intercept and read.
There are some hidden input tags within this html the 3rd ( name = wctx) having a value that will look something like
ru=https%3a%2f%2fcrmDynamicsUri.com%2fdefault.aspx%3fcode%3xxxxcodexxxxx%2fdefault.aspx
you will need to extract the value xxxxcodexxxxx which will be a 300+ alphanumeric string.
I used the HtmlAgilityPack to aid extracting the input tag out of the Html response and some good old fashion string manipulation to get the code form the inputs value.
Once you have the code you can make the retrieve Token request to ADFS - Note the Authorization Code has a very short lifespan - I found it did not appear to be valid for more than a few minutes when I was analysing this process.
Retrieve Token Process
Once you have your authorization code the second request to ADFS is more straight forward
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);
will return a json response containing your ADFS token , the token type and an expires in value, you only need to token which needs to be included in the header of any request made to the Dynamics api.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
That's pretty much it. It is worth noting that the token has a 30 min lifespan and should be reused during that period. It was discovered that if repeated requests where made to ADFS it would stop sending the authorization code required to get the next token - often 15 requests within 5 seconds was sufficient for ADFS to stop responding - This is likely inbuilt security to prevent 'spamming' ADFS. With that in mind I included caching within the c# project I developed so if the code was called within the lifespan of the last token it returned that token rather than making a fresh call to ADFS - Microsoft.Extensions.Caching.Memory would be a good starting point.
Hopefully that will be enough to get you going.
many regards
T