3

I'm a web developer working on the back end of my clients website rebuild. They wish to integrate their existing CRM system with the new site, so the site can perform CRUD queries against the system. However, I am struggling to successfully properly authentic with the Microsoft systems, and would like some help to plan the best strategy to get this working.

System Overview:

Server: MS Dynamics CRM 2013 (Internet facing deployment), using Active Directory for authentication

Client: LAMP (Will be runnning Drupal 7, but this is the bit I actually know how to use!)

Firstly, I have tried the authentication method described below, in the 'Discover the OAuth endpoint URL' section:

http://msdn.microsoft.com/en-gb/library/dn531009.aspx#bkmk_oauthurl

For this i used the following curl from the command line:

curl -H "Authorization: Bearer" http://crm.example.com/XRMServices/2011/Organization.svc -v

Which gives the following output:

* Hostname was NOT found in DNS cache
*   Trying xx.xx.xx.xx...
* Connected to crm.example.com (xx.xx.xx.xx) port 80 (#0)
> GET /XRMServices/2011/Organization.svc HTTP/1.1
> User-Agent: curl/7.35.0
> Host: crm.example.com
> Accept: */*
> Authorization: Bearer
>
< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Length: 3165
< Content-Type: text/html; charset=UTF-8
* Server Microsoft-IIS/8.0 is not blacklisted
< Server: Microsoft-IIS/8.0
< X-AspNet-Version: 4.0.30319
< Set-Cookie: ReqClientId=ee9c75fe-db2e-4775-a71e-c2708c46748b; expires=Tue, 14-Oct-2064 09:16:06 GMT; path=/; HttpOnly
< X-Powered-By: ASP.NET
< Date: Tue, 14 Oct 2014 09:16:06 GMT
<
<HTML>...

But no mention of a authorization-uri :(

I also tried the same command with the urls ending ‘discovery.svc’, ‘organization.svc/web’, ‘discovery.svc/web’. The latter two did include the ‘WWW-Authenticate’ field in the response, as follows:

< WWW-Authenticate: Negotiate
< WWW-Authenticate: NTLM

Which lead me to believe I was attempting the wrong method and should be looking into MS’s NTLM authentication.

I established that PHP-cURL could use NTLM, so my second approach involved sending a SOAP request from code. Similarly to the code in this StackOverflow thread:

php - access dynamics crm 2011 with web services

Here’s my code:

//sample SOAP envelope from StackOverflow thead
$SOAPEnv = <<<ENV
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
 <s:Body>
  <Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services"
           xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
   <request i:type="b:AssignRequest"
            xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts"
            xmlns:b="http://schemas.microsoft.com/crm/2011/Contracts">
    <a:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
     <a:KeyValuePairOfstringanyType>
      <c:key>Target</c:key>
      <c:value i:type="a:EntityReference">
       <a:Id>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</a:Id>
       <a:LogicalName>account</a:LogicalName>
       <a:Name i:nil="true" />
      </c:value>
     </a:KeyValuePairOfstringanyType>
     <a:KeyValuePairOfstringanyType>
      <c:key>Assignee</c:key>
      <c:value i:type="a:EntityReference">
       <a:Id>XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</a:Id>
       <a:LogicalName>systemuser</a:LogicalName>
       <a:Name i:nil="true" />
      </c:value>
     </a:KeyValuePairOfstringanyType>
    </a:Parameters>
    <a:RequestId i:nil="true" />
    <a:RequestName>Assign</a:RequestName>
   </request>
  </Execute>
 </s:Body>
</s:Envelope>
ENV;

$headers = array(
  'Method: POST',
  'Connection: Keep-Alive',
  'User-Agent: PHP-SOAP-CURL',
  'Content-Type: text/xml; charset=utf-8',
  "SOAPAction: http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute",  
);


$url = 'http://crm.example.com/XRMServices/2011/Organization.svc/web';
$username  = 'myusername';
$password  = 'MyP@55w0rd';

$ch = curl_init();

curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $SOAPEnv);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);

$output = curl_exec($ch);

And this give the SOAP response:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">a:FailedAuthentication</faultcode>
      <faultstring xml:lang="en-GB">Access is denied.</faultstring>
    </s:Fault>
  </s:Body>
</s:Envelope>

It is encouraging that this is a SOAP response, so I feel like I’m close. It is certainly authenticating with the credentials that I provide, because if I change them I don’t get any SOAP back. But I feel there must be another level of authentication to let the SOAP Endpoint know I’m a legitimate client.

So I’m a bit stuck as the documentation on using SOAP with Dynamics seems to be written from the point of Microsofts SDK’s, and certainly not helpful for someone of my experience who's only really knows PHP.

If anyone could give any feedback on my approach, I would greatly appreciate. If you could point me in the right direction to proceed next that would be even better. And I’m running out of time on the project so have put this out to the experts. Please note I’m a relatively new developer so apologise in advance if you have to dumb down your answers slightly :)

Many thanks, Ashwah

Community
  • 1
  • 1
  • How are you going with this? Have you made any progress?? I spend a fair bit of time working on connection Drupal to CRM Online. I have got it working however I never did crack the Active Directory puzzle. My investigation is on my blog. http://crmtroubleshoot.blogspot.com.au/search/label/soap Active Directory - SOAP - CRM seems to be a recurring question on stackoverflow and i'd love to know if you figured it out – Campey Nov 26 '14 at 22:00
  • 1
    Ended up not doing the authentication ourselves. Had to employ a MS developer to build some middleware to talk to the Dynamics API. It's a shame that its so difficult to make something like this work cross platform. Although probably for the best cos it meant we could take advantage of some of the more custom CRM functionality. – AshTheSmash Jul 27 '15 at 09:12

1 Answers1

1

I struggled with something related. I could get sample Python code to connect with a cloud hosted version of the CRM which had a tenant name, but I was trying to integrate with an on-premises/IFD install which had its own STS (security token server). Using that domain didn't work for me. I couldn't figure out what the right redirect_uri was.

Once I realized I had to connect to a CRM api endpoint to get the Bearer authorization_uri, it worked form me.

If you have a Dynamics CRM instand at foo.crm.dynamics.com you can curl this

curl -I https://foo.crm.dynamics.com/api/data/v8.0/WhoAmI

Replace foo with the name of your CRM instance, and get back something like this

HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Length: 49
Content-Type: text/html
Server: Microsoft-IIS/8.X
REQ_ID: 995XXXX-XXXX-XXXX-XXX-XXXXXXXXe7
Set-Cookie: ReqClientId=95XXXX-XXXX-XXXX-XXX-XXXXXXXXe7; expires=Wed, XX-Jan-XX 15:MM:SS GMT; path=/; secure; HttpOnly
WWW-Authenticate: Bearer authorization_uri=https://login.windows.net/62d6e7de-bbbb-cccc-dddd-4f9605b2ad79/oauth2/authorize, resource_id=https://foo.crm.dynamics.com/
X-Powered-By: ASP.NET
...

The UUID 62d6e7de-bbbb-cccc-dddd-4f9605b2ad79 is what you are looking for. For some inadquately explained reason I needed to switch the domain from login.windows.net to login.microsoftonline.com.

https://login.microsoftonline.com/62d6e7de-bbbb-cccc-dddd-4f9605b2ad79.

Then I was able to use this sample code with username and password and actually connect.

https://github.com/jlattimer/CrmWebApiPython/blob/master/CrmConnect

amjoconn
  • 2,113
  • 2
  • 17
  • 17