Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unauthorized HTTP request to Dynamics CRM Web API via Angular 2 using ADAL

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:

enter image description here

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:

Response 1

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

Response 2

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.

like image 423
Dave Clark Avatar asked Oct 17 '22 14:10

Dave Clark


1 Answers

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.

Update

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:

enter image description here

Append the code demo to test:

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

like image 125
Fei Xue - MSFT Avatar answered Nov 04 '22 20:11

Fei Xue - MSFT