Pankas
Pankas

Reputation: 137

Twitter OAUTh and PHP

I'm trying to obtain a request token from Twitter API in order to log in user with his twitter account and I keep getting a 401 response with "Failed to validate oauth signature and token". I'm doing this over PHP. I looked for similar questions but apparently I'm the only one crazy enough to do it from scratch without a library.

In their API documentation they talk about "percent encode" the values sent in the authorization header, I'm doing so with the urlencode() function, not sure if it's right.

To calculate the signature I use hash_hmac( 'SHA1', $signParameters, $hashKey), also not sure if it is the right one to use.

This is the request that gets generated, through cURL:

POST /oauth/request_token HTTP/1.1
Host: api.twitter.com
Accept: */*
Authorization: OAuth oauth_callback="http%3A%2F%2Fwww.soytumascota.com%2Ftwitter%2Fuser.php", oauth_consumer_key="MY_APP_KEY", oauth_nonce="0dde25902bde5f3b280f58ea642047cf", oauth_signature_method="HMAC_SHA1", oauth_timestamp="1334697987", oauth_version="1.0", oauth_signature="8313277875f20cd8a8631966a2ba273a5d13aeda"
Content-Length: 0
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue

I would really appreciate any help you can give, thank you.

EDIT: Here's the code i've written so far.

<?php
DEFINE( 'CONSUMER_KEY', 'MY_APP_KEY' );
DEFINE( 'CONSUMER_SECRET', 'MY_APP_SECRET' );

$url = 'https://api.twitter.com/oauth/request_token';

//setting OAuth parameters
$Oauth = Array();
$Oauth['oauth_callback'] = 'http://www.soytumascota.com/twitter/user.php';
$Oauth['oauth_consumer_key'] = CONSUMER_KEY;
$Oauth['oauth_nonce'] = md5( $Oauth['oauth_callback'] . CONSUMER_KEY . time() );
$Oauth['oauth_signature_method'] = 'HMAC_SHA1';
$Oauth['oauth_timestamp'] = (string) time();
$Oauth['oauth_version'] = '1.0';

//signature and authorization header are calculated inside functions
$Oauth['oauth_signature'] = calculateSignature( 'POST', $url, $Oauth );
$authorization = getAuthorizationHeader( $Oauth ); 
ksort( $Oauth );

//setting and sending request using cURL
$curl_session = curl_init( $url );
curl_setopt( $curl_session, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $curl_session, CURLOPT_POST, true );
curl_setopt( $curl_session, CURLINFO_HEADER_OUT, true );
curl_setopt( $curl_session, CURLOPT_HTTPHEADER, Array( 'Authorization: ' . $authorization ) );

$result = curl_exec( $curl_session );


function getAuthorizationHeader( $parameters )
{
    $authorization = 'OAuth ';
    $j = count( $parameters );
    foreach( $parameters as $key => $val )
    {
        $authorization .= $key . '="' . urlencode( $val ) . '"';

        if( $j-- > 1 )
        {
            $authorization .= ', ';
        }   
    }

    return $authorization;
}

function calculateSignature( $method, $url, $parameters, $accessToken = '' )
{   
    foreach( $parameters as $key => $val )
    {
        $foo = urlencode( $key );
        unset( $parameters[$key] );

        $parameters[$foo] = urlencode( $val );      
    }

    ksort( $parameters );   

    $signBase = '';
    $j = count( $parameters );
    foreach( $parameters as $key => $val )
    {
        $signBase .= "{$key}={$val}";

        if( $j-- > 1 )
        {
            $signBase .= '&';
        }
    }

    $signBase = strtoupper( $method ) . '&' . urlencode( $url ) . '&' . urlencode( $signBase );

    $signKey = urlencode( CONSUMER_SECRET ) . '&' . urlencode( $accessToken );

    $signature = hash_hmac( 'SHA1', $signParameters, $hashKey);

    return $signature;
}

Upvotes: 2

Views: 1809

Answers (1)

aribon
aribon

Reputation: 31

First, 'oauth_signature_method' must be 'HMAC-SHA1' (or nothing will work).

About "percent encode", they say in their API documentation https://dev.twitter.com/docs/auth/percent-encoding-parameters they require strings to be encoded according to RFC 3986 so you should use rawurlencode instead of urlencode.

To calculate the signature they say here https://dev.twitter.com/docs/auth/creating-signature that you have to use HMAC-SHA1 hashing algorithm and convert the raw output to base64, something like:

$signature = base64_encode(hash_hmac( 'SHA1', $signBase, $signKey, true));

And finally you may need to add

//no content
curl_setopt( $curl_session, CURLOPT_POSTFIELDS, '');
//no verify certs
curl_setopt( $curl_session, CURLOPT_SSL_VERIFYPEER, FALSE);

to the curl options to get the request working.

Upvotes: 3

Related Questions