{KnowHow}–Authentication with Dynamics CRM online Web API without user login screen– Where headless authentication works and where not?

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.

https://debajmecrm.com/2016/02/23/understanding-in-depth-cross-origin-resource-sharing-cors-in-dynamics-crm-2016/

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.

https://github.com/Azure-Samples/active-directory-dotnet-native-headless

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

http://www.cloudidentity.com/blog/2014/07/08/using-adal-net-to-authenticate-users-via-usernamepassword/

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.

image

 

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

image

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 helps!

Advertisements

About Debajit
I am a Dynamics CRM Most Valuable Professional (MVP) with 10 years of experience in Microsoft .NET Technologies and 7 years of dedicated experience in Microsoft Dynamics CRM. I have worked with companies like Microsoft, SanDisk, PwC, TMF Group and have extensive experience of implementing complex CRM solutions from both offshore and client side. Currently the face of XrmForYou.com with significant experience in delivering corporate training on Dynamics CRM and have already delivered multiple projects to client through XrmForYou.com Author of multiple tools on codeplex including the 'Role Based Views' and 'CRM-Sharepoint Metadata manager & Attachment Extractor' which are available for commercial use under XrmForYou.com For consulting/ training, drop me a note at info@xrmforyou.com or visit our website www.xrmforyou.com

12 Responses to {KnowHow}–Authentication with Dynamics CRM online Web API without user login screen– Where headless authentication works and where not?

  1. 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

    Access Denied (authentication_failed)

    Your credentials could not be authenticated: “Credentials are missing.”. You will not be permitted access until your credentials can be verified.

    This is typically caused by an incorrect username and/or password, but could also be caused by network problems.

    For assistance, contact your network support team.

    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.”}

    • Debajit says:

      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.

  2. 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 ?

  3. Christopher says:

    How do I do the opposite? I want CRM to manage sigin activities, not my app.

  4. Pankaj vats says:

    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

  5. Chamali says:

    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!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: