"Failed to validate oauth signature and token" when acquiring a request_token


#1

I built a test App, and try to obtain a request token.
The parameters are like this:

Method	POST
URL	https://api.twitter.com/oauth/request_token
oauth_callback	http://127.0.0.1/twitter/index.php/login/oauth2callback
oauth_consumer_key	i4GNoh2CXkB1T152BlD1A
oauth_nonce	MTM5MzQ5MTMwOA
oauth_signature_method	HMAC-SHA1
oauth_timestamp	1393492670
oauth_version	1.0

Then I composed a base string for calculating signature:

POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Dhttp%253A%252F%252F127.0.0.1%252Ftwitter%252Findex.php%252Flogin%252Foauth2callback%26oauth_consumer_key%3Di4GNoh2CXkB1T152BlD1A%26oauth_nonce%3DMTM5MzQ5MjY3MA%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1393492670%26oauth_version%3D1.0

The signing key is like this:(don’t worry, this is not the real consumer secret, I just forged one :))

MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98&

So the signature is:

C53UjwtSef1JlxfLuzB8d9d2B5I=

The signature passed the verification on the online tool: http://quonos.nl/oauthTester/
After fill the header array with the signature, we got a header string:

OAuth oauth_callback="http%3A%2F%2F127.0.0.1%2Ftwitter%2Findex.php%2Flogin%2Foauth2callback", oauth_consumer_key="i4GNoh2CXkB1T152BlD1A", oauth_nonce="MTM5MzQ5MjY3MA", oauth_signature="C53UjwtSef1JlxfLuzB8d9d2B5I%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1393492670", oauth_version="1.0"

Finally, I made a HTTP request by PHP code, like this:

$ch = curl_init('https://api.twitter.com/oauth/request_token');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROXY, $this->proxy);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization' => $headerString));
$output = curl_exec(($ch));
echo '<br>output of request token is: ' . $output . '<br>';

the output is: Failed to validate oauth signature and token
and the error code is 401
My machine is located in China, where the whole country is blocked from accessing Twitter. That’s why I used a proxy in the code.
You may check my machine’s time by examine the timestamp with this post time.
Any ideas?


#2

Fixed.

the way I used set header string is wrong.
The right way is like this:

$ch = curl_init($this->tokenBaseURL);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROXY, $this->proxy);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ’ . $headerString));

now, the token has been successfully fetched back.


#3

Do you think that you could post all of your working php code somewhere? I’m running into similar problems and can’t figure out where I’m going wrong.

Thanks


#4

Hi Barth, I use Codeigniter as the web framework. My main code is as pasted:

<?php class Login extends CI_Controller { var $tokenBaseURL = 'https://api.twitter.com/oauth/request_token'; var $oauth_callback = 'http://the unencoded callback url you registered on the twitter App web site'; var $oauth_signature_method = "HMAC-SHA1"; var $oauth_version = "1.0"; var $oauth_consumer_key = 'the consumer key, without urlencode '; var $oauth_consumer_secret = 'the consumer secret, without urlencode'; var $proxy = 'the http proxy if needed:8080'; public function Login(){ parent::__construct(); $this->load->helper('url'); $this->load->library('curl'); $this->load->library('session'); } public function index(){ $this->load->view('login'); } public function obtainRequestToken(){ $paramsWitoutSignature = $this->getFields2RequestToken(); $signature = $this->calculateSignature('post', $this->tokenBaseURL, $paramsWitoutSignature); echo '
the signature is: '. $signature . '
'; $headerParams = $paramsWitoutSignature; $headerParams['oauth_signature'] = $signature; $headerString = $this->generateHeaderString($headerParams); echo '
the header string is: '; var_dump(array('Authorization' => $headerString)); echo '
'; $ch = curl_init($this->tokenBaseURL); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch,CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_PROXY, $this->proxy); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ' . $headerString)); $output = curl_exec(($ch)); echo '
output of the requested token is: ' . $output . '
'; curl_close($ch); } private function generateHeaderString($params) { ksort($params); $headerString = "OAuth "; $firstPair = True; foreach ($params as $key => $value) { if($firstPair) { $firstPair = False; } else { $headerString = $headerString . ", "; } $headerString = $headerString . rawurlencode($key) . "=\"" . rawurlencode($value) . "\""; } return $headerString; } public function oauth2callback(){ $code = $this->input->get('code', TRUE); $this->session->set_userdata('code', $code); $data['code'] = $code; $this->load->view('oauth2callback', $data); } private function getFields2RequestToken(){ $ts = time(); return array( //'oauth_callback' => rawurldecode($this->oauth_callback), 'oauth_callback' => $this->oauth_callback, 'oauth_consumer_key' => $this->oauth_consumer_key, 'oauth_nonce' => trim(base64_encode($ts), '='), //'oauth_signature' => to be calculated, 'oauth_signature_method' => $this->oauth_signature_method, 'oauth_timestamp' => $ts, 'oauth_version' => $this->oauth_version ); } private function calculatePostFileds($paramsArray){ $postFiliedsPairs =array(); foreach ($paramsArray as $key => $value) { $key = rawurlencode($key); $value = rawurlencode($value); $postFiliedsPairs[] = "{$key}={$value}"; } return implode('&', $postFiliedsPairs); } private function calculateSignature($HTTPMethod, $baseURL, $paramsArray){ //Step 1. Collecting parameters //1. Percent encode every key and value that will be signed. $encodedPrams = array(); foreach ($paramsArray as $key => $value) { $encodedPrams[rawurlencode($key)] = rawurlencode($value); } //2. Sort the list of parameters alphabetically[1] by encoded key[2]. ksort($encodedPrams); //3. For each key/value pair: // Append the encoded key to the output string. // Append the '=' character to the output string. // Append the encoded value to the output string. // If there are more key/value pairs remaining, append a '&' character to the output string. $paramsString = ""; foreach ($encodedPrams as $key => $value) { $paramsString = $paramsString . $key . "=" . $value . "&"; } $paramsString = substr($paramsString,0,-1); //Step 2. Creating the signature base string //1. Convert the HTTP Method to uppercase and set the output string equal to this value. //2. Append the '&' character to the output string. //3. Percent encode the URL and append it to the output string. //4. Append the '&' character to the output string. //5. Percent encode the parameter string and append it to the output string. $baseString = strtoupper($HTTPMethod) . '&' . rawurlencode($baseURL) . '&' . rawurlencode($paramsString); //Step 3. Getting a signing key //The signing key is simply the percent encoded consumer secret, //followed by an ampersand character '&', followed by the percent encoded token secret: $signedKey = rawurlencode($this->oauth_consumer_secret) . '&'; //. rawurlencode($this->oauth_token_secret); //Step 4. Calculating the signature //Finally, the signature is calculated by passing the signature base string and signing key to the HMAC-SHA1 hashing algorithm. return base64_encode(hash_hmac('sha1', $baseString, $signedKey, true)); } } ?> If you suspect the calculating of your signature, I suggest you to check the result by entering your signature base string to this online testing site:http://quonos.nl/oauthTester/

#5

One thing more that every twitter developer should know, and I’m pretty sure about that, in the 1st step of authentication process, the oauth_callback is not a must when requesting a token. You can fill it in the POST header, it works, but you can also omit it, it works too…