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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
package Net::Twitter::Role::AppAuth;
$Net::Twitter::Role::AppAuth::VERSION = '4.01043';
use Moose::Role;
use Carp::Clan qw/^(?:Net::Twitter|Moose|Class::MOP)/;
use HTTP::Request::Common qw/POST/;
use Net::Twitter::Types;
requires qw/_add_authorization_header ua from_json/;
use namespace::autoclean;
# flatten oauth_urls with defaults
around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
my $args = $class->$orig(@_);
my $oauth_urls = delete $args->{oauth_urls} || {
request_token_url => "https://api.twitter.com/oauth2/token",
invalidate_token_url => "https://api.twitter.com/oauth2/invalidate_token",
};
return { %$oauth_urls, %$args };
};
has [ qw/consumer_key consumer_secret/ ] => (
isa => 'Str',
is => 'ro',
required => 1,
);
# url attributes
has [ qw/request_token_url invalidate_token_url/ ] => (
isa => 'Net::Twitter::Types::URI',
is => 'ro',
required => 1,
coerce => 1,
);
has access_token => (
isa => 'Str',
is => 'rw',
clearer => "clear_access_token",
predicate => "authorized",
);
sub _add_consumer_auth_header {
my ( $self, $req ) = @_;
$req->headers->authorization_basic(
$self->consumer_key, $self->consumer_secret);
}
sub request_access_token {
my $self = shift;
my $req = POST($self->request_token_url,
'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
Content => { grant_type => 'client_credentials' },
);
$self->_add_consumer_auth_header($req);
my $res = $self->ua->request($req);
croak "request_token failed: ${ \$res->code }: ${ \$res->message }"
unless $res->is_success;
my $r = $self->from_json($res->decoded_content);
croak "unexpected token type: $$r{token_type}" unless $$r{token_type} eq 'bearer';
return $self->access_token($$r{access_token});
}
sub invalidate_token {
my $self = shift;
croak "no access_token" unless $self->authorized;
my $req = POST($self->invalidate_token_url,
'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
Content => join '=', access_token => $self->access_token,
);
$self->_add_consumer_auth_header($req);
my $res = $self->ua->request($req);
croak "invalidate_token failed: ${ \$res->code }: ${ \$res->message }"
unless $res->is_success;
$self->clear_access_token;
}
around _prepare_request => sub {
my $orig = shift;
my $self = shift;
my ($http_method, $uri, $args, $authenticate) = @_;
delete $args->{source};
$self->$orig(@_);
};
override _add_authorization_header => sub {
my ( $self, $msg ) = @_;
return unless $self->authorized;
$msg->header(authorization => join ' ', Bearer => $self->access_token);
};
1;
__END__
=encoding utf-8
=for stopwords
=head1 NAME
Net::Twitter::Role::AppAuth - OAuth2 Application Only Authentication
=head1 VERSION
version 4.01043
=head1 SYNOPSIS
use Net::Twitter;
my $nt = Net::Twitter->new(
traits => ['API::RESTv1_1', 'AppAuth'],
consumer_key => "YOUR-CONSUMER-KEY",
consumer_secret => "YOUR-CONSUMER-SECRET",
);
$nt->request_token;
my $tweets = $nt->user_timeline({ screen_name => 'Twitter' });
=head1 DESCRIPTION
Net::Twitter::Role::OAuth is a Net::Twitter role that provides OAuth
authentication instead of the default Basic Authentication.
Note that this client only works with APIs that are compatible to OAuth authentication.
=head1 METHODS
=over 4
=item authorized
True if the client has an access_token. This does not check the validity of the
access token, so requests may fail if it is invalid.
=item request_access_token
Request an access token. Returns the token as well as saving it in the object.
=item access_token
Get or set the access token.
=item invalidate_token
Invalidates and clears the access_token.
Note: There seems to be a Twitter bug preventing this from working---perhaps a
documentation bug. E.g., see: L<https://twittercommunity.com/t/revoke-an-access-token-programmatically-always-getting-a-403-forbidden/1902>
=back
=cut
|