Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
Added OAuth2 authentication support
Browse files Browse the repository at this point in the history
Added OAuth2 authentication support.
  • Loading branch information
sfdrogojan authored May 20, 2019
1 parent 8018468 commit a4443c1
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 85 deletions.
96 changes: 41 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,49 @@ Salesforce Marketing Cloud Fuel SDK for PHP
## Overview ##
The Fuel SDK for PHP provides easy access to Salesforce Marketic Cloud's Fuel API Family services, including a collection of REST and SOAP API. These APIs provide access to Salesforce Marketing Cloud (previously called ExactTarget) functionality via common collection types such as array/hash.

## New Features in Version 1.2.2 ##

* Added support for in memory cache.

## New Features in Version 1.2.1 ##

* Updated robrichards/wse-php dependency version to 2.0.3

## New Features in Version 1.2.0 ##
## New Features in Version 1.3.0 ##

* Added support for OAuth2 authentication - [More Details](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/integration-considerations.htm)
* To enable OAuth2 authentication, set `'useOAuth2Authentication' => true` in the config.php file or pass it in the `params` argument to the ET_Client constructor.
* Sample Config for OAuth2:
```
'appsignature' => 'none',
'clientid' => '<CLIENT_ID>',
'clientsecret' => '<CLIENT_SECRET>',
'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl',
'xmlloc' => '/some/path/to/cache/ExactTargetWSDL.xml',
'baseAuthUrl' => '<AUTH TENANT SPECIFIC ENDPOINT>',
'baseSoapUrl' => '<SOAP TENANT SPECIFIC ENDPOINT>',
'baseUrl' => '<REST TENANT SPECIFIC ENDPOINT>',
'useOAuth2Authentication' => true,
'accountId' => <TARGET_ACCOUNT_ID>,
'scope' => '<PERMISSION_LIST>'
```
* Example passing config as a parameter to ET_Client constructor:
```
$myclient = new ET_Client(
true,
true,
array(
'appsignature' => 'none',
'clientid' => '<CLIENT_ID>',
'clientsecret' => '<CLIENT_SECRET>',
'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl',
'xmlloc' => '/some/path/to/cache/ExactTargetWSDL.xml',
'baseAuthUrl' => '<AUTH TENANT SPECIFIC ENDPOINT>',
'baseSoapUrl' => '<SOAP TENANT SPECIFIC ENDPOINT>',
'baseUrl' => '<REST TENANT SPECIFIC ENDPOINT>',
'useOAuth2Authentication' => true,
'accountId' => '<TARGET_ACCOUNT_ID>',
'scope' => '<PERMISSION_LIST>'
)
);
```

## Version 1.2.0 ##

* Added support for your tenant’s endpoints - [More Details](https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/your-subdomain-tenant-specific-endpoints.htm)

## New Features in Version 1.1.0 ##

* **namespace :** namespace is introduced.

* **newly supported objects:**
- Result Message
- Data Extract
- Triggered Send Summary

* composer autoload issue fix


<!--
* **mcrypt :** mcrypt dependency removed.
mcrypt extension dependency prevented client application from upgrading to PHP 7.x. This release supports any PHP version > 5.6.24 and PHP 7.x
* **proxy :** added proxy server support.
If your client application sits behind a proxy server, you can use PHP SDK with following configuration settings in config.php file.
- proxyhost
- proxyport
- proxyusername
- proxypassword
You can override these configuration setting using $params parameter passed to the constructor of ET_Client class.
* **jwt :** jwt.php is removed from the project source tree and added as dependency.
jwt.php removed and added as dependency in composer.json. If you are manually downloading the project, call **composer update** to get **jwt** downloaded.
* **soap-wsse :** soap-wsse.php is removed from the project source tree and added as dependency in composer.json. If you are manually downloading the project, call **composer update** to get the **soap-wsse** downloaded.
* **code refactor :** code refactored to individual class files. (under src/ directory)
Project tree structure is now changed to:
- src : source files
- doc : SDK API documentation
- tests : unit test cases
- objsamples : sample files
* **unit test :** added unit test cases (happy path for now) using phpunit testing framework. (under tests/ directory)
* **API docs :** added API documentation using phpdocumentor framework. (under docs/ directory)
* **auto loader :** integrated auto loader (spl_autoload_register) for all source code under src/, tests/, objsamples/ directory.
-->


