Oauth Echo 401 Error


#1

Hello,
I’m creating an API for my Twitter app and i’m using Twitter’s OAuth Echo.
Here’s my code:

$header_info = apache_request_headers();
$header = 'Authorization : ' . $header_info['X-Verify-Credentials-Authorization'];

Here’s how the $header_info[‘X-Verify-Credentials-Authorization’] looks like (i’ve just replace actual values with XXXXXX):

OAuth realm=“http://api.twitter.com/”, oauth_consumer_key=“XXXXXX”, oauth_signature_method=“HMAC-SHA1”, oauth_token=“XXXXXX”, oauth_timestamp=“1329026580”, oauth_nonce=“1329026580”, oauth_version=“1.0”, oauth_signature=“XXXXXX”

Code to pass the header to Twitter:

$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, "myAgent");
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_URL,$header_info['X-Auth-Service-Provider']);
$result = curl_exec($ch);
$response_info=curl_getinfo($ch);
curl_close($ch);

I’m always getting a 401 error. Any help would be greatly appreciated.
Thanks,
Krishley


#2

I’m having some doubt about the oauth_signature. Here’s how it’s generated :

$url = "http://api.twitter.com/1/account/verify_credentials.json";

$consumer_key = “XXXXXX”;
$consumer_secret = “XXXXXX”;
$oauth_access_token = “XXXXXX”;
$oauth_access_token_secret = “XXXXXX”;

function buildBaseString($baseURI, $method, $params)
{
$r = array();
ksort($params);
foreach($params as $key=>$value){
$r[] = “$key=” . rawurlencode($value);
}

return $method."&" . rawurlencode($baseURI) . '&' . rawurlencode(implode('&', $r)); //return complete base string

}

function buildAuthorizationHeader($oauth)
{
$r = 'X-Verify-Credentials-Authorization ';
$values = array();
foreach($oauth as $key=>$value)
$values[] = “$key=”" . rawurlencode($value) . “”";

$r .= implode(', ', $values);
return $r;

}

$oauth = array( ‘oauth_consumer_key’ => $consumer_key,
‘oauth_signature_method’ => ‘HMAC-SHA1’,
‘oauth_token’ => $oauth_access_token,
‘oauth_timestamp’ => time(),
‘oauth_nonce’ => time(),
‘oauth_version’ => ‘1.0’);

$base_info = buildBaseString($url, ‘GET’, $oauth);
$composite_key = rawurlencode($consumer_secret) . ‘&’ . rawurlencode($oauth_access_token_secret);
$oauth_signature = base64_encode(hash_hmac(‘sha1’, $base_info, $composite_key, true));

Is that good? I’m testing all the above code on a localhost but i don’t think that should be a problem or is it?

Cheers,
Krishley


#3

From your example I’m having a hard time understanding which step of the process you’re trying to code toward. Are you trying to build a service that will receive OAuth Echo requests and then relay them to Twitter or are you building a client that is sending OAuth Echo requests to be relayed by a service?


#4

Hi Taylor,
Sorry for the confusion. I’m building a service that will receive OAuth Echo requests and then relay them to Twitter. That’s the code in my first post. I’m the Delegator part. My app : http://www.gwitr.com .

Now i wanted to check if my script would work correctly thus i coded a test script to simulate a consumer that is sending me an authorization header. That code is in my second post.

But it’s not working. What is wrong here? Is the signature generated by the test script wrongly generated or am i not passing the headers in the right way to Twitter?

Thanks for your help.
Krishley


#5

What are some example values that you’re sending? Have you tried executing the request you’re building to account/verify_credentials to make sure that the core request you’re building (that your service will then “echo”) is correct from the get-go?

