AbstractService.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <?php
  2. namespace OAuth\OAuth1\Service;
  3. use OAuth\Common\Consumer\CredentialsInterface;
  4. use OAuth\Common\Storage\TokenStorageInterface;
  5. use OAuth\Common\Http\Exception\TokenResponseException;
  6. use OAuth\Common\Http\Client\ClientInterface;
  7. use OAuth\Common\Http\Uri\UriInterface;
  8. use OAuth\OAuth1\Signature\SignatureInterface;
  9. use OAuth\OAuth1\Token\TokenInterface;
  10. use OAuth\OAuth1\Token\StdOAuth1Token;
  11. use OAuth\Common\Service\AbstractService as BaseAbstractService;
  12. abstract class AbstractService extends BaseAbstractService implements ServiceInterface
  13. {
  14. /** @const OAUTH_VERSION */
  15. const OAUTH_VERSION = 1;
  16. /** @var SignatureInterface */
  17. protected $signature;
  18. /** @var UriInterface|null */
  19. protected $baseApiUri;
  20. /**
  21. * {@inheritDoc}
  22. */
  23. public function __construct(
  24. CredentialsInterface $credentials,
  25. ClientInterface $httpClient,
  26. TokenStorageInterface $storage,
  27. SignatureInterface $signature,
  28. UriInterface $baseApiUri = null
  29. ) {
  30. parent::__construct($credentials, $httpClient, $storage);
  31. $this->signature = $signature;
  32. $this->baseApiUri = $baseApiUri;
  33. $this->signature->setHashingAlgorithm($this->getSignatureMethod());
  34. }
  35. /**
  36. * {@inheritDoc}
  37. */
  38. public function requestRequestToken()
  39. {
  40. $authorizationHeader = array('Authorization' => $this->buildAuthorizationHeaderForTokenRequest());
  41. $headers = array_merge($authorizationHeader, $this->getExtraOAuthHeaders());
  42. $responseBody = $this->httpClient->retrieveResponse($this->getRequestTokenEndpoint(), array(), $headers);
  43. $token = $this->parseRequestTokenResponse($responseBody);
  44. $this->storage->storeAccessToken($this->service(), $token);
  45. return $token;
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. public function getAuthorizationUri(array $additionalParameters = array())
  51. {
  52. // Build the url
  53. $url = clone $this->getAuthorizationEndpoint();
  54. foreach ($additionalParameters as $key => $val) {
  55. $url->addToQuery($key, $val);
  56. }
  57. return $url;
  58. }
  59. /**
  60. * {@inheritDoc}
  61. */
  62. public function requestAccessToken($token, $verifier, $tokenSecret = null)
  63. {
  64. if (is_null($tokenSecret)) {
  65. $storedRequestToken = $this->storage->retrieveAccessToken($this->service());
  66. $tokenSecret = $storedRequestToken->getRequestTokenSecret();
  67. }
  68. $this->signature->setTokenSecret($tokenSecret);
  69. $bodyParams = array(
  70. 'oauth_verifier' => $verifier,
  71. );
  72. $authorizationHeader = array(
  73. 'Authorization' => $this->buildAuthorizationHeaderForAPIRequest(
  74. 'POST',
  75. $this->getAccessTokenEndpoint(),
  76. $this->storage->retrieveAccessToken($this->service()),
  77. $bodyParams
  78. )
  79. );
  80. $headers = array_merge($authorizationHeader, $this->getExtraOAuthHeaders());
  81. $responseBody = $this->httpClient->retrieveResponse($this->getAccessTokenEndpoint(), $bodyParams, $headers);
  82. $token = $this->parseAccessTokenResponse($responseBody);
  83. $this->storage->storeAccessToken($this->service(), $token);
  84. return $token;
  85. }
  86. /**
  87. * Refreshes an OAuth1 access token
  88. * @param TokenInterface $token
  89. * @return TokenInterface $token
  90. */
  91. public function refreshAccessToken(TokenInterface $token)
  92. {
  93. }
  94. /**
  95. * Sends an authenticated API request to the path provided.
  96. * If the path provided is not an absolute URI, the base API Uri (must be passed into constructor) will be used.
  97. *
  98. * @param string|UriInterface $path
  99. * @param string $method HTTP method
  100. * @param array $body Request body if applicable (key/value pairs)
  101. * @param array $extraHeaders Extra headers if applicable.
  102. * These will override service-specific any defaults.
  103. *
  104. * @return string
  105. */
  106. public function request($path, $method = 'GET', $body = null, array $extraHeaders = array())
  107. {
  108. $uri = $this->determineRequestUriFromPath($path, $this->baseApiUri);
  109. /** @var $token StdOAuth1Token */
  110. $token = $this->storage->retrieveAccessToken($this->service());
  111. $extraHeaders = array_merge($this->getExtraApiHeaders(), $extraHeaders);
  112. $authorizationHeader = array(
  113. 'Authorization' => $this->buildAuthorizationHeaderForAPIRequest($method, $uri, $token, $body)
  114. );
  115. $headers = array_merge($authorizationHeader, $extraHeaders);
  116. return $this->httpClient->retrieveResponse($uri, $body, $headers, $method);
  117. }
  118. /**
  119. * Return any additional headers always needed for this service implementation's OAuth calls.
  120. *
  121. * @return array
  122. */
  123. protected function getExtraOAuthHeaders()
  124. {
  125. return array();
  126. }
  127. /**
  128. * Return any additional headers always needed for this service implementation's API calls.
  129. *
  130. * @return array
  131. */
  132. protected function getExtraApiHeaders()
  133. {
  134. return array();
  135. }
  136. /**
  137. * Builds the authorization header for getting an access or request token.
  138. *
  139. * @param array $extraParameters
  140. *
  141. * @return string
  142. */
  143. protected function buildAuthorizationHeaderForTokenRequest(array $extraParameters = array())
  144. {
  145. $parameters = $this->getBasicAuthorizationHeaderInfo();
  146. $parameters = array_merge($parameters, $extraParameters);
  147. $parameters['oauth_signature'] = $this->signature->getSignature(
  148. $this->getRequestTokenEndpoint(),
  149. $parameters,
  150. 'POST'
  151. );
  152. $authorizationHeader = 'OAuth ';
  153. $delimiter = '';
  154. foreach ($parameters as $key => $value) {
  155. $authorizationHeader .= $delimiter . rawurlencode($key) . '="' . rawurlencode($value) . '"';
  156. $delimiter = ', ';
  157. }
  158. return $authorizationHeader;
  159. }
  160. /**
  161. * Builds the authorization header for an authenticated API request
  162. *
  163. * @param string $method
  164. * @param UriInterface $uri The uri the request is headed
  165. * @param TokenInterface $token
  166. * @param array $bodyParams Request body if applicable (key/value pairs)
  167. *
  168. * @return string
  169. */
  170. protected function buildAuthorizationHeaderForAPIRequest(
  171. $method,
  172. UriInterface $uri,
  173. TokenInterface $token,
  174. $bodyParams = null
  175. ) {
  176. $this->signature->setTokenSecret($token->getAccessTokenSecret());
  177. $authParameters = $this->getBasicAuthorizationHeaderInfo();
  178. if (isset($authParameters['oauth_callback'])) {
  179. unset($authParameters['oauth_callback']);
  180. }
  181. $authParameters = array_merge($authParameters, array('oauth_token' => $token->getAccessToken()));
  182. $authParameters = (is_array($bodyParams)) ? array_merge($authParameters, $bodyParams) : $authParameters;
  183. $authParameters['oauth_signature'] = $this->signature->getSignature($uri, $authParameters, $method);
  184. if (isset($bodyParams['oauth_session_handle'])) {
  185. $authParameters['oauth_session_handle'] = $bodyParams['oauth_session_handle'];
  186. unset($bodyParams['oauth_session_handle']);
  187. }
  188. $authorizationHeader = 'OAuth ';
  189. $delimiter = '';
  190. foreach ($authParameters as $key => $value) {
  191. $authorizationHeader .= $delimiter . rawurlencode($key) . '="' . rawurlencode($value) . '"';
  192. $delimiter = ', ';
  193. }
  194. return $authorizationHeader;
  195. }
  196. /**
  197. * Builds the authorization header array.
  198. *
  199. * @return array
  200. */
  201. protected function getBasicAuthorizationHeaderInfo()
  202. {
  203. $dateTime = new \DateTime();
  204. $headerParameters = array(
  205. 'oauth_callback' => $this->credentials->getCallbackUrl(),
  206. 'oauth_consumer_key' => $this->credentials->getConsumerId(),
  207. 'oauth_nonce' => $this->generateNonce(),
  208. 'oauth_signature_method' => $this->getSignatureMethod(),
  209. 'oauth_timestamp' => $dateTime->format('U'),
  210. 'oauth_version' => $this->getVersion(),
  211. );
  212. return $headerParameters;
  213. }
  214. /**
  215. * Pseudo random string generator used to build a unique string to sign each request
  216. *
  217. * @param int $length
  218. *
  219. * @return string
  220. */
  221. protected function generateNonce($length = 32)
  222. {
  223. $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
  224. $nonce = '';
  225. $maxRand = strlen($characters)-1;
  226. for ($i = 0; $i < $length; $i++) {
  227. $nonce.= $characters[rand(0, $maxRand)];
  228. }
  229. return $nonce;
  230. }
  231. /**
  232. * @return string
  233. */
  234. protected function getSignatureMethod()
  235. {
  236. return 'HMAC-SHA1';
  237. }
  238. /**
  239. * This returns the version used in the authorization header of the requests
  240. *
  241. * @return string
  242. */
  243. protected function getVersion()
  244. {
  245. return '1.0';
  246. }
  247. /**
  248. * Parses the request token response and returns a TokenInterface.
  249. * This is only needed to verify the `oauth_callback_confirmed` parameter. The actual
  250. * parsing logic is contained in the access token parser.
  251. *
  252. * @abstract
  253. *
  254. * @param string $responseBody
  255. *
  256. * @return TokenInterface
  257. *
  258. * @throws TokenResponseException
  259. */
  260. abstract protected function parseRequestTokenResponse($responseBody);
  261. /**
  262. * Parses the access token response and returns a TokenInterface.
  263. *
  264. * @abstract
  265. *
  266. * @param string $responseBody
  267. *
  268. * @return TokenInterface
  269. *
  270. * @throws TokenResponseException
  271. */
  272. abstract protected function parseAccessTokenResponse($responseBody);
  273. }