Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authorizing with oAuth, posting to Twitter

I've created a Twitter Application to auto-post to my Twitter account. So, I don't need to authorize new users.

I've already set access level to read/write, and received an access token. I've used the OAuth Tool in the application to generate a cURL command:

curl --request 'POST' 'https://api.twitter.com/1/statuses/update.json' --data 'status=Maybe+he%27ll+finally+find+his+keys.+%23peterfalk' --header 'Authorization: OAuth oauth_consumer_key="...", oauth_nonce="97fad626790e8e5988d4a06cfd47fa74", oauth_signature="...", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1364161424", oauth_token="...", oauth_version="1.0"' --verbose

Above the command, it says:

Important: This will only be valid for a few minutes. Also remember the cURL command will actually execute the request.

I assume that this will work in a linux terminal.

I'd like to know how to translate this into a PHP cURL command. Here's what I've tried. Note that value of $DST is the value of "DST" in https://dev.twitter.com/docs/auth/authorizing-request; it is also equal to the value of the string after --header 'Authorization: in the cURL command in the OAuth tool.

$ch = curl_init();
curl_setopt_array($ch, array(
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => $url,
    CURLOPT_POST => 1,
    CURLOPT_POSTFIELDS => array(
        data => 'status='.urlencode($status),
        header => 'Authorization: '.$DST
    )
));
$resp = curl_exec($ch);
curl_close($ch);

But the value of $resp is:

{
request: "/1/statuses/update.json",
error: "Could not authenticate you."
}

Is there something I'm doing wrong? Note that the OAuth Tool said that the cURL command would actually work. So I think it's just a matter of figuring out how to arrange the cURL in PHP. I'm not very familiar with it. Also note that I want to avoid using the OAuth libraries if I can. I feel like there should be a much more light-weight solution to this than to install a whole library.

like image 845
bozdoz Avatar asked Oct 05 '22 15:10

bozdoz


2 Answers

I had three problems:

(1) I did not include it in this question, but my signature wasn't created correctly.

What I was doing was base64_encode(hash_hmac('sha1', $base, $key)); but I should have set the hash_hmac fourth parameter to true to return a raw binary instead of the hexidecimal (the hexidecimal is shown as the example in the Twitter docs, which was what was confusing me). The correct function is therefore: base64_encode(hash_hmac('sha1', $base, $key, true));.

(2) The cURL was not set up correctly. I required CURLOPT_HTTPHEADER to set the Authorization, and CURL_VERBOSE set to true:

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ' . $DST));
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'status='.rawurlencode($status));
curl_setopt($ch, CURLOPT_URL, $url);

(3) As seen in the above code, I had to post the status as a string, and not an array. I found the solution to this problem on this question: Why can't I authenticate with OAuth?.

All of this is now working perfectly. Also, make sure that your access token in your Twitter application says that the access level is "Read and write". If not, change the permissions in Settings, then go back to Details and recreate your access token.

Full Script

<?php

class Tweet {

public $url = 'https://api.twitter.com/1/statuses/update.json';

function the_nonce(){
    $nonce = base64_encode(uniqid());
    $nonce = preg_replace('~[\W]~','',$nonce);
    return $nonce;
}

function get_DST($status){

    $url = $this->url;

    $consumer_key = $your_consumer_key_here;
    $nonce = $this->the_nonce();
    $sig_method = 'HMAC-SHA1';
    $timestamp = time();
    $version = "1.0";
    $token = $your_token_here;
    $access_secret = $your_access_secret_here;
    $consumer_secret = $your_consumer_secret_here;

    $param_string = 'oauth_consumer_key='.$consumer_key.
            '&oauth_nonce='.$nonce.
            '&oauth_signature_method='.$sig_method.
            '&oauth_timestamp='.$timestamp.
            '&oauth_token='.$token.
            '&oauth_version='.$version.
            '&status='.rawurlencode($status);
    $sig_base_string = 'POST&'.rawurlencode($url).'&'.rawurlencode($param_string);
    $sig_key = rawurlencode($consumer_secret).'&'.rawurlencode($access_secret);

    $tweet_sig = base64_encode(hash_hmac('sha1', $sig_base_string, $sig_key, true));

    $DST = 'OAuth oauth_consumer_key="'.rawurlencode($consumer_key).'",'.
        'oauth_nonce="'.rawurlencode($nonce).'",'.
        'oauth_signature="'.rawurlencode($tweet_sig).'",'.
        'oauth_signature_method="HMAC-SHA1",'.
        'oauth_timestamp="'.rawurlencode($timestamp).'",'.
        'oauth_token="'.rawurlencode($token).'",'.
        'oauth_version="1.0"';
    return $DST;
}

function set($status){

    $url = $this->url;

    $ch = curl_init();

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ' . $this->get_DST($status)));
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'status='.rawurlencode($status));
curl_setopt($ch, CURLOPT_URL, $url);

