I'm trying to create an application using Angular that can connect to Dynamics CRM via the Web API. These are the steps I have followed:
1. Registered a native application in Azure, granted the required delegated permissions and updated the manifest to allow implicit flow.
2. Created an Application User in CRM, setting its Application ID equal to the Client ID of my Azure registered application. Assigned my Application User a custom Security Role.
3. Cloned many Angular 2 quickstart Git repositories which authenticate with Azure AD via ADAL such as this one.
4. Updated the cloned code's adal config, setting my tenant
, clientId
, redirectUri
and endpoints
.
So far this has been successful. I'm able to launch the application and login via my browser either as my Application User or a different CRM User which is a part of Azure AD. This returns a token.
5. Attempt to send an http.get
to either v8.0
or v8.2
(I'm told that v8.2
does not support cross-domain calls):
getEntities(): Promise<any> {
let token = this.adalService.getCachedToken(this.adalService.config.clientId);
let headers = new Headers({
'Authentication': 'Bearer ' + token,
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8',
'OData-MaxVersion': '4.0',
'OData-Version': '4.0'
});
let options = new RequestOptions({ headers: headers });
return this.http.get(`${crmURL}/api/data/v8.2/accounts`, options)
.toPromise()
.then((res) => { return res; })
.catch((e) => { console.error(e); });
}
6. Receive this error message:
It reads:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:3000' is therefore not allowed access.
The response had HTTP status code 401.
Looking at my Chrome browser's network tab, I receive two responses:
General
Request URL:https://ms-dyn365-prevxxxxxx/api/data/v8.2/accounts
Request Method:OPTIONS
Status Code:200 OK
Remote Address:104.44.xxx.xxx:xxx
Referrer Policy:no-referrer-when-downgrade
Headers
Access-Control-Allow-Headers:authentication,content-type,odata-maxversion,odata-version
Access-Control-Allow-Methods:GET
Access-Control-Allow-Origin:http://localhost:3000
Access-Control-Expose-Headers:Preference-Applied,OData-EntityId,Location,ETag,OData-Version,Content-Encoding,Transfer-Encoding,Content-Length,Retry-After
Access-Control-Max-Age:3600
Content-Length:0
Date:Thu, 13 Apr 2017 10:08:01 GMT
Server:Microsoft-IIS/8.5
Set-Cookie:crmf5cookie=!NDyiupL55lrWWLtPQKTK52dwbxk9wdEAHeCiec0/z/7x9KWXe2dVIdQCGvL0S/HAp7F3N0OGfeWf/70=;secure; path=/
Strict-Transport-Security:max-age=31536000; includeSubDomains
Vary:Origin
X-Powered-By:ASP.NET
General
Request URL:https://ms-dyn365-prevxxxxx.crm4.dynamics.com/api/data/v8.2/accounts
Request Method:GET
Status Code:401 Unauthorized
Remote Address:104.xx.xxx.xxx:xxx
Referrer Policy:no-referrer-when-downgrade
Headers
Cache-Control:private
Content-Length:49
Content-Type:text/html
Date:Thu, 13 Apr 2017 10:08:01 GMT
REQ_ID:b2be65bc-xxxx-4b34-xxxx-5c39812650xx
Server:Microsoft-IIS/8.5
Set-Cookie:ReqClientId=xxxxxxxx-70b5-45f9-9b84-30f59481bxxx; expires=Wed, 13-Apr-2067 10:08:01 GMT; path=/; secure; HttpOnly
Strict-Transport-Security:max-age=31536000; includeSubDomains
WWW-Authenticate:Bearer authorization_uri=https://login.windows.net/xxxxxxxx-87e4-4d81-8010-xxxxxxxxxxxxx/oauth2/authorize, resource_id=https://ms-dyn365-prevxxxxxx.crm4.dynamics.com/
X-Powered-By:ASP.NET
Note: I am able to successfully access the Web API via Postman:
1. I enter https://www.getpostman.com/oauth2/callback
as a Callback URL in Azure for my Application.
2. I open Postman, set the parameters as follows and press Request Token:
Token Name: Token
Auth URL: https://login.windows.net/common/oauth2/authorize?resource=https://ms-dyn365-prevxxxxxx.crm4.dynamics.com
Access Token URL: https://login.microsoftonline.com/common/oauth2/token
Client ID: xxxxxxxx-ebd3-429c-9a95-xxxxxxxxxxxx
Callback URL: https://www.getpostman.com/oauth2/callback
Grant Type: Authorization Code
3. This opens a web page in which I login.
4. A token is returned which I add to a Postman GET header:
Content-Type: application/json
Authorization: Bearer eyJ0eXAiO...
5. Send a GET in Postman:
GET https://ms-dyn365-prevxxxxxx.crm4.dynamics.com/api/data/v8.2/accounts
6. Accounts are successfully returned.
If I use the same token within my Application I still receive a 401 error.
The Access-Control-Allow-Origin
indicates the issue is caused by cross domain.
Normally, we can use JSONP or set Access-Control-Allow-Origin
header on the server. And if both JSONP and header is not able to set since the service is provider by the third-party, we can also create a service proxy which enable to allow called from the specific orignal.
More detail about the AJAX cross domain issue, you can refer this thread.
After the further investigation, the issue is a server-side issue which relative to the specific version of REST requesting.
The 8.2
version of REST doesn't support cross domain at present. As a workaround we can use 8.0
which works well for me like the figure below:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<base href="/">
<title></title>
<script src="node_modules\angular\angular.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="HomeController">
<ul class="nav navbar-nav navbar-right">
<li><a class="btn btn-link" ng-click="listAccounts()">List account info</a></li>
</ul>
<div ng-repeat="account in accounts">
<span>name:</span><span>{{account.name}}</span>
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp',[]);
myApp.controller('HomeController', ['$scope', '$http',
function ($scope, $http){
$scope.listAccounts=function(){
var req = {
method: 'GET',
url: 'https://{domain}.crm.dynamics.com/api/data/v8.0/accounts',
headers: {
'authorization': 'Bearer eyJ0eXAiO...'
}
};
$http(req).then(function(response){
$scope.accounts=response.data.value
}, function(){
});
}
}]);
</script>
</body>
</html>
Append test code sample for Angular 2:
https://github.com/VitorX/angular2-adaljs-crm
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With