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