    $result = json_decode(curl_exec($ch));
if(!curl_errno($ch)){
    $info = curl_getinfo($ch);
    if($info['http_code']!='200'){
//error posting
echo 'Error: '.$result->{'error'};
    }else{
//success
echo 'Success! <a href="https://twitter.com/AOKHalifax/status/'.$result->{'id_str'}.'" target="_blank">View Tweet</a>';
    }
}else{
    //error connecting
    echo 'error posting';
    }
    curl_close($ch);
    }
}

/* 
Usage example:
$status = new Tweet();
$status->set('checking'); 
*/

?>
like image 137
bozdoz Avatar answered Oct 20 '22 18:10

bozdoz


Hi and thank you for your help.

I had the same problem, you'll find below my code:

<?php
public function updateStatus($message)
{
    // Encoding message for the curl data parameter
    $messageData = rawurlencode($message);

    // Double encoding message for the message in signature base string
    $messageSignature = rawurlencode($messageData);

    // URL for posting a new tweet
    $statusURL = rawurlencode('https://api.twitter.com/'.$this->version.'/statuses/update.json');

    // Create oauth_nonce parameter
    $oauth_nonce = preg_replace('~[\W]~','',base64_encode(uniqid()));

    // Create timestamp
    $oauth_timestamp = time();

    // Create signature base string parameter
    $signature_base_string = "POST&".$statusURL."&oauth_consumer_key%3D".CB_TWITTER_API_KEY."%26oauth_nonce%3D".$oauth_nonce."%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D".$oauth_timestamp."%26oauth_token%3D".$this->sessionData['oauth_token']."%26oauth_version%3D1.0%26status%3D".$messageSignature."";

    // Create signature key 
    $signature_key = CB_TWITTER_SECRET_KEY.'&'.$this->sessionData['oauth_token_secret'];

    // Create new signature
    $newSignature = rawurlencode(base64_encode(hash_hmac('sha1', $signature_base_string, $signature_key, true)));

    // Create header
    $header="Authorization: 
                OAuth 
                oauth_consumer_key=\"".CB_TWITTER_API_KEY."\", 
                oauth_nonce=\"".$oauth_nonce."\", 
                oauth_signature=\"".$newSignature."\", 
                oauth_signature_method=\"HMAC-SHA1\", 
                oauth_timestamp=\"".$oauth_timestamp."\", 
                oauth_token=\"".$this->sessionData['oauth_token']."\", 
                oauth_version=\"1.0\"";

    // Replace line breaks and tabulations in header
    $header = preg_replace( "/\r|\n|\t/", "", $header);

    // Init cURL
    $ch = curl_init();

    // Put header
    curl_setopt($ch, CURLOPT_HTTPHEADER, array($header));

    // Set request type to POST for POSTing a tweet
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');

    // Enable verbose for debugging only
    curl_setopt($ch, CURLOPT_VERBOSE, false);

    // Return the transfer in a string
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    // Set URL of cURL request
    curl_setopt($ch, CURLOPT_URL, rawurldecode($statusURL));

    // Do a POST request (HTML POST)
    curl_setopt($ch, CURLOPT_POST, 1);

    // Set data
    curl_setopt($ch, CURLOPT_POSTFIELDS, 'status='.$messageData);

    // Execute cURL and store result
    $output = curl_exec($ch);

    // Close cURL
    curl_close($ch);

    if ($output) {
        $output = json_decode($output);
    }

    if ($this->hasErrors($output, false))
    {
        return false;
    }


   return true;
}
?>

Hope it'll help.

like image 30
Guillaume Renoult Avatar answered Oct 20 '22 16:10

Guillaume Renoult