Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
92.86% |
13 / 14 |
CRAP | |
98.20% |
109 / 111 |
Social\Providers\TwitterProvider | |
0.00% |
0 / 1 |
|
92.86% |
13 / 14 |
42 | |
98.20% |
109 / 111 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
setProviderName | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
getRedirectRoute | |
100.00% |
1 / 1 |
7 | |
100.00% |
9 / 9 |
|||
sendClientRequest | |
100.00% |
1 / 1 |
5 | |
100.00% |
12 / 12 |
|||
getCredentials | |
100.00% |
1 / 1 |
4 | |
100.00% |
15 / 15 |
|||
checkCredentials | |
100.00% |
1 / 1 |
4 | |
100.00% |
10 / 10 |
|||
makeRequest | |
100.00% |
1 / 1 |
5 | |
100.00% |
18 / 18 |
|||
setDefaultParams | |
100.00% |
1 / 1 |
3 | |
100.00% |
12 / 12 |
|||
buildAuthorisation | |
0.00% |
0 / 1 |
5.12 | |
83.33% |
10 / 12 |
|||
getSignature | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
getSignatureBaseString | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
getNonce | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getTimestamp | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
urlencodeRfc3986 | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
<?php | |
/** | |
* Class TwitterProvider | |
* | |
* @package Social\Providers | |
* @author Nigel Hurnell | |
* @version v 1.0.0 | |
* @license BSD | |
* @copyright Copyright (c) 2017, Nigel Hurnell | |
*/ | |
namespace Social\Providers; | |
use Social\Providers\ProviderInterface\ProviderInterface; | |
use Social\Service\SocialManager; | |
/** | |
* Class GoogleProvider | |
* Social Media OAuth1 provider for TWITTER | |
* | |
* @package Social\Providers | |
* @author Nigel Hurnell | |
* @version v 1.0.0 | |
* @license BSD | |
* @copyright Copyright (c) 2017, Nigel Hurnell | |
*/ | |
class TwitterProvider implements ProviderInterface | |
{ | |
/** | |
* TWITTER's base authorisation URL | |
* | |
* @var string | |
*/ | |
protected $baseUrl = 'https://api.twitter.com/'; // oauth/authenticate | |
/** | |
* Part of URL to append | |
* | |
* @var string | |
*/ | |
protected $preUrl = 'oauth/request_token'; | |
/** | |
* Type of hash to use | |
* | |
* @var string | |
*/ | |
protected $signatureMethod = 'HMAC-SHA1'; | |
/** | |
* TWITTER API version | |
* | |
* @var string | |
*/ | |
protected $oauthVersion = '1.0'; | |
/** | |
* Default parameters to use in the URL request | |
* | |
* @var array | |
*/ | |
protected $defaultParams = []; | |
/** | |
* social login or registration | |
* | |
* @var string | |
*/ | |
protected $action; | |
/** | |
* Constructor Instantiate class and pass Social manager and | |
* set the extending social provider name. | |
* | |
* @param SocialManager $socialManager | |
*/ | |
public function __construct(SocialManager $socialManager) | |
{ | |
$this->socialManager = $socialManager; | |
$this->setProviderName(); | |
} | |
/** | |
* Set the provider name | |
*/ | |
protected function setProviderName() | |
{ | |
$this->providerName = 'twitter'; | |
} | |
/** | |
* Get the full redirect URL (including query string) | |
* | |
* @param string $callback | |
* @return string full redirect URL | |
*/ | |
public function getRedirectRoute($callback) | |
{ | |
$this->callback = $callback; | |
$this->setDefaultParams(); | |
$path = 'oauth/request_token'; | |
if (false !== $response = $this->makeRequest($path, 'POST')) { | |
$result = []; | |
parse_str($response->getBody(), $result); | |
if (200 == $response->getStatusCode() && array_key_exists('oauth_callback_confirmed', $result) && $result['oauth_callback_confirmed'] == 'true' && array_key_exists('oauth_token', $result) && array_key_exists('oauth_token_secret', $result)) { | |
return $this->baseUrl . 'oauth/authenticate' . '?' . 'oauth_token=' . $result['oauth_token']; | |
} | |
} | |
throw new \Exception('Twitter returned an error'); | |
} | |
/** | |
* Send Client Request | |
* Form client request URL with query params and send via Zend Client | |
* | |
* @param string $callback the callback URL | |
* @param array $queryParams parameters to append to end of callback URL | |
* @return array that contains the user profile | |
*/ | |
public function sendClientRequest($callback, $queryParams) | |
{ | |
$this->callback = $callback; | |
if (array_key_exists('oauth_token', $queryParams) && array_key_exists('oauth_verifier', $queryParams)) { | |
$this->setDefaultParams($queryParams['oauth_token'], $queryParams['oauth_verifier']); | |
$path = 'oauth/access_token'; | |
if (false !== $response = $this->makeRequest($path, 'POST', ['oauth_verifier' => $queryParams['oauth_verifier']])) { | |
$result = []; | |
parse_str($response->getBody(), $result); | |
if (200 == $response->getStatusCode()) { | |
$result = []; | |
parse_str($response->getBody(), $result); | |
return $this->getCredentials($result); | |
} | |
} | |
} | |
throw new \Exception('Twitter returned an error (1).'); | |
} | |
/** | |
* Get TWITTER user's credentials | |
* | |
* @param type $queryParams | |
* @return type | |
* @throws \Exception | |
*/ | |
protected function getCredentials($queryParams) | |
{ | |
$this->checkCredentials($queryParams); | |
$this->setDefaultParams($queryParams['oauth_token']); | |
$path = '1.1/account/verify_credentials.json'; | |
$params = array('include_email' => 'true', 'include_entities' => 'false', 'skip_status' => 'true'); | |
if (false !== $response = $this->makeRequest($path, 'GET', $params, $queryParams['oauth_token_secret'])) { | |
$result = []; | |
parse_str($response->getBody(), $result); | |
if (200 == $response->getStatusCode()) { | |
$user = json_decode($response->getBody()); | |
if (isset($user->email)) { | |
return [ | |
'name' => var_export($user->name, true), | |
'email' => $user->email, | |
'id' => $user->id, | |
'provider' => $this->providerName | |
]; | |
} | |
} | |
} | |
throw new \Exception('Twitter could not get valid credentials.'); | |
} | |
/** | |
* CHeck that the returned array has the required keys | |
* | |
* @param array $queryParams | |
* @return JSON object | |
* @throws \Exception when returned object does not conform to requirements | |
*/ | |
protected function checkCredentials($queryParams) | |
{ | |
$error = ''; | |
switch (true) { | |
case (!array_key_exists('oauth_token', $queryParams)): | |
$error = 'oauth_token not in array'; | |
break; | |
case (!array_key_exists('oauth_token_secret', $queryParams)): | |
$error = 'oauth_token_secret not in array'; | |
break; | |
} | |
if ('' !== $error) { | |
throw new \Exception('Twitter returned an error "' . $error . '".'); | |
} | |
} | |
/** | |
* Make HTTP request | |
* | |
* @param string $path | |
* @param string $method | |
* @param array $params | |
* @param string $tokenSecret | |
* @return Response | |
*/ | |
protected function makeRequest($path, $method, $params = [], $tokenSecret = '') | |
{ | |
$authorization = $this->buildAuthorisation($path, $method, $tokenSecret, $params); | |
$client = $this->socialManager->getClient(); | |
$client->resetParameters(); | |
$headers = [ | |
'Accept' => 'application/json', | |
'Authorization' => $authorization, | |
'User-Agent' => 'TwitterOAuth (+https://twitteroauth.com) Adapted for Zend http client', | |
'Expect' => '', | |
]; | |
switch (true) { | |
case 'POST' == $method && count($params) > 0: | |
$client->setParameterPost($params); | |
break; | |
case 'GET' == $method && count($params) > 0: | |
$path .= '?' . http_build_query($params); | |
break; | |
default: | |
break; | |
} | |
$client->setUri($this->baseUrl . $path); | |
$client->setMethod($method); | |
$client->setHeaders($headers); | |
return $client->send(); | |
} | |
/** | |
* Populate defaultParams with TWITTER specific values | |
* | |
* @param string|false $oauthToken | |
* @param string|false $oauthVerifier | |
*/ | |
protected function setDefaultParams($oauthToken = false, $oauthVerifier = false) | |
{ | |
$this->defaultParams = [ | |
'oauth_consumer_key' => $this->socialManager->getModuleOptions()->getConsumerKey('twitter'), | |
'oauth_nonce' => $this->getNonce(), | |
'oauth_signature_method' => $this->signatureMethod, | |
'oauth_timestamp' => $this->getTimestamp(), | |
'oauth_version' => $this->oauthVersion, | |
'oauth_extra' => 'action' | |
]; | |
if (false !== $oauthToken) { | |
$this->defaultParams['oauth_token'] = $oauthToken; | |
} | |
if (false !== $oauthVerifier) { | |
$this->defaultParams['oauth_verifier'] = $oauthVerifier; | |
} | |
} | |
/** | |
* Build special authorisation hashed string specific to twitter | |
* | |
* @param string $path | |
* @param string $method | |
* @param string $tokenSecret | |
* @param array $params | |
* @return string | |
* @throws TwitterOAuthException | |
*/ | |
protected function buildAuthorisation($path, $method, $tokenSecret = '', $params = []) | |
{ | |
$first = true; | |
$this->defaultParams['oauth_signature'] = $this->getSignature(array_merge($this->defaultParams, $params), $path, $method, $tokenSecret); | |
$out = 'OAuth'; | |
foreach ($this->defaultParams as $k => $v) { | |
if (substr($k, 0, 5) != "oauth") { | |
continue; | |
} | |
if (is_array($v)) { | |
throw new \Exception('Arrays not supported in headers'); | |
} | |
$out .= ($first) ? ' ' : ', '; | |
$out .= $this->urlencodeRfc3986($k) . '="' . $this->urlencodeRfc3986($v) . '"'; | |
$first = false; | |
} | |
return $out; | |
} | |
/** | |
* Get specially constructed hashed string for TWITTER | |
* | |
* @param array $params | |
* @param string $path | |
* @param string $method | |
* @param string $tokenSecret | |
* @return string | |
*/ | |
protected function getSignature($params, $path, $method, $tokenSecret = '') | |
{ | |
$signatureBase = $this->getSignatureBaseString($params, $path, $method); | |
$secret = $this->socialManager->getModuleOptions()->getSecret('twitter'); | |
$key_parts = [$secret, $tokenSecret]; | |
$key = implode('&', $this->urlencodeRfc3986($key_parts)); | |
return base64_encode(hash_hmac('sha1', $signatureBase, $key, true)); | |
} | |
/** | |
* Get special query string ordered appropriately | |
* | |
* @param array $params | |
* @param string $path | |
* @param string $method | |
* @return string | |
*/ | |
protected function getSignatureBaseString($params, $path, $method) | |
{ | |
ksort($params); | |
$parts = [ | |
$method, | |
$this->baseUrl . $path, | |
http_build_query($params) | |
]; | |
return implode('&', $this->urlencodeRfc3986($parts)); | |
} | |
/** | |
* Get hashed string | |
* | |
* @return string | |
*/ | |
protected function getNonce() | |
{ | |
return md5(microtime() . mt_rand()); | |
} | |
/** | |
* Get time stamp | |
* | |
* @return string | |
*/ | |
protected function getTimestamp() | |
{ | |
return time(); | |
} | |
/** | |
* Special URL encoding | |
* | |
* @param array|string $input | |
* @return string | |
*/ | |
protected function urlencodeRfc3986($input) | |
{ | |
$output = ''; | |
if (is_array($input)) { | |
$output = array_map([$this, 'urlencodeRfc3986'], $input); | |
} elseif (is_scalar($input)) { | |
$output = rawurlencode($input); | |
} | |
return $output; | |
} | |
} |