I have written a class (so I could learn how OAuth works). It is working fine; I can retrieve the access token with the class. But when I try to post an update, it says that I'm not authenticated! What am I doing wrong here?
// By Kevin Jacobs
class OAuth {
private $url = null;
private $debug = false;
private $method = 'POST';
private $oauthData = array();
private $data = array();
private $token = array('key' => '', 'secret' => '');
private $consumer = array('key' => '', 'secret' => '');
/**
* Encode a string in such a way you can use it for OAuth.
* @param string $oauthData Data to encode
* @return string $encData Encoded data
*/
public static function encode($oauthData) {
if (is_string($oauthData)) {
return str_ireplace(
array('+', '%7E'),
array(' ', '~'),
rawurlencode($oauthData)
);
} else {
return '';
}
}
/**
* Generates a relative unique random string of a certain length.
* @param int $length Length of the string
* @return string $strRand A random string
*/
public static function generateString($length = 40) {
// Only strong cryptographic strings are allowed
while (!isset($bStrong) || $bStrong === false) {
$bytes = openssl_random_pseudo_bytes(floor($length / 2), $bStrong);
}
$strRand = bin2hex($bytes);
return sha1($strRand);
}
/**
* Generate a token pair (key and secret).
* @return array $tokenPair
*/
public static function generateTokenPair() {
$tokenPair = array();
$tokenPair['key'] = self::generateString();
$tokenPair['secret'] = self::generateString();
return $tokenPair;
}
/**
* Set the callback URL.
* @param string $callbackURL
*/
public function setCallback($callback = null) {
if ($callback === null) {
$callback = 'http';
if ($_SERVER['SERVER_PORT'] == 443) $callback .= 's';
$callback .= '://' . $_SERVER['SERVER_NAME'];
if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
$callback .= ':' . $_SERVER['SERVER_PORT'];
}
$callback .= $_SERVER['REQUEST_URI'];
}
$this->oauthData['oauth_callback'] = $callback;
}
/**
* Get the callback URL.
* @return string $callbackURL
*/
public function getCallback() {
return $this->oauthData['oauth_callback'];
}
/**
* Generate the nonce.
* @return string $nonce
*/
public function setNonce() {
$this->oauthData['oauth_nonce'] = md5(self::generateString(20) . mktime());
return $this->oauthData['oauth_nonce'];
}
/**
* Set the timestamp.
* @return int $timestamp
*/
public function setTimestamp() {
$this->oauthData['oauth_timestamp'] = mktime();
return $this->oauthData['oauth_timestamp'];
}
/**
* Set the OAuth version.
* @param string Version
*/
public function setVersion($version = '1.0') {
$this->oauthData['oauth_version'] = $version;
}
/**
* Set the HTTP method.
* @param string Method
*/
public function setMethod($method = 'POST') {
$this->method = trim(strtoupper($method));
}
/**
* Get the HTTP method.
* @return string Method
*/
public function getMethod() {
return $this->method;
}
/**
* Get the URL to call.
* @return string URL
*/
public function getURL() {
return $this->url;
}
/**
* Set the URL to call.
* @param string URL
*/
public function setURL($URL) {
$this->url = $URL;
}
/**
* Get the token key and secret
* @return array $token Containing token key and secret
*/
public function getToken() {
return $this->token;
}
/**
* Set the token
* @param string $tokenKey Token key
* @param string $tokenSecret Token secret
*/
public function setToken($tokenKey, $tokenSecret = null) {
$this->token['key'] = $tokenKey;
$this->token['secret'] = $tokenSecret;
$this->oauthData['oauth_token'] = $tokenKey;
$this->oauthData['oauth_token_secret'] = $tokenSecret;
}
/**
* Get the consumer
* @return array $consumer Containing consumer key and secret
*/
public function getConsumer() {
return $this->consumer;
}
/**
* Set the consumer
* @param string $consumerKey Consumer key
* @param string $consumerSecret Consumer secret
*/
public function setConsumer($consumerKey, $consumerSecret) {
$this->oauthData['oauth_consumer_key'] = $consumerKey;
$this->consumer['key'] = $consumerKey;
$this->consumer['secret'] = $consumerSecret;
}
/**
* Generate the signature.
* @return array Signature properties
*/
public function setSignature() {
// Set the signature method
$this->oauthData['oauth_signature_method'] = 'HMAC-SHA1';
// First, sort the OAuth data
$oauthData = $this->oauthData;
ksort($oauthData);
// Now combine them in a string
$query = http_build_query($oauthData);
// Make it URL proof
$query = rawurlencode($query);
// Fetch the method and URL
$method = $this->getMethod();
$url = $this->getURL();
// Make the URL URL proof
$url = rawurlencode($url);
// Now bind everything together
$baseString = $method . '&' . $url . '&' . $query;
// Retrieve the key
$consumer = $this->getConsumer();
$token = $this->getToken();
$key = self::encode($consumer['secret']) . '&' . self::encode($token['secret']);
// Encrypt the base string
$signature = hash_hmac('SHA1', $baseString, $key, true);
// And make it URL proof using base64_encode
$signature = base64_encode($signature);
$this->oauthData['oauth_signature'] = $signature;
}
public function setVerifier($verifier) {
$this->oauthData['oauth_verifier'] = $verifier;
}
public function debugOn() {
$this->debug = true;
}
public function debugOff() {
$this->debug = false;
}
public function setData($data) {
$this->data = $data;
}
public function call($url) {
$method = $this->getMethod();
$this->setURL($url);
$this->setNonce();
$this->setTimestamp();
$this->setSignature();
$oauthData = $this->oauthData;
$data = $this->data;
$data = array_merge($data, $oauthData);
if ($method == 'GET') {
$url = explode('#', $url);
$url = reset($url);
if (strpos($url, '?') !== false) {
$binder = '&';
} else {
$binder = '?';
}
$url .= $binder . http_build_query($oauthData);
}
$ch = curl_init();
if (!empty($headers)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if ($method == 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $oauthData);
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_ENCODING, '');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
}
$x = new OAuth();
$x->debugOn();
$x->setVersion('1.0');
$x->setConsumer('consumerToken', 'consumerSecret');
$x->setToken('accessToken', 'accessSecret');
$x->setMethod('POST');
$x->setData(array('status' => 'Hello World!'));
echo $x->call('http://api.twitter.com/1/statuses/update.json', true);
The following code is working (retrieving the access token):
$x = new OAuth();
$x->debugOn();
$x->setVersion('1.0');
$x->setConsumer('consumerToken', 'consumerSecret');
$x->setToken('accessToken', 'accessSecret');
$x->setMethod('POST');
if (isset($_GET['oauth_verifier']) && isset($_GET['oauth_token'])) {
// Request token -> Access token
$verifier = $_GET['oauth_verifier'];
$token = $_GET['oauth_token'];
$x->setVerifier($verifier);
$x->setToken($token);
$x->setMethod('GET');
$result = $x->call('https://api.twitter.com/oauth/access_token', true);
parse_str($result);
echo 'Access token: ' . $oauth_token . '<br />';
echo 'Access token secret: ' . $oauth_token_secret;
} else {
// Request token
$x->setCallback();
$x->setMethod('GET');
$result = $x->call('https://api.twitter.com/oauth/request_token');
parse_str($result);
header('Location: http://api.twitter.com/oauth/authorize?oauth_token=' . $oauth_token);
}