## Requirements ##
PHP Version >=5.6.24

Expand All @@ -89,7 +75,7 @@ The following code is an example of a minimal composer.json file:
<pre>
{
"require": {
"salesforce-mc/fuel-sdk-php": "1.2.2"
"salesforce-mc/fuel-sdk-php": "1.3.0"
}
}
</pre>
Expand Down
3 changes: 3 additions & 0 deletions config.php.template
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ return array(
'baseUrl' => 'https://www.exacttargetapis.com',
'baseAuthUrl' => 'https://auth.exacttargetapis.com',
'baseSoapUrl' => 'https://webservice.exacttarget.com/Service.asmx',
'useOAuth2Authentication' => false,
'accountId' => <account id>,
'scope' => '<permissions list>',
'proxyhost' => 'localhost',
'proxyport' => '8080',
'proxyusername' => '',
Expand Down
154 changes: 125 additions & 29 deletions src/ET_Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ET_Client extends SoapClient

private $wsdlLoc, $debugSOAP, $lastHTTPCode, $clientId,
$clientSecret, $appsignature, $endpoint,
$tenantTokens, $tenantKey, $xmlLoc, $baseAuthUrl, $baseSoapUrl;
$tenantTokens, $tenantKey, $xmlLoc, $baseAuthUrl, $baseSoapUrl, $useOAuth2Authentication, $accountId, $scope;

private $defaultBaseSoapUrl = 'https://webservice.exacttarget.com/Service.asmx';

Expand Down Expand Up @@ -110,7 +110,6 @@ function __construct($getWSDL = false, $debug = false, $params = null)
$this->clientId = $config['clientid'];
$this->clientSecret = $config['clientsecret'];
$this->appsignature = $config['appsignature'];
$this->baseUrl = $config["baseUrl"];
$this->baseAuthUrl = $config["baseAuthUrl"];
if (array_key_exists('baseSoapUrl', $config)) {
if (!empty($config["baseSoapUrl"])) {
Expand All @@ -119,6 +118,20 @@ function __construct($getWSDL = false, $debug = false, $params = null)
$this->baseSoapUrl = $this->defaultBaseSoapUrl;
}
}
if(array_key_exists('useOAuth2Authentication', $config)){$this->useOAuth2Authentication = $config['useOAuth2Authentication'];}
if(array_key_exists("baseUrl", $config))
{
$this->baseUrl = $config["baseUrl"];
}
else
{
if($this->isNullOrEmptyString($this->useOAuth2Authentication) || $this->useOAuth2Authentication === false) {
throw new Exception("baseUrl is null: Must be provided in config file when instantiating ET_Client");
}
}
if (array_key_exists('accountId', $config)){$this->accountId = $config['accountId'];}
if (array_key_exists('scope', $config)){$this->scope = $config['scope'];}

if (array_key_exists('xmlloc', $config)){$this->xmlLoc = $config['xmlloc'];}

if(array_key_exists('proxyhost', $config)){$this->proxyHost = $config['proxyhost'];}
Expand All @@ -129,42 +142,54 @@ function __construct($getWSDL = false, $debug = false, $params = null)
}
if ($params)
{
if ($params && array_key_exists('defaultwsdl', $params)){$this->wsdlLoc = $params['defaultwsdl'];}
if (array_key_exists('defaultwsdl', $params)){$this->wsdlLoc = $params['defaultwsdl'];}
else {$this->wsdlLoc = "https://webservice.exacttarget.com/etframework.wsdl";}
if ($params && array_key_exists('clientid', $params)){$this->clientId = $params['clientid'];}
if ($params && array_key_exists('clientsecret', $params)){$this->clientSecret = $params['clientsecret'];}
if ($params && array_key_exists('appsignature', $params)){$this->appsignature = $params['appsignature'];}
if ($params && array_key_exists('xmlloc', $params)){$this->xmlLoc = $params['xmlloc'];}

if ($params && array_key_exists('proxyhost', $params)){$this->proxyHost = $params['proxyhost'];}
if ($params && array_key_exists('proxyport', $params)){$this->proxyPort = $params['proxyport'];}
if ($params && array_key_exists('proxyusername', $params)) {$this->proxyUserName = $params['proxyusername'];}
if ($params && array_key_exists('proxypassword', $params)) {$this->proxyPassword = $params['proxypassword'];}
if ($params && array_key_exists('sslverifypeer', $params)) {$this->sslVerifyPeer = $params['sslverifypeer'];}
if ($params && array_key_exists('baseUrl', $params))
if (array_key_exists('clientid', $params)){$this->clientId = $params['clientid'];}
if (array_key_exists('clientsecret', $params)){$this->clientSecret = $params['clientsecret'];}
if (array_key_exists('appsignature', $params)){$this->appsignature = $params['appsignature'];}
if (array_key_exists('xmlloc', $params)){$this->xmlLoc = $params['xmlloc'];}

if (array_key_exists('proxyhost', $params)){$this->proxyHost = $params['proxyhost'];}
if (array_key_exists('proxyport', $params)){$this->proxyPort = $params['proxyport'];}
if (array_key_exists('proxyusername', $params)) {$this->proxyUserName = $params['proxyusername'];}
if (array_key_exists('proxypassword', $params)) {$this->proxyPassword = $params['proxypassword'];}
if (array_key_exists('sslverifypeer', $params)) {$this->sslVerifyPeer = $params['sslverifypeer'];}
if (array_key_exists('baseUrl', $params))
{
$this->baseUrl = $params['baseUrl'];
}
else
{
$this->baseUrl = "https://www.exacttargetapis.com";
}
if ($params && array_key_exists('baseAuthUrl', $params))
if (array_key_exists('baseAuthUrl', $params))
{
$this->baseAuthUrl = $params['baseAuthUrl'];
}
else
{
$this->baseAuthUrl = "https://auth.exacttargetapis.com";
}
if ($params && array_key_exists('baseSoapUrl', $params))
if (array_key_exists('baseSoapUrl', $params))
{
if (!empty($params["baseSoapUrl"])) {
$this->baseSoapUrl = $params['baseSoapUrl'];
} else {
$this->baseSoapUrl = $this->defaultBaseSoapUrl;
}
}
if (array_key_exists('useOAuth2Authentication', $params))
{
$this->useOAuth2Authentication = $params['useOAuth2Authentication'];
}
if (array_key_exists('accountId', $params))
{
$this->accountId = $params['accountId'];
}
if (array_key_exists('scope', $params))
{
$this->scope = $params['scope'];
}
}

