Zenithies » PHP »

Connect to Microsoft Dynamics Crm 4.0 web service from PHP using IFD authentication


If you are looking for integration of data exchange betwen your PHP web site and MS CRM please visit:
Microsoft Dynamics Crm 4.0 PHP Integration Offer.

Also integration with MS CRM 2011 Online is possible: Microsoft Dynamics CRM 2011 Online PHP Integration Offer

Some time ago i got really hard stuck on IFD authentication to Microsoft Dynamics Crm webservice. It took few days, but i found solution. And i will gladly share it with all who's i need as i was.

theory

IFD authentication for CRM works with this scheme

  • Your client app. has to call CrmDiscoveryService -> RetrieveCrmTicketRequest with crm login
  • From response you will get CrmTicket
  • Every request sent to CrmService must have in soap:header CrmAuthenticationToken which contains auth type and in case of IFD auth CrmTicket
  • With this information you could be able to create functional service, but...

... but it just didnt work that easily... Something was still missing and with soapClient or nusoap i all time got 402 error from service.

how to

Well, here is little snippet u can use :)

<?php  

/*php Microsoft Dynamics Crm 4.0 IFD authentication library

Copyright (c) 2009 Zenithies

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/


class MSCrmIFD {
public $usr;
public $pwd;
public $org;
public $domain;

public $crmHost;
private $crmTicket;
private $cURLHandle;

// performs login
public function getAccess() {
$matches = array();

// prepare request
$request = '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Execute xmlns="http://schemas.microsoft.com/crm/2007/CrmDiscoveryService">
<Request xsi:type="RetrieveCrmTicketRequest">
<OrganizationName>' . $this->org . '</OrganizationName>
<UserId>' . $this->domain . '\\' . $this->usr .'</UserId>
<Password>' . $this->pwd . '</Password>
</Request>
</Execute>
</soap:Body>
</soap:Envelope>';

// setup headers
$headers = array(
"POST ". '/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx' ." HTTP/1.1",
"Host: " . $this->crmHost,
'Connection: Keep-Alive',
"SOAPAction: \"http://schemas.microsoft.com/crm/2007/CrmDiscoveryService/Execute\"",
"Content-type: text/xml;charset=\"utf-8\"",
"Content-length: ".strlen($request),
);

// Initialize cURL, this instance will be used for passing all requests to CrmService
$this->cURLHandle = curl_init();

curl_setopt($this->cURLHandle, CURLOPT_URL, 'http://' . $this->crmHost . '/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx');
curl_setopt($this->cURLHandle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($this->cURLHandle, CURLOPT_TIMEOUT, 60);
curl_setopt($this->cURLHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($this->cURLHandle, CURLOPT_HTTPHEADER, $headers);

//assign soap XML
curl_setopt($this->cURLHandle, CURLOPT_POST, 1);
curl_setopt($this->cURLHandle, CURLOPT_POSTFIELDS, $request);

$response = curl_exec($this->cURLHandle);

if (preg_match('/<CrmTicket>(.*)<\/CrmTicket>/', $response, $matches)) {
$this->crmTicket = $matches[1];
return true;
} else {
throw new Exception('MSCrmIFD::getAccess() IFD auth failed');
}
}

public function request($request, $action) {
$headers = array(
"POST /MSCrmServices/2007/MSCrmServices/2007/CrmService.asmx HTTP/1.1",
"Host: " . $this->crmHost,
'Connection: keep-alive',
"SOAPAction: " . $action,
"Content-type: text/xml;charset=utf-8",
"Content-length: ".strlen($request),
);

var_dump('he ');

curl_setopt($this->cURLHandle, CURLOPT_URL, "http://" . $this->crmHost . "/MSCrmServices/2007/CrmService.asmx");
curl_setopt($this->cURLHandle, CURLOPT_HTTPHEADER, $headers);
curl_setopt($this->cURLHandle, CURLOPT_POST, 1);
curl_setopt($this->cURLHandle, CURLOPT_POSTFIELDS, $request);

// ticket is set into cookie, with that you dont need him in soap header anymore
// in fact this row is most important in whole struggle with this-
curl_setopt($this->cURLHandle, CURLOPT_COOKIE, 'MSCRMSession=ticket=' . $this->crmTicket . ';');

$response = curl_exec($this->cURLHandle);
$responseHeaders = curl_getinfo($this->cURLHandle);

if ($responseHeaders['http_code'] != 200) {
print_r($response);
die('MSCrmIFD::__doRequest() failed');
}

return $response;
}

public function getAuthHeader() {
$header = '<soap:Header>
<CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices">
<AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">2</AuthenticationType>
<OrganizationName xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">' . $this->org . '</OrganizationName>
</CrmAuthenticationToken>
</soap:Header>';

return $header;
}

public function closeConnection() {
curl_close($this->cURLHandle);
}
}

/// example of use

$service = new MSCrmIFD();

$service->usr = 'your_user';
$service->pwd = 'your_password';
$service->domain = 'domain';
$service->org = 'organization';
$service->crmHost = 'crm.example.ex:5555';

// login into service
$service->getAccess();

// prepare some request, put into request auth header
$request = '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
' . $service->getAuthHeader() . '
<soap:Body>
<Fetch xmlns="http://schemas.microsoft.com/crm/2007/WebServices">
<fetchXml>&lt;fetch mapping=\'logical\'&gt;&lt;entity name=\'account\'&gt;&lt;all-attributes/&gt;</fetchXml>
</Fetch>
</soap:Body>
</soap:Envelope>';

// get response
$response = $service->request($request, 'http://schemas.microsoft.com/crm/2007/WebServices/Fetch');

// !!! note '''if you get ServerServer was unable to process request error
// try to write down whole xml request on just one row
?>

As you can see, whole trick is in fact just in putting 'MSCRMSession=ticket=' cookie info to request, with correct ticket obtained from Discovery Service Hope this will be helpfull... If you have any questions, you can contact me at zenithies [at] gmail [dot] com

last update: 2010-01-03 | author: Zenithy