OAuthTokenCredential.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. <?php
  2. namespace PayPal\Auth;
  3. use PayPal\Cache\AuthorizationCache;
  4. use PayPal\Common\PayPalResourceModel;
  5. use PayPal\Core\PayPalHttpConfig;
  6. use PayPal\Core\PayPalHttpConnection;
  7. use PayPal\Core\PayPalLoggingManager;
  8. use PayPal\Exception\PayPalConfigurationException;
  9. use PayPal\Exception\PayPalConnectionException;
  10. use PayPal\Handler\IPayPalHandler;
  11. use PayPal\Rest\ApiContext;
  12. use PayPal\Security\Cipher;
  13. /**
  14. * Class OAuthTokenCredential
  15. */
  16. class OAuthTokenCredential extends PayPalResourceModel
  17. {
  18. public static $CACHE_PATH = '/../../../var/auth.cache';
  19. /**
  20. * @var string Default Auth Handler
  21. */
  22. public static $AUTH_HANDLER = 'PayPal\Handler\OauthHandler';
  23. /**
  24. * Private Variable
  25. *
  26. * @var int $expiryBufferTime
  27. */
  28. public static $expiryBufferTime = 120;
  29. /**
  30. * Client ID as obtained from the developer portal
  31. *
  32. * @var string $clientId
  33. */
  34. private $clientId;
  35. /**
  36. * Client secret as obtained from the developer portal
  37. *
  38. * @var string $clientSecret
  39. */
  40. private $clientSecret;
  41. /**
  42. * Generated Access Token
  43. *
  44. * @var string $accessToken
  45. */
  46. private $accessToken;
  47. /**
  48. * Seconds for with access token is valid
  49. *
  50. * @var $tokenExpiresIn
  51. */
  52. private $tokenExpiresIn;
  53. /**
  54. * Last time (in milliseconds) when access token was generated
  55. *
  56. * @var $tokenCreateTime
  57. */
  58. private $tokenCreateTime;
  59. /**
  60. * Instance of cipher used to encrypt/decrypt data while storing in cache.
  61. *
  62. * @var Cipher
  63. */
  64. private $cipher;
  65. /**
  66. * Construct
  67. *
  68. * @param string $clientId client id obtained from the developer portal
  69. * @param string $clientSecret client secret obtained from the developer portal
  70. */
  71. public function __construct($clientId, $clientSecret)
  72. {
  73. $this->clientId = $clientId;
  74. $this->clientSecret = $clientSecret;
  75. $this->cipher = new Cipher($this->clientSecret);
  76. }
  77. /**
  78. * Get Client ID
  79. *
  80. * @return string
  81. */
  82. public function getClientId()
  83. {
  84. return $this->clientId;
  85. }
  86. /**
  87. * Get Client Secret
  88. *
  89. * @return string
  90. */
  91. public function getClientSecret()
  92. {
  93. return $this->clientSecret;
  94. }
  95. /**
  96. * Get AccessToken
  97. *
  98. * @param $config
  99. *
  100. * @return null|string
  101. */
  102. public function getAccessToken($config)
  103. {
  104. // Check if we already have accessToken in Cache
  105. if ($this->accessToken && (time() - $this->tokenCreateTime) < ($this->tokenExpiresIn - self::$expiryBufferTime)) {
  106. return $this->accessToken;
  107. }
  108. // Check for persisted data first
  109. $token = AuthorizationCache::pull($config, $this->clientId);
  110. if ($token) {
  111. // We found it
  112. // This code block is for backward compatibility only.
  113. if (array_key_exists('accessToken', $token)) {
  114. $this->accessToken = $token['accessToken'];
  115. }
  116. $this->tokenCreateTime = $token['tokenCreateTime'];
  117. $this->tokenExpiresIn = $token['tokenExpiresIn'];
  118. // Case where we have an old unencrypted cache file
  119. if (!array_key_exists('accessTokenEncrypted', $token)) {
  120. AuthorizationCache::push($config, $this->clientId, $this->encrypt($this->accessToken), $this->tokenCreateTime, $this->tokenExpiresIn);
  121. } else {
  122. $this->accessToken = $this->decrypt($token['accessTokenEncrypted']);
  123. }
  124. }
  125. // Check if Access Token is not null and has not expired.
  126. // The API returns expiry time as a relative time unit
  127. // We use a buffer time when checking for token expiry to account
  128. // for API call delays and any delay between the time the token is
  129. // retrieved and subsequently used
  130. if (
  131. $this->accessToken != null &&
  132. (time() - $this->tokenCreateTime) > ($this->tokenExpiresIn - self::$expiryBufferTime)
  133. ) {
  134. $this->accessToken = null;
  135. }
  136. // If accessToken is Null, obtain a new token
  137. if ($this->accessToken == null) {
  138. // Get a new one by making calls to API
  139. $this->updateAccessToken($config);
  140. AuthorizationCache::push($config, $this->clientId, $this->encrypt($this->accessToken), $this->tokenCreateTime, $this->tokenExpiresIn);
  141. }
  142. return $this->accessToken;
  143. }
  144. /**
  145. * Get a Refresh Token from Authorization Code
  146. *
  147. * @param $config
  148. * @param $authorizationCode
  149. * @param array $params optional arrays to override defaults
  150. * @return string|null
  151. */
  152. public function getRefreshToken($config, $authorizationCode = null, $params = array())
  153. {
  154. static $allowedParams = array(
  155. 'grant_type' => 'authorization_code',
  156. 'code' => 1,
  157. 'redirect_uri' => 'urn:ietf:wg:oauth:2.0:oob',
  158. 'response_type' => 'token'
  159. );
  160. $params = is_array($params) ? $params : array();
  161. if ($authorizationCode) {
  162. //Override the authorizationCode if value is explicitly set
  163. $params['code'] = $authorizationCode;
  164. }
  165. $payload = http_build_query(array_merge($allowedParams, array_intersect_key($params, $allowedParams)));
  166. $response = $this->getToken($config, $this->clientId, $this->clientSecret, $payload);
  167. if ($response != null && isset($response["refresh_token"])) {
  168. return $response['refresh_token'];
  169. }
  170. return null;
  171. }
  172. /**
  173. * Updates Access Token based on given input
  174. *
  175. * @param array $config
  176. * @param string|null $refreshToken
  177. * @return string
  178. */
  179. public function updateAccessToken($config, $refreshToken = null)
  180. {
  181. $this->generateAccessToken($config, $refreshToken);
  182. return $this->accessToken;
  183. }
  184. /**
  185. * Retrieves the token based on the input configuration
  186. *
  187. * @param array $config
  188. * @param string $clientId
  189. * @param string $clientSecret
  190. * @param string $payload
  191. * @return mixed
  192. * @throws PayPalConfigurationException
  193. * @throws \PayPal\Exception\PayPalConnectionException
  194. */
  195. protected function getToken($config, $clientId, $clientSecret, $payload)
  196. {
  197. $httpConfig = new PayPalHttpConfig(null, 'POST', $config);
  198. // if proxy set via config, add it
  199. if (!empty($config['http.Proxy'])) {
  200. $httpConfig->setHttpProxy($config['http.Proxy']);
  201. }
  202. $handlers = array(self::$AUTH_HANDLER);
  203. /** @var IPayPalHandler $handler */
  204. foreach ($handlers as $handler) {
  205. if (!is_object($handler)) {
  206. $fullHandler = "\\" . (string)$handler;
  207. $handler = new $fullHandler(new ApiContext($this));
  208. }
  209. $handler->handle($httpConfig, $payload, array('clientId' => $clientId, 'clientSecret' => $clientSecret));
  210. }
  211. $connection = new PayPalHttpConnection($httpConfig, $config);
  212. $res = $connection->execute($payload);
  213. $response = json_decode($res, true);
  214. return $response;
  215. }
  216. /**
  217. * Generates a new access token
  218. *
  219. * @param array $config
  220. * @param null|string $refreshToken
  221. * @return null
  222. * @throws PayPalConnectionException
  223. */
  224. private function generateAccessToken($config, $refreshToken = null)
  225. {
  226. $params = array('grant_type' => 'client_credentials');
  227. if ($refreshToken != null) {
  228. // If the refresh token is provided, it would get access token using refresh token
  229. // Used for Future Payments
  230. $params['grant_type'] = 'refresh_token';
  231. $params['refresh_token'] = $refreshToken;
  232. }
  233. $payload = http_build_query($params);
  234. $response = $this->getToken($config, $this->clientId, $this->clientSecret, $payload);
  235. if ($response == null || !isset($response["access_token"]) || !isset($response["expires_in"])) {
  236. $this->accessToken = null;
  237. $this->tokenExpiresIn = null;
  238. PayPalLoggingManager::getInstance(__CLASS__)->warning("Could not generate new Access token. Invalid response from server: ");
  239. throw new PayPalConnectionException(null, "Could not generate new Access token. Invalid response from server: ");
  240. } else {
  241. $this->accessToken = $response["access_token"];
  242. $this->tokenExpiresIn = $response["expires_in"];
  243. }
  244. $this->tokenCreateTime = time();
  245. return $this->accessToken;
  246. }
  247. /**
  248. * Helper method to encrypt data using clientSecret as key
  249. *
  250. * @param $data
  251. * @return string
  252. */
  253. public function encrypt($data)
  254. {
  255. return $this->cipher->encrypt($data);
  256. }
  257. /**
  258. * Helper method to decrypt data using clientSecret as key
  259. *
  260. * @param $data
  261. * @return string
  262. */
  263. public function decrypt($data)
  264. {
  265. return $this->cipher->decrypt($data);
  266. }
  267. }