0
votes

I am trying to create a single page web application that authenticates with Dynamics 365 for Operations through Azure AD. I have followed all instructions to register my application in Azure AD and within Dynamics 365. I have successfully authenticated with Azure AD and received a valid token in my application using adal javascript library, however, when I make a request to the OData endpoint in D365, I get a 401 error. I'm authenticated with Azure AD, and I'm passing my token in the authentication header, so I can't figure out why I'm getting an authentication error unless maybe I'm not setting my authorization headers correctly? Here is my request:

//Function that actually retrieves the accounts
function retrieveAccounts(error, token) {
// Handle ADAL Errors.
if (error || !token) {
    errorMessage.textContent = 'ADAL error occurred: ' + error;
    return;
}

var req = new XMLHttpRequest()
req.open("GET", encodeURI(organizationURI + "/data/Projects"), true);
//Set Bearer token
req.setRequestHeader("Authorization", "Bearer" + token);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
    if (this.readyState == 4 /* complete */) {
        req.onreadystatechange = null;
        if (this.status == 200) {
            var projects = JSON.parse(this.response).value;
            renderProjects(projects);
        }
        else {
            var error = JSON.parse(this.response).error;
            console.log(error.message);
            errorMessage.textContent = error.message;
        }
    }
};
req.send();
}  

Any ideas would be much appreciated! I was able to do this successfully with Dynamics 365 CRM using the web api, so I'm assuming that it will work for Operations as well even though the setup is a little different.

3
Hi Josh- good catch, yes, you are correct. This was actually a copy and paste error into the code block. You do need the space. However, I learned later that this method of authentication(JavaScript) is not supported yet by D365-Ops. There is an issue with CORS which prevents a successful call.James Houck

3 Answers

0
votes

I believe that this method will not work and the issue is that the D365 Operations OData endpoint needs to be authenticated with a client secret. In this method, the JWT token that is being passed as a bearer token in the call to the service is not signed with the client key. I don't think there is any secure way to do this from a single web app. I'm going to close this down because I believe the answer is to create a second web api app that takes care of the calls, server side, and passes the data back to my SPA

0
votes

This looks like a simple typo:

//Set Bearer token
req.setRequestHeader("Authorization", "Bearer " + token);

Notice the space after "Bearer"

0
votes

I was facing the same issue but after doing some research my integration with CRM using Web API finally got successful.

Try using this procedure :-

    int                             find;
    real                            inventOnHandQtyval;
    str                             url,aosUri,activeDirectoryTenant;
    str                             activeDirectoryClientAppId;
    str                             activeDirectoryClientAppSecret,json;
    str                             postData,activeDirectoryResource;
    str                             aadClientAppSecret,oAuthHeader;
    str                             prdGUIDCode,prodnum;
    str                             returnValue,jsonString,jsondszstr;

    System.Net.HttpWebRequest       request;
    System.Net.HttpWebResponse      response;
    System.Byte[]                   byteArray;
    System.IO.Stream                dataStream;
    System.IO.StreamReader          streamRead;
    System.IO.StreamWriter          streamWrite;
    System.Net.ServicePoint         servicePoint;
    System.Net.ServicePointManager  servicePointmgr;
    System.Net.HttpVersion          version;
    System.Web.Script.Serialization.JavaScriptSerializer jsonSerializer;
    CLRObject                       clrObj;
    Newtonsoft.Json.JsonReader      reader;
    Newtonsoft.Json.Linq.JObject    prod;

    System.Text.Encoding            utf8;
    System.Exception                ex;
    Counter                         countCounter;
    Object                          obj;
    Map                             data;
    EcoResProduct                   ecoresproductfetch;
    EcoResProductTranslation        ecoresproductTranslation;
    InventTableModule               inventTableModule;




    try
    {
        InventTable     inventtable = sender as InventTable;

        System.Net.WebHeaderCollection  headers = new System.Net.WebHeaderCollection();
        var jsonSerializerobj = new  System.Web.Script.Serialization.JavaScriptSerializer();
        prod = new Newtonsoft.Json.Linq.JObject();
        inventOnHandQtyval = InventOnhand::newItemId(inventtable.ItemId).availPhysical();
        select inventTableModule where inventTableModule.ItemId==inventtable.ItemId;
        prod.Add("name",inventtable.itemName());
        prod.Add("productnumber",inventtable.ItemId);
        prod.Add("[email protected]","/uomschedules(7F6F7338-1D80-4E90-9110-A70897C73834)");
        prod.Add("[email protected]","/uoms(68A2E342-E1CA-4CC2-B430-C05057BCE7BC)");
        prod.Add("currentcost",0.00);
        prod.Add("description",inventtable.itemName());
        prod.Add("msdyn_fieldserviceproducttype",690970000);


        json=Newtonsoft.Json.JsonConvert::SerializeObject(prod);

        System.IO.MemoryStream mem = new System.IO.MemoryStream();



        new InteropPermission(InteropKind::ClrInterop).assert();



        headers = new System.Net.WebHeaderCollection();

        url = "https://abc.crm4.dynamics.com/api/data/v8.1/details";

        clrObj = System.Net.WebRequest::Create(url);

        aosUri = "https://login.windows.net/abcd1234/oauth2/authorize?";

        activeDirectoryTenant = "https://login.microsoftonline.com/abcd134/oauth2/token";

        activeDirectoryClientAppId = "xyz124";



        activeDirectoryResource = "https://abc.crm4.dynamics.com";

        Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authenticationContext =
                                new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(activeDirectoryTenant);



        var userCredential = new  Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential("[email protected]", "abc@1234");

        Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult authenticationResult =
            authenticationContext.AcquireTokenAsync(activeDirectoryResource, activeDirectoryClientAppId, userCredential).Result;


        headers.Add("Authorization", authenticationResult.CreateAuthorizationHeader());

        headers.Add("Prefer", "return=representation");
        request = clrObj;

        request.Headers=headers;

        utf8 = System.Text.Encoding::get_UTF8();
        byteArray = utf8.GetBytes(json);
        request.set_Method("POST");
        request.set_KeepAlive(true);

        request.ContentType="application/json";

        request.set_ContentLength(byteArray.Length);







        servicePoint = request.get_ServicePoint();

        System.Net.ServicePointManager::set_Expect100Continue(false);

        System.Net.ServicePointManager::set_SecurityProtocol(System.Net.SecurityProtocolType::Tls12);



        dataStream = request.GetRequestStream();

        dataStream.Write(byteArray, 0, byteArray.get_Length());

        response = request.GetResponse();



            dataStream = response.GetResponseStream();


            streamRead = new System.IO.StreamReader(dataStream);

            jsonString = streamRead.ReadToEnd();



            data=RetailCommonWebAPI::getMapFromJsonString(jsonString);

            prdGUIDCode = data.lookup("productid");

            prodnum = data.lookup("productnumber");           

    }

    catch(Exception::CLRError)
    {
        ex = CLRInterop::getLastException().GetBaseException();
        error(ex.get_Message());
    }





    dataStream.Close();

    response.Close();