It looks like your first request might be incomplete… let’s review the steps of an OAuth Echo implementation.

  1. The client prepares a mock request to account/verify_credentials - do everything the client would do to make this request to account/verify_credentials without actually making it. Store the executed URL and the Authorization header you would have used for later.

  2. Formulate a new request to the OAuth Echo-enabled service provider. This request will be to the service’s (your) reception end point.

  • Include the Authorization header you prepared in step 1 as a “X-Verify-Credentials-Authorization” HTTP header.
  • Include the URL you used to generate your signature base string for the authorization header as your “X-Auth-Service-Provider” HTTP header.
  • Any other headers or parameters you send to the service provider are service-provider specific. You shouldn’t send anything else Twitter related unless it’s necessary.
  1. Your service provider gets this OAuth Echo request and now needs to reconstitute the request and send along to Twitter.
  • Take the value you got from the “X-Auth-Service-Provider” HTTP header value and verify that, at the very least, the domain points to something you’re willing to honor. (Something on twitter.com in this case – you don’t want to just execute any random request someone throws at you).
  • If the value is worthwhile to you, set this value aside. It will be the URL you execute in your next request.
  • Take the value you got from the “X-Verify-Credentials-Authorization” HTTP header and save it. You’ll use this as the HTTP “Authorization” header in the next step.
  1. Prepare your echo’d request
  • This request doesn’t need any kind of additional OAuth processing
  • Make the request as simple as possible. Perform a GET to the URL in X-Auth-Service-Provider with the Authorization header set to the value from X-Verify-Credentials Authorization. Don’t add any POST parameters or any other additional headers. Perform this step as close to receiving the request as possible.

Hope this helps!


#6

Hi Taylor,
I’ve tried all that you mentioned but i’m still getting the 401 error. I cannot post my consumer keys and access tokens here. Do you have an email address where i can send you my scripts?

Thanks,
Krishley


#7

Ok problem solved :slight_smile: It’s working now. I will post the code shortly.


#8

For those getting the 401 error too, here’s how i solved mine:

My second post contains the php codes that i use to generate the oauth_signature. This whole part is correct. If you do not want to go this way, then you can always use Abraham’s OAuth library. You can download dabr source code here : http://code.google.com/p/dabr/ You can check the codes to see how the oauth_signature is generated and how it is sent in headers to the delegator.

Now the real problem was how i was passing the authorization header to Twitter. I was doing this :

$header_info = apache_request_headers(); $header = 'Authorization : ' . $header_info['X-Verify-Credentials-Authorization'];

And then was putting this in cURL :

curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

This does not work. You have to extract each parameters (oauth_consumer_key, oauth_nonce etc…) from the X-Verify-Credentials-Authorization that you receive and then build a GET request like this :

http://api.twitter.com/1/account/verify_credentials.json?oauth_consumer_key=XXXXXX&oauth_signature_method=HMAC-SHA1&oauth_token=XXXXXX&oauth_timestamp=1329026580&oauth_nonce=1329026580&oauth_version=1.0&oauth_signature=XXXXXX

Then just use cURL like this:

curl_setopt($ch,CURLOPT_URL,‘GET REQUEST HERE’);

One thing that i’ve noticed is that if the parameters values are between " ,they will not work
e.g oauth_token=“XXXXX”&oauth_nonce=“abcdef” -> This does not work
oauth_token=XXXXX&oauth_nonce=abcdef -> This works.

Thanks a lot for your help Taylor!

Now even though everything is working fine, in every 10-15 requests i get at least one that returns a 401 error. Any idea what’s causing this Taylor? Maybe a problem from Twitter’s side?

Cheers,
Krishley


#9

I’m having a similar problem (i.e. getting a 401 when I post, even though I can verify my credentials successfully). I’m pretty sure it’s a headers problem - here’s a my code:

function post() { $token = ACCES_TOKEN; $secret = ACCESS_TOKEN_SECRET;
    $sigBase = "POST&" . rawurlencode($this->postURL) . "&"
            . rawurlencode("oauth_consumer_key=" . rawurlencode($this->consumerKey)
                    . "&oauth_nonce=" . rawurlencode($this->nonce)
                    . "&oauth_signature_method=" . rawurlencode($this->oauthSignatureMethod)
                    . "&oauth_timestamp=" . rawurlencode($this->time)
                    . "&oauth_token=" . rawurlencode($token)
                    . "&oauth_version=" . rawurlencode($this->oauthVersion)
                    . "&status=" . rawurlencode("Testing"));
    $sigKey = $this->consumerSecret . "&" . $secret;
    $oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));

    $authHeader = "OAuth oauth_consumer_key=" . rawurlencode($this->consumerKey) . ","
            . "oauth_nonce=" . rawurlencode($this->nonce) . ","
            . "oauth_signature_method=" . rawurlencode($this->oauthSignatureMethod) . ","
            . "oauth_signature=" . rawurlencode($oauthSig) . ","
            . "oauth_timestamp=" . rawurlencode($this->time) . ","
            . "oauth_token=" . rawurlencode($token) . ","
            . "oauth_version=" . rawurlencode($this->oauthVersion);
    $httpPostDataUrl = "status=" . "Testing";

    $context = array(
        "method" => "POST",
        "header" => "Content-Type: application/x-www-form-urlencoded\r\nAuthorization: " . $authHeader . "\r\n",
        "content" => $httpPostDataUrl);

    $c = curl_init();
    curl_setopt($c, CURLOPT_URL, $this->postURL);
    curl_setopt($c, CURLOPT_HEADER, 1);
    curl_setopt($c, CURLOPT_HTTPHEADER, $context);
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    curl_exec($c);
    $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
    $error = curl_getinfo($c);
    curl_close($c);

    if ($code == '201') {
        echo 'Success!';
    } else {
        echo '<strong>Error</strong>: ';
        print_r($error);
    }

