Jiri Mihal Jiri Mihal - 1 month ago 14
PHP Question

Twitter sign-in user with PHP and curl

I'm trying to signin the user just with PHP and

curl
, without any external libraries for Twitter authentication. I follow these Twitter instructions. But I can't figure out how to complete step 3.

1. Obtaining a request token (done)

2. Redirecting the user (done)

3. Converting the request token to an access token. (done * problem)

Here is my code:

// API SETTINGS
$consumerSecret = 'xxx';
$consumerKey = 'xxx';
$oauth_signature_method = 'HMAC-SHA1';

if (!isset($_GET['oauth_verifier'])) {

// STEP 1 - TWITTER OBTAINING A REQUEST TOKEN
$callbackUrl = 'http://localhost/skeletons/webiik/example/login/';
$url = 'https://api.twitter.com/oauth/request_token';

// Data we will send
$data = [
'oauth_callback' => $callbackUrl,
'oauth_consumer_key' => $consumerKey,
'oauth_signature_method' => $oauth_signature_method,
'oauth_timestamp' => time(),
'oauth_nonce' => $token->generate(3),
'oauth_version' => '1.0',
];

// Sort data alphabetically, because Twitter requires that
ksort($data);

// Generate signature and add it to data array
$signData = 'POST&' . urlencode($url) . '&' . urlencode(http_build_query($data));
$secret = '';
$signKey = urlencode($consumerSecret) . '&' . urlencode($secret);
$data['oauth_signature'] = base64_encode(hash_hmac('sha1', $signData, $signKey, true));

// Prepare http headers from data
$httpHeaders = [];
foreach ($data as $key => $value) {
$httpHeaders[] = urlencode($key) . '="' . urlencode($value) . '"';
}

// Add OAuth header with all data
$httpHeaders = 'Authorization: OAuth ' . implode(', ', $httpHeaders);

// Send post request to Twitter API with http headers and data
$res = $http->post($url, ['httpHeaders' => [$httpHeaders]], []);

// If we got some error, show error message and stop
if (count($res['err']) > 0) {
echo $res['err'];
exit;
}

// Prepare data for step 2 and 3 from Twitter's response
parse_str($res['body'], $res);
$oauth_callback_confirmed = $res['oauth_callback_confirmed'];
$oauth_request_token = $res['oauth_token'];

// Store oauth_token_secret into session, we will need it in step 3
$this->sessions->setToSession('oauth_token_secret', $res['oauth_token_secret']);
$this->sessions->setToSession('oauth_token', $oauth_request_token);

// STEP 2 - REDIRECTING THE USER TO TWITTER LOGIN
header('HTTP/1.1 302 Found');
header('Location: https://api.twitter.com/oauth/authenticate?oauth_token=' . urlencode($oauth_request_token));
}

// STEP 3 - CONVERTING THE REQUEST TOKEN TO AN ACCESS TOKEN
$url = 'https://api.twitter.com/oauth/access_token';
$oauth_token = $_GET['oauth_token'];
$oauth_verifier = $_GET['oauth_verifier'];

// Data we will send
$data = [
'oauth_consumer_key' => $consumerKey,
'oauth_nonce' => $token->generate(3),
'oauth_signature_method' => $oauth_signature_method,
'oauth_timestamp' => time(),
'oauth_token' => $oauth_token,
'oauth_version' => '1.0',
];

// Sort data alphabetically, because Twitter requires that
ksort($data);

// Generate signature and add it to data array
$signData = 'POST&' . urlencode($url) . '&' . urlencode(http_build_query($data));
$secret = $this->sessions->getFromSession('oauth_token_secret');
$signKey = urlencode($consumerSecret) . '&' . urlencode($secret);
$data['oauth_signature'] = base64_encode(hash_hmac('sha1', $signData, $signKey, true));

// Sort data also with added oauth_signature, just for sure
ksort($data);

// Prepare http headers from data
$httpHeaders = [];
foreach ($data as $key => $value) {
$httpHeaders[] = urlencode($key) . '="' . urlencode($value) . '"';
}

// Add OAuth header with all data
$httpHeaders = ['Authorization: OAuth ' . implode(', ', $httpHeaders)];
$httpHeaders[] = 'Content-Length: ' . strlen('oauth_verifier=' . urlencode($oauth_verifier));
$httpHeaders[] = 'Content-Type: application/x-www-form-urlencoded';

// Add oauth_verifier to POST data
$postData = ['oauth_verifier' => $oauth_verifier];

// Send post request to Twitter API with http headers and data
$res = $http->post($url, ['httpHeaders' => $httpHeaders], $postData);

// If we got some error, show error message and stop
if (count($res['err']) > 0) {
echo $res['err'];
exit;
}

print_r($res);


Edit 1:
$http is just object that creates standard curl requests. In third step the curl request looks like:

$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FAILONERROR => 1,
CURLOPT_VERBOSE => 1,
CURLOPT_HEADER => 1,
CURLOPT_HTTPHEADER => $arrayOfHttpHeaders,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => http_build_query($postData),
]);

$response = curl_exec($curl);

// Process response...


Edit 2:
I just updated the code to be more accurate to Twitter's example. Now I really don't know where can be the problem.

Edit 3:
Solution: The code is ok. I had problem with router, which added slash at the end of URL. And I grabbed verifier_token with that slash.

Answer

The question is the answer by itself.

Comments