I have wrote blogs on how to execute call web-api from HTML page as well as Web Application sometime back. And while I truly appreciate the vast number of people writing back to me on how much they liked the blog, there were asks of how to acquire the access token seamlessly for Dynamics CRM Online, that is without user intervention.
For readers who are not aware of OAuth concepts and how it works in CRM perspective, please visit the below link.
The above post first gives you an introduction to the OAuth 2.0 protocol and then how to use that to connect to Dynamics CRM Web API endpoint from an external HTML Page. The post also covers on how to register the application in azure AD and get the client id and the client secret. Before you read further I would strongly suggest you go through the above post and understand how OAuth 2.0 works from CRM perspective.
So coming back to the topic, I put my head around a lot to get headless authentication from my web application but could not make it through. Basically, I was doing similar to what is mentioned in the below git-hub article, but somehow could not make it work from my web application. The git-hub mentions about the new ADAL library which introduces the classes for the headless authentication.
By chance one of the community discussions in Dynamics CRM group of facebook was discussing on a topic similar to this, I was directed to the original article on this topic
I carefully read through the entire post and finally after reading the limitations section I could understand why it is not working for my web application, although I am able to make it work from my WPF application or a simple c# console client.
First the positive one. How to make it work for native client applications. Below is the example for the same.
As a client I chose a C# console client. I registered my client in Azure AD and allowed implicit authorization in the manifest of the application. Also I allowed my application access to my Online Microsoft CRM instance. Getting bit confused about all this. You can always go back to the first link I shared in this post and understand. All these are explained in great details.
Everything is ready. So let’s get a bit dirty here with code.
Below is the code for my application. I am just retrieving the accounts and displaying the account names. CRM instance URL is – https://xrmtr20.crm.dynamics.com
static void Main(string[] args)
{
UserCredential userCredential = new UserCredential(“<office 365 user name>”, “<Office 365 Password>”);
string authorityUri = “https://login.windows.net/xrmtr20.onmicrosoft.com/oauth2/authorize”;
TokenCache tokenCache = new TokenCache();
AuthenticationContext context = new AuthenticationContext(authorityUri);
AuthenticationResult result = context.AcquireToken(“https://xrmtr20.crm.dynamics.com”, “<client id obtained after registering with Azure AD>”, userCredential);
LoadAccounts(result.AccessToken);
Console.Read();
}
static void LoadAccounts(string accessToken)
{
var webRequest = (HttpWebRequest)WebRequest.Create(new Uri(“https://xrmtr20.crm.dynamics.com/api/data/v8.0/accounts?$select=name,address1_city&$top=10″));
webRequest.Method = “GET”;
webRequest.ContentLength = 0;
webRequest.Headers.Add(“Authorization”, String.Format(“Bearer {0}”, accessToken));
webRequest.Headers.Add(“OData-MaxVersion”, “4.0”);
webRequest.Headers.Add(“OData-Version”, “4.0”);
webRequest.ContentType = “application/json; charset=utf-8”;
using (var response = webRequest.GetResponse() as System.Net.HttpWebResponse)
{
//Get reader from response stream
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
{
string responseContent = reader.ReadToEnd();
dynamic dynamicObj = JsonConvert.DeserializeObject(responseContent);
foreach (var data in dynamicObj.value)
{
Console.WriteLine(“Account: {0}”, data.name.Value);
}
}
}
}
And these are the list of accounts.
Let’s explain the code a bit here. To use this code you have to add reference to Active Directory Authentication Library (ADAL) and then NewtonSoft.json.dll
In visual studio open Tools –> NuGet Package manager –> Package manage console
In the console, type the following command
Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory -Version 2.7.10707.1513-rc –Pre
The package will be installed successfully. In the same way you can add reference to Newtonsoft.json.dll using the Nuget.
Once done, now its time to review the code.
The first improvement is the UserCredentials which has a provision for taking both the username and the password. The authorityuri in the code is basically the login screen of windows live to login to your dynamics crm application. If you carefully notice the URL here, it contains xrmtr20.onmicrosoft.com. This is the AD tenant you need to access to get the token.
Also the new ADAL API introduces new overloads of the AcquireToken.
public AuthenticationResult AcquireToken(string resource, string clientId, UserCredential userCredential);
public Task<AuthenticationResult> AcquireTokenAsync(string resource, string clientId, UserCredential userCredential)
I have utilized both the above to get the access token. The remaining code just passes the access token to CRM to fetch the account data. Some mundane stuff.
Oh! So far so good and it works great with native client applications like WPF based application or C# console client. Unfortunately, I was not able to make it work from my web application project. The area where I got stuck is, a web application project would need to use the client secret as well apart from the client id to get the access token and I could not find a overload of acquire token which would take the UserCredential object along with the client secret.
Just copying some of the limitations of headless authentication from one of the previous links I shared in this post. The content is as-is from the link.
Constraints & Limitations
Here there’s a list of limitations you’ll have to take into account when using this flow.
Only on .NET
Given the intended usage of this feature, we decided to add it only to .NET.
On Windows Store we added the ability to use Windows Integrated auth, which has many of the same advantages and less drawbacks. Details in another post.
No web sites/confidential clients
This is not an ADAL limitation, but an AAD setting. You can only use those flows from a native client. A confidential client, such as a web site, cannot use direct user credentials.
No MSA
Microsoft accounts that are used in the context of an AAD tenant (classic example: Azure admins) cannot authenticate to AAD via raw credentials – they MUST use the interactive flow (though the PromptBehavior.Never flag remains an option).
No MFA
Multi-factor authentication requires dynamic UX to be served on the fly – that clearly cannot happen in this flow.
No Consent
Users do not have any opportunity of providing consent if username & password are passed directly.
No multi-hop federation
Any scenario requiring home realm discovery, multiple federation hops and similar won’t work – the protocol steps are rigidly codified in the client library, with no chance for the server to dynamically influence the authentication path.
No any server side features, really
In the “traditional” AcquireToken flows you have the opportunity of injecting extra parameters that will influence the behavior of AAD – including parameters that AAD didn’t even support when the library was released. None of that is an option when using username and password directly.
I would be happy if someone can get this working for me.
Hope this helped!
Debajit Dutta
Business Solutions MVP
Discover more from Debajit's Power Apps & Dynamics 365 Blog
Subscribe to get the latest posts sent to your email.
Hi Debajit,
Tried couple of ways to test the console app, but getting authentication issues.
opt1 – authorityUri=https://login.microsoftonline.com/jksoft123.crm4.dynamics.com/oauth2/authorize
UserCredential userCredential = new UserCredential(“”, “”);
string authorityUri = “https://login.microsoftonline.com/jksoft123.crm4.dynamics.com/oauth2/authorize”;
TokenCache tokenCache = new TokenCache();
AuthenticationContext context = new AuthenticationContext(authorityUri);
AuthenticationResult result = context.AcquireToken(“https://jksoft123.crm4.dynamics.com”, “f3aaf9d8-baca-4f0d-a88d-1fa6e1ce694f”, userCredential);
LoadAccounts(result.AccessToken);
Error
‘authority’ is not in the list of valid addresses. unknown_error (login.microsoftonline.com):
Access Denied
opt2 –
authorityUri=”https://login.windows.net/jksoft123.onmicrosoft.com/oauth2/authorize”;
UserCredential userCredential = new UserCredential(“”, “”);
string authorityUri = “https://login.windows.net/jksoft123.onmicrosoft.com/oauth2/authorize”;
TokenCache tokenCache = new TokenCache();
AuthenticationContext context = new AuthenticationContext(authorityUri);
AuthenticationResult result = context.AcquireToken(“https://jksoft123.crm4.dynamics.com”, “f3aaf9d8-baca-4f0d-a88d-1fa6e1ce694f”, userCredential);
LoadAccounts(result.AccessToken);
Error
user_realm_discovery_failed: User realm discovery failed
{“The remote server returned an error: (407) Proxy Authentication Required.”}
Hi,
Just confirming, prior to running this code, did you do the following steps
1. add your Dynamics Online directory to Azure
2. When you registered your application with Dynamics Online AD, did you allow implicit authorization. Also I assume you tried this from a native client like Console or WPF
3. Did you provide access to the Dynamics CRM online instance for your app?
In case you have missed any of the above, complete that and try with option2 again. It should work. If not, let me know.
Regards
Debajit
I have gone through your first post and it works fine for the web client. But for the Console app, i am getting the above errors, yes, points 1,2,3 are all completed.
Hi Debajit, Thank you for your efforts in maintaining this tutorial. I am learning Dynamic CRM and trying to get the access token of the current session when user actively using online CRM instance. Every api talks only about taking token using url and callback. Can you guide me in right direction ?
Hi Ramnivas,
To get the token we have to use OAuth. Now OAuth itself is a URL based protocol based on client_id and client_secret. Now the question is when you are trying to access the token, it is from which context. Are you trying to access the token from within dynamics only or from an external application that connects with Dynamics?
Cheers
Debajit
Hi Debajit,
Thank you for reply, I am trying to get it from within dynamics itself.
Thanks,
Ramnivas
Hi Debajit, This is what I am trying to achieve.
https://community.dynamics.com/crm/f/117/p/213158/563618
Thanks,
Ramnivas
How do I do the opposite? I want CRM to manage sigin activities, not my app.
Hi Christopher,
Sorry for the delayed reply as I was out on vacation.
I think what you are trying to achieve is Dynamics CRM would like an identity provide for your APP. Probably this link helps.
https://debajmecrm.com/2016/03/01/configure-your-dynamics-crm-as-identity-provider-for-an-external-web-application/
Hi Debajit
Please help me out !!!
1. I have Rest Api and its hosted on our Server
2. I want to Integrate my Rest Api with Dynamic CRM 365.
Scenario:-
Suppose when user create any Accounts/Contacts in Dynamic CRM 365 and fill some value in Postal-Code/Zip-Code and click on Save Button.
On Click of save button , validate this Postal-code through Rest Api which is hosted on other server and API send respose to the Dynamic CRM 365 which is valid Postal code or not
Hi Debajit..Thanks for this tutorial which saved me a lot of time. I too was trying access CRM web APIs in a web application and had no luck to find an overload method which uses both client id and client secret (which is really needed for a web application) What I finally did was to self host WCF services in a console application and register it as a native app in AAD. It worked great!
Thanks Chamali. It’s really great to know that it has helped you!