From 27895de944c1e64ccc09a36a5bdb1838d0b5c8aa Mon Sep 17 00:00:00 2001 From: VINESH MANI <31937234+manivinesh@users.noreply.github.com> Date: Wed, 21 Aug 2019 09:35:31 -0400 Subject: [PATCH] Refresh token public and web app support (#150) * initial checkin * Adding unit tests * updating version and readme * address code review concerns * address code review concerns * adding the context variable back --- README.md | 47 +++++++++++++++++- config.php.template | 4 +- src/ET_Client.php | 112 ++++++++++++++++++++++++++++++++++--------- src/ET_Util.php | 2 +- src/autoload.php | 1 - tests/OAuth2Test.php | 31 ++++++++++++ 6 files changed, 170 insertions(+), 27 deletions(-) mode change 100755 => 100644 config.php.template create mode 100644 tests/OAuth2Test.php diff --git a/README.md b/README.md index 9fa6883..443c6a0 100755 --- a/README.md +++ b/README.md @@ -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' => '', + 'clientsecret' => '', + 'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl', + 'xmlloc' => '/some/path/to/cache/ExactTargetWSDL.xml', + 'baseAuthUrl' => '', + 'baseSoapUrl' => '', + 'baseUrl' => '', + 'useOAuth2Authentication' => true, + 'applicationType' => 'public|web' + 'redirectURI' => 'REDIRECT_URL_FOR_YOUR_APP' + 'authorizationCode' => 'AUTHORIZATION_CODE_RECEIVED_FROM_AUTHORIZE_UI_CALL' + 'accountId' => , + 'scope' => '' +``` +* Example passing config as a parameter to ET_Client constructor: +``` + $myclient = new ET_Client( + true, + true, + array( + 'appsignature' => 'none', + 'clientid' => '', + 'clientsecret' => '', + 'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl', + 'xmlloc' => '/some/path/to/cache/ExactTargetWSDL.xml', + 'baseAuthUrl' => '', + 'baseSoapUrl' => '', + 'baseUrl' => '', + 'useOAuth2Authentication' => true, + 'applicationType' => 'public|web' + 'redirectURI' => 'REDIRECT_URL_FOR_YOUR_APP' + 'authorizationCode' => 'AUTHORIZATION_CODE_RECEIVED_FROM_AUTHORIZE_UI_CALL' + 'accountId' => , + 'scope' => '' + ) + ); +``` + +## 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. diff --git a/config.php.template b/config.php.template old mode 100755 new mode 100644 index fd7ecdc..e85cdf2 --- a/config.php.template +++ b/config.php.template @@ -1,6 +1,6 @@ 'none', + 'appsignature' => 'none', 'clientid' => 'CCCCCCCCCCCCCCCCCCCCCCC', 'clientsecret' => 'CCCCCCCCCCCCCCCCCCCCCCC', 'defaultwsdl' => 'https://webservice.exacttarget.com/etframework.wsdl', @@ -15,4 +15,4 @@ return array( 'proxyport' => '8080', 'proxyusername' => '', 'proxypassword' => '' -); +); \ No newline at end of file diff --git a/src/ET_Client.php b/src/ET_Client.php index e902e1a..b06bbb0 100755 --- a/src/ET_Client.php +++ b/src/ET_Client.php @@ -13,6 +13,7 @@ use \DOMXPath; use \Exception; + /** * Auto load method to load dependent classes */ @@ -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'; @@ -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'];} @@ -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)){ @@ -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 @@ -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){ @@ -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); @@ -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 ); } @@ -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) { diff --git a/src/ET_Util.php b/src/ET_Util.php index 8514e43..be02bb0 100644 --- a/src/ET_Util.php +++ b/src/ET_Util.php @@ -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"; } /** diff --git a/src/autoload.php b/src/autoload.php index 1d4119b..c8d13cb 100644 --- a/src/autoload.php +++ b/src/autoload.php @@ -1,7 +1,6 @@ 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); + } +} +?> \ No newline at end of file