1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
use strict;
use warnings;
use Test::More;
use Data::Dumper 'Dumper';
use Digest::SHA 'hmac_sha1_base64';
use JSON::PP 'decode_json';
use URI;
use URI::Escape 'uri_escape_utf8', 'uri_unescape';
use WWW::OAuth;
use WWW::OAuth::Util 'form_urldecode', 'form_urlencode', 'oauth_request';
my $api_key = $ENV{TWITTER_API_KEY};
my $api_secret = $ENV{TWITTER_API_SECRET};
my $token = $ENV{TWITTER_ACCESS_TOKEN};
my $token_secret = $ENV{TWITTER_ACCESS_SECRET};
my $oauth_base_url = 'https://api.twitter.com/oauth/';
my $api_base_url = 'https://api.twitter.com/1.1/';
my $test_online;
if ($ENV{AUTHOR_TESTING} and defined $api_key and defined $api_secret and defined $token and defined $token_secret) {
note 'Running online test for Twitter OAuth 1.0';
$test_online = 1;
require HTTP::Tiny;
HTTP::Tiny->VERSION('0.014');
} else {
note 'Running offline test for Twitter OAuth 1.0; set AUTHOR_TESTING and TWITTER_API_KEY/TWITTER_API_SECRET/TWITTER_ACCESS_TOKEN/TWITTER_ACCESS_SECRET for online test';
$api_key = 'foo';
$api_secret = 'bar';
$token = 'baz';
$token_secret = 'ban';
}
# OAuth token request
my $oauth_request_url = $oauth_base_url . 'request_token';
my $oauth_request = _request(POST => $oauth_request_url);
my $auth = WWW::OAuth->new(client_id => $api_key, client_secret => $api_secret);
my $auth_header = $auth->authorization_header($oauth_request, { oauth_callback => 'oob' });
like $auth_header, qr/^OAuth oauth_/, 'Formed Authorization header';
$oauth_request->header(Authorization => $auth_header);
is $auth_header, $oauth_request->header('Authorization'), 'Authorization header has been set';
my $oauth_params = _parse_oauth_header($auth_header);
is $oauth_params->{oauth_consumer_key}, $api_key, 'oauth_consumer_key is set to API key';
ok defined($oauth_params->{oauth_nonce}), 'oauth_nonce is set';
is $oauth_params->{oauth_signature_method}, 'HMAC-SHA1', 'oauth_signature_method is set to HMAC-SHA1';
ok defined($oauth_params->{oauth_timestamp}), 'oauth_timestamp is set';
is $oauth_params->{oauth_version}, '1.0', 'oauth_version is set to 1.0';
ok defined($oauth_params->{oauth_signature}), 'oauth_signature is set';
is $oauth_params->{oauth_callback}, 'oob', 'oauth_callback is set to "oob"';
ok !defined($oauth_params->{oauth_token}), 'oauth_token is not set';
my $test_signature = _test_signature('POST', $oauth_request_url, $oauth_params, $api_secret);
is $test_signature, $oauth_params->{oauth_signature}, 'signature formed correctly';
my $response;
if ($test_online) {
my $res = $oauth_request->request_with(HTTP::Tiny->new);
ok $res->{success}, 'OAuth request successful' or diag Dumper $res;
$response = $res->{content};
} else {
$response = q{oauth_token=aaaaaaaaaaaaaaaaaaaaaaaaaaa&oauth_token_secret=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&oauth_callback_confirmed=true};
}
my %response_params = @{form_urldecode($response)};
is $response_params{oauth_callback_confirmed}, 'true', 'OAuth callback was confirmed';
ok defined(my $request_token = $response_params{oauth_token}), 'got request token';
ok defined(my $request_secret = $response_params{oauth_token_secret}), 'got request secret';
# Verify credentials request
my $verify_url = $api_base_url . 'account/verify_credentials.json';
my $verify_request = _request(GET => $verify_url);
$auth->token($token);
$auth->token_secret($token_secret);
$auth->authenticate($verify_request, { realm => 'foo' });
$auth_header = $verify_request->header('Authorization');
ok defined $auth_header, 'Authorization header has been set';
$oauth_params = _parse_oauth_header($auth_header);
is $oauth_params->{oauth_consumer_key}, $api_key, 'oauth_consumer_key is set to API key';
ok defined($oauth_params->{oauth_nonce}), 'oauth_nonce is set';
is $oauth_params->{oauth_signature_method}, 'HMAC-SHA1', 'oauth_signature_method is set to HMAC-SHA1';
ok defined($oauth_params->{oauth_timestamp}), 'oauth_timestamp is set';
is $oauth_params->{oauth_version}, '1.0', 'oauth_version is set to 1.0';
ok defined($oauth_params->{oauth_signature}), 'oauth_signature is set';
is $oauth_params->{oauth_token}, $token, 'oauth_token is set to API access token';
is $oauth_params->{realm}, 'foo', 'realm is set';
$test_signature = _test_signature('GET', $verify_url, $oauth_params, $api_secret, $token_secret);
is $test_signature, $oauth_params->{oauth_signature}, 'signature formed correctly';
if ($test_online) {
my $res = $verify_request->request_with(HTTP::Tiny->new);
ok $res->{success}, 'OAuth request successful' or diag Dumper $res;
$response = $res->{content};
} else {
$response = <<'JSON_RESPONSE';
{"id":36885345,"id_str":"36885345","name":"Grinnz","screen_name":"TheGrinnz","location":"CT","description":"nerrrrrd","url":"http:\/\/t.co\/IomLjiiycH","entities":{"url":{"urls":[{"url":"http:\/\/t.co\/IomLjiiycH","expanded_url":"http:\/\/grinnz.net","display_url":"grinnz.net","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":73,"friends_count":74,"listed_count":3,"created_at":"Fri May 01 04:45:52 +0000 2009","favourites_count":0,"utc_offset":-18000,"time_zone":"Quito","geo_enabled":true,"verified":false,"statuses_count":365,"lang":"en","status":{"created_at":"Thu Aug 14 03:05:35 +0000 2014","id":499753673501990912,"id_str":"499753673501990912","text":"@GermanFatass vpssd down, at least one irc server still up","source":"\u003ca href=\"http:\/\/twitter.com\" rel=\"nofollow\"\u003eTwitter Web Client\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":499752682178224128,"in_reply_to_status_id_str":"499752682178224128","in_reply_to_user_id":66307969,"in_reply_to_user_id_str":"66307969","in_reply_to_screen_name":"GermanFatass","geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[],"symbols":[],"user_mentions":[{"screen_name":"GermanFatass","name":"John ","id":66307969,"id_str":"66307969","indices":[0,13]}],"urls":[]},"favorited":false,"retweeted":false,"lang":"en"},"contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"C6E2EE","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme2\/bg.gif","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme2\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/221712898\/tux_redhat80_normal.png","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/221712898\/tux_redhat80_normal.png","profile_link_color":"1F98C7","profile_sidebar_border_color":"C6E2EE","profile_sidebar_fill_color":"DAECF4","profile_text_color":"663B12","profile_use_background_image":true,"has_extended_profile":false,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false}
JSON_RESPONSE
}
my $response_data = decode_json $response;
ok defined($response_data->{id_str}), 'Twitter ID returned in response';
sub _request {
my ($method, $url, $params) = @_;
my %req = (method => $method, url => $url);
if (defined $params) {
if ($method eq 'GET' or $method eq 'HEAD') {
my $uri = URI->new($url);
$uri->query_form($params);
$req{url} = $uri->as_string;
} else {
$req{content} = form_urlencode($params);
$req{headers}{'content-type'} = 'application/x-www-form-urlencoded';
}
}
return oauth_request(Basic => \%req);
}
sub _parse_oauth_header {
my $auth_header = shift;
return {} unless defined $auth_header;
my %oauth_params;
while ($auth_header =~ m/(\w+)="(.+?)"/g) {
$oauth_params{$1} = uri_unescape $2;
}
return \%oauth_params;
}
sub _test_signature {
my ($method, $request_url, $oauth_params, $client_secret, $token_secret) = @_;
my $params_str = join '&', map { $_ . '=' . uri_escape_utf8($oauth_params->{$_}) } grep { defined $oauth_params->{$_} }
qw(oauth_callback oauth_consumer_key oauth_nonce oauth_signature_method oauth_timestamp oauth_token oauth_version);
my $base_str = uc($method) . '&' . uri_escape_utf8($request_url) . '&' . uri_escape_utf8($params_str);
my $signing_key = uri_escape_utf8($client_secret) . '&';
$signing_key .= uri_escape_utf8($token_secret) if defined $token_secret;
my $test_signature = hmac_sha1_base64($base_str, $signing_key);
$test_signature .= '='x(4 - length($test_signature) % 4) if length($test_signature) % 4;
return $test_signature;
}
done_testing;
|