Skip to content

Commit

Permalink
Refresh token public and web app support (salesforce-marketingcloud#150)
Browse files Browse the repository at this point in the history
* initial checkin

* Adding unit tests

* updating version and readme

* address code review concerns

* address code review concerns

* adding the context variable back
  • Loading branch information
manivinesh authored Aug 21, 2019
1 parent ad7416e commit 27895de
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 27 deletions.
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,52 @@ 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.3.0 ##
## New Features in Version 1.4.0 ##

In Addition to the OAuth2 feature added as part of Version 1.3.0, We have now added the support to authenticate Public/Web Apps using OAuth2.

* 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,
'applicationType' => 'public|web'
'redirectURI' => 'REDIRECT_URL_FOR_YOUR_APP'
'authorizationCode' => 'AUTHORIZATION_CODE_RECEIVED_FROM_AUTHORIZE_UI_CALL'
'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,
'applicationType' => 'public|web'
'redirectURI' => 'REDIRECT_URL_FOR_YOUR_APP'
'authorizationCode' => 'AUTHORIZATION_CODE_RECEIVED_FROM_AUTHORIZE_UI_CALL'
'accountId' => <TARGET_ACCOUNT_ID>,
'scope' => '<PERMISSION_LIST>'
)
);
```

## 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.
Expand Down
4 changes: 2 additions & 2 deletions config.php.template
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
return array(
'appsignature' => 'none',
'appsignature' => 'none',
'clientid' => 'CCCCCCCCCCCCCCCCCCCCCCC',
'clientsecret' => 'CCCCCCCCCCCCCCCCCCCCCCC',
'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl',
Expand All @@ -15,4 +15,4 @@ return array(
'proxyport' => '8080',
'proxyusername' => '',
'proxypassword' => ''
);
);
112 changes: 90 additions & 22 deletions src/ET_Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use \DOMXPath;
use \Exception;


/**
* Auto load method to load dependent classes
*/
Expand Down Expand Up @@ -67,7 +68,8 @@ class ET_Client extends SoapClient

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

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

Expand Down Expand Up @@ -130,6 +132,20 @@ function __construct($getWSDL = false, $debug = false, $params = null)
}
}
if (array_key_exists('accountId', $config)){$this->accountId = $config['accountId'];}

if (array_key_exists('applicationType', $config))
{
$this->applicationType = $config['applicationType'];
}
if (array_key_exists('redirectURI', $config))
{
$this->redirectURI = $config['redirectURI'];
}
if (array_key_exists('authorizationCode', $config))
{
$this->authorizationCode = $config['authorizationCode'];
}

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

if (array_key_exists('xmlloc', $config)){$this->xmlLoc = $config['xmlloc'];}
Expand Down Expand Up @@ -190,14 +206,43 @@ function __construct($getWSDL = false, $debug = false, $params = null)
{
$this->scope = $params['scope'];
}
if (array_key_exists('applicationType', $params))
{
$this->applicationType = $params['applicationType'];
}
if (array_key_exists('redirectURI', $params))
{
$this->redirectURI = $params['redirectURI'];
}
if (array_key_exists('authorizationCode', $params))
{
$this->authorizationCode = $params['authorizationCode'];
}
}

$this->debugSOAP = $debug;
if (!property_exists($this,'clientId') || is_null($this->clientId) || !property_exists($this,'clientSecret') || is_null($this->clientSecret)){
throw new Exception('clientid or clientsecret is null: Must be provided in config file or passed when instantiating ET_Client');

if (empty($this->applicationType)){
$this->applicationType = 'server';
}


if($this->applicationType == 'public' || $this->applicationType == 'web'){
if (empty($this->redirectURI) || empty($this->authorizationCode)){
throw new Exception('redirectURI or authorizationCode is null: Must be provided in config file or passed when instantiating ET_Client');
}
}

if($this->applicationType == 'public'){
if (empty($this->clientId)){
throw new Exception('clientid is null: Must be provided in config file or passed when instantiating ET_Client');
}
}
else {
if (empty($this->clientId) || empty($this->clientSecret)) {
throw new Exception('clientid or clientsecret is null: Must be provided in config file or passed when instantiating ET_Client');
}
}

if ($getWSDL){$this->CreateWSDL($this->wsdlLoc);}

if ($params && array_key_exists('jwt', $params)){
Expand Down Expand Up @@ -238,11 +283,11 @@ function __construct($getWSDL = false, $debug = false, $params = null)
}
}

$context = stream_context_create([
'ssl' => [
'verify_peer' => ET_Util::shouldVerifySslPeer($this->sslVerifyPeer)
]
]);
$context = stream_context_create([
'ssl' => [
'verify_peer' => ET_Util::shouldVerifySslPeer($this->sslVerifyPeer)
]
]);

$soapOptions = array(
'stream_context' => $context
Expand Down Expand Up @@ -321,6 +366,36 @@ function refreshToken($forceRefresh = false)
}
}

function createPayloadForOauth2(){
$payload = new stdClass();

$payload->client_id = $this->clientId;
if($this->applicationType != 'public') {
$payload->client_secret = $this->clientSecret;
}

$refreshKey = $this->getRefreshToken(null);
if(!empty($refreshKey)){
$payload->grant_type = 'refresh_token';
$payload->refresh_token = $refreshKey;
} else if($this->applicationType == 'public' || $this->applicationType == 'web'){
$payload->grant_type = 'authorization_code';
$payload->code = $this->authorizationCode;
$payload->redirect_uri = $this->redirectURI;
} else {
$payload->grant_type = 'client_credentials';
}

if (!empty(trim($this->accountId))){
$payload->account_id = $this->accountId;
}
if (!empty(trim($this->scope))){
$payload->scope = $this->scope;
}

return $payload;
}

function refreshTokenWithOAuth2($forceRefresh = false)
{
if (property_exists($this, "sdl") && $this->sdl == 0){
Expand All @@ -338,17 +413,7 @@ function refreshTokenWithOAuth2($forceRefresh = false)

$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;
}
$jsonRequest = $this->createPayloadForOauth2();

$authResponse = ET_Util::restPost($url, json_encode($jsonRequest), $this);
$authObject = json_decode($authResponse->body);
Expand All @@ -360,6 +425,9 @@ function refreshTokenWithOAuth2($forceRefresh = false)
$this->setInternalAuthToken($this->tenantKey, $authObject->access_token);
$this->baseUrl = $authObject->rest_instance_url;
$this->baseSoapUrl= $authObject->soap_instance_url."Service.asmx";
if (property_exists($authObject,'refresh_token')){
$this->setRefreshToken($this->tenantKey, $authObject->refresh_token);
}
} else {
throw new Exception('Unable to validate App Keys(ClientID/ClientSecret) provided, requestToken response:'.$authResponse->body );
}
Expand Down Expand Up @@ -629,7 +697,7 @@ function setRefreshToken($tenantKey, $refreshToken)
* @param string $tenantKey
* @return string Refresh token for the tenant
*/
function getRefreshToken($tenantKey)
public function getRefreshToken($tenantKey)
{
$tenantKey = $tenantKey == null ? $this->tenantKey : $tenantKey;
if ($this->tenantTokens[$tenantKey] == null) {
Expand Down
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.3.0";
return "FuelSDK-PHP-v1.4.0";
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/autoload.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?php
// This file can be used if you do not use composer to get all the dependencies.
//Then you need to download all the dependencies manually and change the first require line accordingly.

require __DIR__ . '/../vendor/autoload.php';
spl_autoload_register( function($class_name) {
if (file_exists('src/'.$class_name.'.php'))
Expand Down
31 changes: 31 additions & 0 deletions tests/OAuth2Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
namespace FuelSdk\Test;
use FuelSdk\ET_Client;
use FuelSdk\ET_Asset;
use PHPUnit\Framework\TestCase;

final class OAuth2Test extends TestCase
{
private $client;

function __construct()
{
$this->client = new ET_Client(true);
}

public function testIfAuthTokenAndRefreshTokenDifferIfRefreshTokenIsEnforced()
{
$reflection = new \ReflectionClass(get_class($this->client));
$clientid = $reflection->getProperty("clientId");
$clientid->setAccessible(true);
$token = $this->client->getAuthToken();
$refreshToken = $this->client->getRefreshToken(null);
$this->client->refreshTokenWithOAuth2(true);

$newtoken = $this->client->getAuthToken();
$newrefreshToken = $this->client->getRefreshToken(null);

$this->assertTrue($refreshToken != $newrefreshToken && $token != $newtoken);
}
}
?>

0 comments on commit 27895de

Please sign in to comment.