I’ve fiddled with this code so much, but no luck. Can you help?


#10

Hi Lee,
Why don’t you simply use Abraham’s php library? It’s makes communicating with Twitter’s API a lot more easier!

Here’s how you post a Tweet with it :

require_once "twitteroauth.php"; require_once "oauth.php";

$connection = new TwitterOAuth(CONSUMER KEY, CONSUMER SECRET, ACCESS TOKEN, ACCESS TOKEN SECRET);
$credential = $connection->get(‘account/verify_credentials’);

if ($connection->http_code==200)
{
//Post tweet to Twitter
$tweet = “TEST TWEET”;
$post_tweet = $connection->post(‘statuses/update’, array(‘status’ => $tweet));
if ($connection->http_code==200)
{
echo “Your tweet was posted to Twitter!”;
}
else
{
echo $connection->http_header[‘status’];
}
}
else
{
//echo error message from Twitter
echo $connection->http_header[‘status’];
}

Hope this helps!
Cheers,
Krishley


#11

Hi Krishley,

I appreciate that, but I’m really trying to understand how this all works from scratch, so a library would defeat the purpose. This is more about the method than the outcome for me, frankly.


#12

Have you compared the results you’re getting when creating your signature basestring here to the results you get from the OAuth Tool available to you on this site under your application settings? Based on the code here, it looks like your signature base string will have “&” and “=” values where you still need escaping: %26 and %3D. The “&” character in the basestring is reserved for separating the components of the base string, and needs to be escaped in the parameters section of the basestring.

It’s also best to generalize the construction of the basestring a little further – status will always be your last parameter here in the case of it being the only parameter you send, but if you’re appending any other parameters they’ll need to be sorted in effectively, and you may as well write the parameter sorter while you’re at this.


#13

Thanks episod. Here’s how I constructed my signature base string:

POST&http%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.json&oauth_consumer_key%3D<CONSUMER_KEY>%26oauth_nonce%3D6c954ef6278ae661720f14bb5882fd10%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1329341008%26oauth_token%3D<ACCESS_TOKEN>%26oauth_version%3D1.0%26status%3DTesting

Here’s how it is in the OAuth Tool on this site under my application settings:

POST&http%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.json&oauth_consumer_key%3D<CONSUMER_KEY>%26oauth_nonce%3D5720972d9f22077c50549d2fddb52827%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1329339667%26oauth_token%3D<ACCESS_TOKEN>%26oauth_version%3D1.0%26status%3DTesting

I don’t see anything different here beyond what you’d expect with timestamp & nonce, so I have my doubts that this is an issue with how the signature base string is constructed.


#14

Lee, i don’t think the oauth_signature is the problem here. I’m building the signature using the function in my second post. and when i pass this to verify credentials it works. But when i put the signature generated by that function in the code you posted above, it does not work.

My guess here is that you are sending the authorization headers and parameters required in the wrong way. I’ve been trying different things too but still no luck. I have to get this to work for my project too.

Would be nice if Twitter has some sample php codes for this.


#15

Krishley, I have that feeling too. I based my code on a tutorial I found here: http://phpmaster.com/understanding-oauth-2/

Unfortunately, as you’ve seen, it doesn’t work - and as you say, this is probably down to the way that the authorization headers and parameters are being sent. If you do work this out then let me know, and in the meantime I’ll try different variations of the code in the hope that something might work!