Need Help Debugging Oauth Signature Logic ( Using HTTP Headers and Perl )


#1

Ok, so here is my issue. I have been using a perl ( 5.8.8 ) application to pull atom request data in version 1.0 of the twitter API. Well, that is going away so it’s time for an update. I have followed the steps listed here https://dev.twitter.com/docs/auth/authorizing-request for authorization requests and here https://dev.twitter.com/docs/auth/creating-signature for creating the base64 encoded encryption signature. After reviewing these multiple times I have caught the obvious errors that stuck out, but there is something that remains still. My best guess is that this is related to how I am creating the encrypted oauth signature.

Thanks in advance for any help with this issue. Let me know if you need more information and I’ll respond as quick as I get the email ").

Here is some debugging output for oauth information

Oauth parameter string

count=10&oauth_consumer_key=MY_CONSUMER_KEY&oauth_nonce=LmSjWAmPwPSX7z3e5s0Le5GZw9nuVvIR&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1366298299&oauth_token=MY_OAUTH_TOKEN&oauth_version=1.0&screen_name=MultiSvc

Oauth base siganture

GET&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fuser_timeline.json&count%3D10%26oauth_consumer_key%3DMY_CONSUMER_KEY%26oauth_nonce%3DLmSjWAmPwPSX7z3e5s0Le5GZw9nuVvIR%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1366298299%26oauth_token%3DMY_OAUTH_TOKEN%26oauth_version%3D1.0%26screen_name%3DMultiSvc

Oauth Auth HTTP Request Header

OAuth oauth_consumer_key=“MY_CONSUMER_KEY”, oauth_nonce=“LmSjWAmPwPSX7z3e5s0Le5GZw9nuVvIR”, oauth_signature=“F3%2FjpYc79ziI6kaQuh47ot23GfI”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1366298299”, oauth_token=“MY_OAUTH_TOKEN”, oauth_version=“1.0”

Error Response returned from Twitter

Request to twitter failed 401 Unauthorized

I assume this is a generic error that my oauth authorization has failed.

Below is a snippet of code from my program and the information that is useful to it. My hope with the snippet is that someone could reasonably take this chunk and swap some data in and test a request themselves.

#!/usr/bin/perl -T use strict; use warnings;

use Digest::SHA qw();
use JSON qw();
use HTTP::Request qw();
use LWP 5.64;
use URI::Escape qw(uri_escape_utf8);

my %config = (
feed => {
url => ‘https://api.twitter.com/1.1/statuses/user_timeline.json’,
param => ‘screen_name=MultiSvc&count=10’,
},
oauth => {
# oauth_nonce - created later
oauth_consumer_key => CONSUMER_KEY,
# oauth_signature - created later
oauth_signature_method => ‘HMAC-SHA1’,
# oauth_timestamp - created later,
oauth_token => ACCESS_TOKEN,
oauth_version => ‘1.0’,
}
);

##############################################################################

get_tweets()

Arguments:

$this - hash ref container for program objects

Returns:

array reference of tweet data

Purpose:

Connect to twitter via HTTP Request ( LWP User Agent ) and return tweet

information based on given paramaters in the URL

sub get_tweets {
my $this = shift;

# add oauth parameters to seperate hash reference
my $oauth_sig_params_ref = $this->{config}{oauth};

# split up or parameters and then add them to the new oauth param hash
my @our_parameters = split( '&', 'screen_name=MultiSvc&count=10' );
foreach my $single_parameter ( @our_parameters ) {
    my @split_parameter = split( '=', $single_parameter );
    $oauth_sig_params_ref->{$split_parameter[0]} = $split_parameter[1];
}

# create unique id ( nonce )
$this->{config}{oauth}{oauth_nonce} = join'', map +(0..9,'a'..'z','A'..'Z')[rand(10+26*2)], 1..32;

# get our epoch timestamp
$this->{config}{oauth}{oauth_timestamp} = time;

my @combined_parameters;
foreach my $key ( sort keys % { $oauth_sig_params_ref } ) {
    my ( $encoded_key, $encoded_value ) = ( uri_escape_utf8($key), uri_escape_utf8( $oauth_sig_params_ref->{$key} ) );
    push( @combined_parameters, $encoded_key . '=' . $encoded_value );
}

my $oauth_parameter_string = join( '&', @combined_parameters );
if ( $this->{config}{options}{debug} ) { log_it( 'Oauth parameter string >' . $oauth_parameter_string . '<' ); }

my $encoded_url = uri_escape_utf8($this->{config}{feed}{url});
my $oauth_base_signature = 'GET&' . $encoded_url . '&' . uri_escape_utf8($oauth_parameter_string);
if ( $this->{config}{options}{debug} ) { log_it( 'Oauth base siganture >' . $oauth_base_signature. '<' ); }

my $oauth_signing_key = uri_escape_utf8(CONSUMER_SECRET) . '&' . uri_escape_utf8(ACCESS_TOKEN_SECRET);

# encrypt with sha1 and then base64 encode the result
my $digest = Digest::SHA->sha1_base64( $oauth_base_signature, $oauth_signing_key );
$this->{config}{oauth}{oauth_signature} = uri_escape_utf8( $digest );

my $oauth_header_value = 'OAuth oauth_consumer_key="'     . $this->{config}{oauth}{oauth_consumer_key}     . '", ' .
                               'oauth_nonce="'            . $this->{config}{oauth}{oauth_nonce}            . '", ' .
                               'oauth_signature="'        . $this->{config}{oauth}{oauth_signature}        . '", ' .
                               'oauth_signature_method="' . $this->{config}{oauth}{oauth_signature_method} . '", ' .
                               'oauth_timestamp="'        . $this->{config}{oauth}{oauth_timestamp}        . '", ' .
                               'oauth_token="'            . $this->{config}{oauth}{oauth_token}            . '", ' .
                               'oauth_version="'          . $this->{config}{oauth}{oauth_version}          . '"'
;
if ( $this->{config}{options}{debug} ) { log_it( 'Oauth Auth Header >' . $oauth_header_value . '<' ) }

my $url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=MultiSvc&count=10';
my $request = HTTP::Request->new( 'GET', $url );
$request->header( 'Authorization' => $oauth_header_value );

$ua = LWP::UserAgent->new();
$ua->timeout(15);

my $response = $ua->request( $request );

if ( !$response->is_success ) {
    report_error( 'Request to twitter failed >' . $response->status_line . '<' );
    die $response->status_line;
}

my $json = JSON->new;
my $tweet_ref = $json->decode( $response->content ); # array reference returned

return $tweet_ref;

}


#2

You’re not using Digest::HMAC. http://search.cpan.org/~gaas/Digest-HMAC-1.03/lib/Digest/HMAC.pm


#3

The Digest::Sha will work as well with a 64bit hmac, but you have to request the token before you can get the response information:

http://www.cubrid.org/blog/dev-platform/dancing-with-oauth-understanding-how-authorization-works/