Microsoft Web API has delighted all and specially if you have a penchant towards exploring the API features, then Web API must have thrilled you by this time. Many consultants and experts, including myself, have written blogs on how to leverage the Web API features in Dynamics CRM.
But all these far, all the examples have been written in javascript. To elaborate most of the examples are on
- Using web API from within Microsoft Dynamics CRM webresources
- Using web API from an external HTML page using javascript and adal.js
Web API uses OAuth 2.0 protocol for authorization. However when you are making Web API calls from within Dynamics CRM, the entire nuances of OAuth 2.0 authorization is hidden from you. All you need to do is send a well formatted request and get the response back, parse the json response and get the data back in the application to use.
For the second example, although it will expose you greatly to the nuances of OAuth 2.0 protocol but there is a subtle difference when you execute Web API calls from browser based application (like javascript/ jquery) and from a server based application like ASP.NET web application.
The main difference is that in the first case, you would not need the client secret to get the access token. The authentication is implicit and you get the access token in just one call to the authorization endpoint. For the second case you would need to first get the authorization code by asking the end-user to enter his credentials and then once you get the authorization code, you would need to make a second call to the token endpoint to get the access token using authorization code + client_id + client_Secret.
If you are not aware of the OAuth 2.0 mechanisms, I would strongly suggest to first check for the OAuth documentations or you may go through my below blog post.
https://debajmecrm.com/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.
After I wrote the above blog post, I received lot of requests personally if I can provide and code of how to make the same call from ASP.NET web application. Well your wish is granted. Here I will show you end-end on how to fetch data from Dynamics CRM Web API from a ASP.NET web application.
- Create a ASP.NET web application project with a single web page. I named it default.aspx.
- Go to the design mode and the markup should look like below.
- A Login button which when the user clicks will take him/ her to the authentication screen.
- The label will display the name of the user once the user is authenticated.
- Get Accounts Button will retrieve the list of accounts when clicked.
- GridView will show the list of accounts retrieved once the Get Accounts button is clicked.
- Run the page is visual studio. For me it is http://localhost:52432/default.aspx. This will be used as the redirect uri after the user has authenticated successfully from the login screen. Since I am using a single web form here, I am redirecting to the same page. You can actually redirect to any other page in the application.
- Register this application in the Dynamics CRM Active Directory in azure management portal. Not sure how to do this. Please follow this blog link – https://debajmecrm.com/understanding-in-depth-cross-origin-resource-sharing-cors-in-dynamics-crm-2016/.
The above link explains how to add Dynamics CRM AD in Azure and register the application in Dynamics AD and how to get the client id and the client secret. Please make a note of the client id and specially the client secret which is visible immediately after saving. Please note that the client secret is visible only for the first time. If you come back later, you won’t be able to view it.
- For this demo, I will keep these values in the web.config file. However do not keep the client id and client secret in the web.config specially the client secret. It’s very sensitive information. You should have some better ways of managing the same.
<appSettings>
<add key=”client_id” value=”f8a39dc3-338b-4018-b67e-6dbaa846f469″/>
<add key=”client_secret” value=”<your client secret>”/>
<add key=”tenant” value=”xrmtr14.onmicrosoft.com”/>
<add key=”redirect_url” value=”http://localhost:52432/default.aspx”/>
<add key=”resource” value=”https://xrmtr14.crm.dynamics.com”/>
</appSettings>
- Now add a reference to the Active Directory authentication library and NewtonSoft.json.dll. The best way to do this is using Nuget Packages. For Active Directory Authentication Library type ‘Microsoft IdentityModel’ in the search box.
- Now come to code-behind file (default.aspx.cs) and copy the entire code from below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net;
using System.Web.Script.Serialization;
using Newtonsoft.Json;
namespace DynamicsCRMWebApiConnector
{
public partial class _default : System.Web.UI.Page
{
const string AuthorityUri = “https://login.windows.net/{0}/oauth2/authorize”;
const string CompleteAuthUri = “https://login.windows.net/{0}/oauth2/authorize?response_type=code&client_id={1}&resource={2}&redirect_uri={3}”;
Configurations _configuration;
protected void Page_Init(object sender, EventArgs e)
{
this.PopulateConfigurations();
}
protected void Page_Load(object sender, EventArgs e)
{
var queryString = Request.QueryString;
// means authorization code has been received
if(queryString != null && queryString[“code”] != null)
{
var authCode = queryString[“code”];
// get the access token using the authorization code
TokenCache tokenCache = new TokenCache();
AuthenticationContext authContext = new AuthenticationContext(string.Format(AuthorityUri, _configuration.ADTenant), tokenCache);
ClientCredential clientCredentials = new ClientCredential(_configuration.ClientId, _configuration.ClientSecret);
AuthenticationResult authResult = authContext.AcquireTokenByAuthorizationCode(authCode, new Uri(_configuration.RedirectUri), clientCredentials);
if(authResult.UserInfo != null)
{
this.UserInfoLabel.Text = string.Format(“Welcome: {0} {1}”, authResult.UserInfo.GivenName, authResult.UserInfo.FamilyName);
Session[“AuthResult”] = authResult;
}
}
}
protected void Login_Click(object sender, EventArgs e)
{
var authorityUri = this.GetAuthorizationUrl();
Response.Redirect(authorityUri);
}
protected void GetAccountsButton_Click(object sender, EventArgs e)
{
if (Session[“AuthResult”] != null)
{
var authResult = (AuthenticationResult)Session[“AuthResult”];
var webRequest = (HttpWebRequest)WebRequest.Create(new Uri(“https://xrmtr14.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}”, authResult.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()))
{
var accounts = new List<Account>();
string responseContent = reader.ReadToEnd();
dynamic dynamicObj = JsonConvert.DeserializeObject(responseContent);
foreach(var data in dynamicObj.value)
{
var account = new Account
{
AccountName = data.name.Value,
City = data.address1_city != null ? data.address1_city.Value : string.Empty
};
accounts.Add(account);
}
AccountsGridView.DataSource = accounts;
AccountsGridView.DataBind();
}
}
}
}
#region Methods
private void PopulateConfigurations()
{
_configuration = new Configurations
{
ADTenant = ConfigurationManager.AppSettings[“tenant”],
ClientId = ConfigurationManager.AppSettings[“client_id”],
ClientSecret = ConfigurationManager.AppSettings[“client_secret”],
RedirectUri = ConfigurationManager.AppSettings[“redirect_url”],
Resource = ConfigurationManager.AppSettings[“resource”]
};
}
private string GetAuthorizationUrl()
{
return string.Format(CompleteAuthUri, _configuration.ADTenant, _configuration.ClientId, _configuration.Resource, Server.UrlEncode(_configuration.RedirectUri));
}
#endregion
}
}
public class Account
{
public string AccountName { get; set; }
public string City { get; set; }
}
public class Configurations
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string ADTenant { get; set; }
public string Resource { get; set; }
public string RedirectUri { get; set; }
}
- I have created two classes ‘Account’ and ‘Configurations’ in the above the code. Check for the class definitions just above this line.
- So let me explain the code a bit here. In the Page_Init method, I retrieve all the values from the web.config file and populate the Configurations class’s object with those values.
- Next look for the code in the Login Button Click handler Login_Click. First we get the URI where the user would be redirected to authorize himself. In that URI we pass the redirect url as well so that when the authentication completes, the control comes back to this page only.
- Once the user authenticates, the control will come back to this page and the Page_Load event would fire. The AuthorizationCode would be present in the querystring of the url with the key=code.
- the Page_Load checks for the authorization code in the query string and then it send another request for the access token. This time it sends the authorization code + client id + client secret. The code makes use of the Microsoft Active Directory Authentication library to get the access token.
- Finally when the ‘Get Accounts’ button is clicked, a HttpWebRequest is made with the token specified in the header of the request and we get the list of accounts. If everything works fine, your final thing would look something like below.
Hope this helps!
Discover more from Debajit's Power Apps & Dynamics 365 Blog
Subscribe to get the latest posts sent to your email.
Hi Debajit,
Your post is very informative. Can you provide more details on how authentication is handled while executing services that are hosted on our network from CRM Cloud from single page HTML applications?
Hi,
Thanks.
I think what you are looking for, the following blog post of mine should help you.
https://debajmecrm.com/2016/02/23/understanding-in-depth-cross-origin-resource-sharing-cors-in-dynamics-crm-2016/
Hi, can you provide me more details for on-premise implementation using web-api and asp.net
Your on-premise is implemented through ADFS or its just integrated windows authentication?
in our case we have ADFS wit onpremise CRM 2016.
what is the best way to authenticate without user intervention ?
Hi elifesy
thanks for reading my blog.
With on-premise on IFD, the basic concept still remains the same like registering the app and getting the client id. However azure active active directory gets replaced with ADFS here.And you would need to register your client with ADFS using Powershell.
Please follow this link..It will give u a decent idea on how to do this.
Hi elifesy,
I missed the link itself.
https://msdn.microsoft.com/en-us/library/dn531010.aspx
-Debajit
hi,
having trouble with AcquireTokenByAuthorizationCodeAsync
Cannot implicitly convert type ‘System.Threading.Tasks.Task’ to ‘Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult’
Seems like reference issues. Did you install the ADAL (Active Directory Authentication Library) from nuget as directed?
Hi Debjit,
As I am trying to execute web Api calls to CRM, it is showing the error on authContext.AcquireTokenByAuthorizationCode,
Can you please suggest me any idea what is the actual reason behind this error.
I have added the using Microsoft.IdentityModel.Clients.ActiveDirectory package.
Error 2 ‘Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext’ does not contain a definition for ‘AcquireTokenByAuthorizationCode’ and no extension method ‘AcquireTokenByAuthorizationCode’ accepting a first argument of type ‘Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext’ could be found (are you missing a using directive or an assembly reference?) C:\Users\kundan.a\Documents\Visual Studio 2012\WebSites\WebSite6\Default.aspx.cs 39 59 WebSite6
Hi Debajit,
What if the crm is on-premises and not configured using ifd? Can web api still be called using this method?
Hi Diaz,
Without IFD and with on-premises actually there is no need. You can just query your webapi from any external application using network credentials. Sample code:
WebRequest webRequest = WebRequest.Create(“”);
webRequest.Credentials = new System.Net.NetworkCredential(“”, “”, “”);
WebResponse response = webRequest.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader sr = new StreamReader(dataStream);
var streamText = sr.ReadToEnd();
Hi Debjit,
it gives me Unauthorize error (401)
every thing i follow as suggested
Hi Debajit,
I am waiting for your reply……………
Thanks in advance
Hi Ayan,
Thanks for reading my blog. Usually authorization fails if we fail to provide a valid client secret. Can u inbox your code to debajit.prod@gmail.com
Hi Deajit,
I want to create a simple external asp.net (webapi based) that will connect to on-premise crm. The website need to provide a case form and will authenticate against the crm contact table (Full name) and additional field with custom password.
Do you have any sample solution that I could download.
————
I tried to use this sample contact form approach, but the asp.net web form does not display customer field: https://msdn.microsoft.com/en-us/library/gg695790.aspx
Can I send you my solution to have a look?
Hi Sam,
Sorry I was travelling and completely out of emails.
The link you provided is a good example. customer field refers to the account entity. In CRM terms it is of type EntityReference which is different from the string fields like First Name/ Last Name etc as shown in the example.
You may check on how to parse EntityReference fields in Dynamics. That would be of help.
-Debajit