$this->debugSOAP = $debug;
Expand Down Expand Up @@ -246,7 +271,10 @@ function __construct($getWSDL = false, $debug = false, $params = null)
*/
function refreshToken($forceRefresh = false)
{

if ($this->useOAuth2Authentication === true){
$this->refreshTokenWithOAuth2($forceRefresh);
return;
}
if (property_exists($this, "sdl") && $this->sdl == 0){
parent::__construct($this->xmlLoc, array('trace'=>1, 'exceptions'=>0));
}
Expand Down Expand Up @@ -292,6 +320,54 @@ function refreshToken($forceRefresh = false)
throw new Exception('Unable to validate App Keys(ClientID/ClientSecret) provided.: '.$e->getMessage());
}
}

function refreshTokenWithOAuth2($forceRefresh = false)
{
if (property_exists($this, "sdl") && $this->sdl == 0){
parent::__construct($this->xmlLoc, array('trace'=>1, 'exceptions'=>0));
}
try {
$currentTime = new DateTime();
if (is_null($this->getAuthTokenExpiration($this->tenantKey))){
$timeDiff = 0;
} else {
$timeDiff = $currentTime->diff($this->getAuthTokenExpiration($this->tenantKey))->format('%i');
$timeDiff = $timeDiff + (60 * $currentTime->diff($this->getAuthTokenExpiration($this->tenantKey))->format('%H'));
}
if (is_null($this->getAuthToken($this->tenantKey)) || ($timeDiff < 5) || $forceRefresh ){

$url = $this->baseAuthUrl."/v2/token";

$jsonRequest = new stdClass();
$jsonRequest->client_id = $this->clientId;
$jsonRequest->client_secret = $this->clientSecret;
$jsonRequest->grant_type = "client_credentials";

if ($this->isNullOrEmptyString($this->accountId) == false){
$jsonRequest->account_id = $this->accountId;
}
if ($this->isNullOrEmptyString($this->scope) == false){
$jsonRequest->scope = $this->scope;
}

$authResponse = ET_Util::restPost($url, json_encode($jsonRequest), $this);
$authObject = json_decode($authResponse->body);

if ($authResponse && property_exists($authObject,"access_token")){
$dv = new DateInterval('PT'.$authObject->expires_in.'S');
$newexpTime = new DateTime();
$this->setAuthToken($this->tenantKey, $authObject->access_token, $newexpTime->add($dv));
$this->setInternalAuthToken($this->tenantKey, $authObject->access_token);
$this->baseUrl = $authObject->rest_instance_url;
$this->baseSoapUrl= $authObject->soap_instance_url."Service.asmx";
} else {
throw new Exception('Unable to validate App Keys(ClientID/ClientSecret) provided, requestToken response:'.$authResponse->body );
}
}
} catch (Exception $e) {
throw new Exception('Unable to validate App Keys(ClientID/ClientSecret) provided.: '.$e->getMessage());
}
}
/**
* Returns the HTTP code return by the last SOAP/Rest call
*
Expand Down Expand Up @@ -379,12 +455,19 @@ function __doRequest($request, $location, $saction, $version, $one_way = 0)
{
$doc = new DOMDocument();
$doc->loadXML($request);
$objWSSE = new WSSESoap($doc);
$objWSSE->addUserToken("*", "*", FALSE);
$this->addOAuth($doc, $this->getInternalAuthToken($this->tenantKey));

$content = $objWSSE->saveXML();
$content_length = strlen($content);

if($this->useOAuth2Authentication === true){
$this->addOAuth($doc, $this->getAuthToken($this->tenantKey));
$content = $doc->saveXML();
}
else{
$objWSSE = new WSSESoap($doc);
$objWSSE->addUserToken("*", "*", FALSE);
$this->addOAuth($doc, $this->getInternalAuthToken($this->tenantKey));

$content = $objWSSE->saveXML();
}

if ($this->debugSOAP){
error_log ('FuelSDK SOAP Request: ');
error_log (str_replace($this->getInternalAuthToken($this->tenantKey),"REMOVED",$content));
Expand Down Expand Up @@ -432,7 +515,6 @@ public function addOAuth( $doc, $token)
$soapPFX = $envelope->prefix;
$SOAPXPath = new DOMXPath($doc);
$SOAPXPath->registerNamespace('wssoap', $soapNS);
$SOAPXPath->registerNamespace('wswsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');

$headers = $SOAPXPath->query('//wssoap:Envelope/wssoap:Header');
$header = $headers->item(0);
Expand All @@ -441,11 +523,17 @@ public function addOAuth( $doc, $token)
$envelope->insertBefore($header, $envelope->firstChild);
}

$authnode = $soapDoc->createElementNS('http://exacttarget.com', 'oAuth');
if ($this->useOAuth2Authentication === true){
$authnode = $soapDoc->createElementNS('http://exacttarget.com','fueloauth',$token);
}
else{
$SOAPXPath->registerNamespace('wswsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');

$authnode = $soapDoc->createElementNS('http://exacttarget.com', 'oAuth');
$oauthtoken = $soapDoc->createElementNS(null,'oAuthToken',$token);
$authnode->appendChild($oauthtoken);
}
$header->appendChild($authnode);

$oauthtoken = $soapDoc->createElementNS(null,'oAuthToken',$token);
$authnode->appendChild($oauthtoken);
}

/**
Expand Down Expand Up @@ -790,7 +878,15 @@ function CreateContentAreas($arrayOfContentAreas)
$sendResponse = $postC->post();
return $sendResponse;
}


/**
* Function for basic field validation (present and neither empty nor only white space)
*
* @param string $str string to be validated
*/
function isNullOrEmptyString($str){
return (!isset($str) || trim($str) === '');
}
}

?>
2 changes: 1 addition & 1 deletion src/ET_Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ public static function isAssoc($array)
*/
public static function getSDKVersion()
{
return "FuelSDK-PHP-v1.2.2";
return "FuelSDK-PHP-v1.3.0";
}

/**
Expand Down

0 comments on commit a4443c1

Please sign in to comment.