File Coverage

blib/lib/WWW/OAuth.pm
Criterion Covered Total %
statement 84 93 90.3
branch 16 30 53.3
condition 4 12 33.3
subroutine 17 19 89.4
pod 2 2 100.0
total 123 156 78.8


line stmt bran cond sub pod time code
1             package WWW::OAuth;
2              
3 1     1   45536 use strict;
  1         1  
  1         28  
4 1     1   3 use warnings;
  1         2  
  1         32  
5 1         10 use Class::Tiny::Chained qw(client_id client_secret token token_secret), {
6             signature_method => 'HMAC-SHA1',
7 1     1   486 };
  1         3255  
8              
9 1     1   602 use Carp 'croak';
  1         1  
  1         49  
10 1     1   4 use Digest::SHA 'hmac_sha1';
  1         1  
  1         48  
11 1     1   5 use List::Util 'all', 'pairs', 'pairgrep';
  1         1  
  1         85  
12 1     1   5 use MIME::Base64 'encode_base64';
  1         1  
  1         41  
13 1     1   4 use Scalar::Util 'blessed';
  1         2  
  1         70  
14 1     1   4 use URI;
  1         1  
  1         20  
15 1     1   10 use URI::Escape 'uri_escape_utf8';
  1         2  
  1         43  
16 1     1   388 use WWW::OAuth::Util 'oauth_request';
  1         2  
  1         872  
17              
18             our $VERSION = '0.005';
19              
20             my %signature_methods = (
21             'PLAINTEXT' => '_signature_plaintext',
22             'HMAC-SHA1' => '_signature_hmac_sha1',
23             'RSA-SHA1' => '_signature_rsa_sha1',
24             );
25              
26             sub authenticate {
27 1     1 1 90 my $self = shift;
28 1 50       6 my @req_args = ref $_[0] ? shift() : (shift, shift);
29 1         5 my $req = oauth_request(@req_args);
30            
31 1         16 my $auth_header = $self->authorization_header($req, @_);
32            
33 1         5 $req->header(Authorization => $auth_header);
34 1         4 return $req;
35             }
36              
37             sub authorization_header {
38 2     2 1 161 my $self = shift;
39 2 50       8 my @req_args = ref $_[0] ? shift() : (shift, shift);
40 2         6 my $req = oauth_request(@req_args);
41 2         22 my $extra_params = shift;
42            
43 2         48 my ($client_id, $client_secret, $token, $token_secret, $signature_method) =
44             ($self->client_id, $self->client_secret, $self->token, $self->token_secret, $self->signature_method);
45            
46 2 50 33     230 croak 'Client ID and secret are required to generate authorization header'
47             unless defined $client_id and defined $client_secret;
48            
49 2 50       6 croak 'RSA-SHA1 signature method requires a coderef or an object with a "sign" method'
50             if $signature_method eq 'RSA-SHA1';
51 2 50 33     22 $signature_method = 'RSA-SHA1' if (blessed $signature_method and $signature_method->can('sign'))
      33        
      33        
52             or (!blessed $signature_method and ref $signature_method eq 'CODE');
53 2         4 my $sign = $signature_methods{$signature_method};
54 2 50       7 croak "Unknown signature method $signature_method" unless defined $sign;
55            
56 2         6 my %oauth_params = (
57             oauth_consumer_key => $client_id,
58             oauth_nonce => _nonce(),
59             oauth_signature_method => $signature_method,
60             oauth_timestamp => time,
61             oauth_version => '1.0',
62             );
63 2 100       7 $oauth_params{oauth_token} = $token if defined $token;
64            
65             # Extra parameters passed to authenticate()
66 2 100       5 if (defined $extra_params) {
67 1 50       4 croak 'OAuth parameters must be specified as a hashref' unless ref $extra_params eq 'HASH';
68 1 50   1   13 croak 'OAuth parameters must all begin with "oauth_"' unless all { m/^oauth_/ } keys %$extra_params;
  1         6  
69 1         8 %oauth_params = (%oauth_params, %$extra_params);
70             }
71            
72             # This parameter is not allowed when creating the signature
73 2         3 delete $oauth_params{oauth_signature};
74            
75 2         10 $oauth_params{oauth_signature} = $self->$sign($req, \%oauth_params, $client_secret, $token_secret);
76            
77 2         12 my $auth_str = join ', ', map { $_ . '="' . uri_escape_utf8($oauth_params{$_}) . '"' } sort keys %oauth_params;
  14         121  
78 2         34 return "OAuth $auth_str";
79             }
80              
81             sub _nonce {
82 2     2   5 my $str = encode_base64 join('', map { chr int rand 256 } 1..32), '';
  64         138  
83 2         17 $str =~ s/[^a-zA-Z0-9]//g;
84 2         16 return $str;
85             }
86              
87             sub _signature_plaintext {
88 0     0   0 my ($self, $req, $oauth_params, $client_secret, $token_secret) = @_;
89 0 0       0 $token_secret = '' unless defined $token_secret;
90 0         0 return uri_escape_utf8($client_secret) . '&' . uri_escape_utf8($token_secret);
91             }
92              
93             sub _signature_hmac_sha1 {
94 2     2   4 my ($self, $req, $oauth_params, $client_secret, $token_secret) = @_;
95 2 100       5 $token_secret = '' unless defined $token_secret;
96 2         6 my $base_str = _signature_base_string($req, $oauth_params);
97 2         155 my $signing_key = uri_escape_utf8($client_secret) . '&' . uri_escape_utf8($token_secret);
98 2         72 return encode_base64(hmac_sha1($base_str, $signing_key), '');
99             }
100              
101             sub _signature_rsa_sha1 {
102 0     0   0 my ($self, $req, $oauth_params) = @_;
103 0         0 my $base_str = _signature_base_string($req, $oauth_params);
104 0         0 my $signer = $self->signature_method;
105 0 0       0 return $signer->sign($base_str) if blessed $signer; # object
106 0         0 return $signer->($base_str); # code ref
107             }
108              
109             sub _signature_base_string {
110 2     2   2 my ($req, $oauth_params) = @_;
111            
112 2         2 my @all_params = (@{$req->query_pairs}, %$oauth_params);
  2         8  
113 2 50       7379 push @all_params, @{$req->body_pairs} if $req->content_is_form;
  0         0  
114 2         5 my @pairs = pairs map { uri_escape_utf8 $_ } @all_params;
  24         206  
115 2 50       41 @pairs = sort { ($a->[0] cmp $b->[0]) or ($a->[1] cmp $b->[1]) } @pairs;
  21         44  
116 2         5 my $params_str = join '&', map { $_->[0] . '=' . $_->[1] } @pairs;
  12         24  
117            
118 2         45 my $base_url = URI->new($req->url);
119 2         106 $base_url->query(undef);
120 2         94 $base_url->fragment(undef);
121 2         57 return uc($req->method) . '&' . uri_escape_utf8($base_url) . '&' . uri_escape_utf8($params_str);
122             }
123              
124             1;
125              
126             =head1 NAME
127              
128             WWW::OAuth - Portable OAuth 1.0 authentication
129              
130             =head1 SYNOPSIS
131              
132             use WWW::OAuth;
133            
134             my $oauth = WWW::OAuth->new(
135             client_id => $client_id,
136             client_secret => $client_secret,
137             token => $token,
138             token_secret => $token_secret,
139             );
140            
141             # Just retrieve authorization header
142             my $auth_header = $oauth->authorization_header($http_request, { oauth_callback => $url });
143             $http_request->header(Authorization => $auth_header);
144            
145             # HTTP::Tiny
146             use HTTP::Tiny;
147             my $res = $oauth->authenticate(Basic => { method => 'GET', url => $url })
148             ->request_with(HTTP::Tiny->new);
149            
150             # HTTP::Request
151             use HTTP::Request::Common;
152             use LWP::UserAgent;
153             my $res = $oauth->authenticate(GET $url)->request_with(LWP::UserAgent->new);
154            
155             # Mojo::Message::Request
156             use Mojo::UserAgent;
157             my $tx = $ua->build_tx(get => $url);
158             $tx = $oauth->authenticate($tx->req)->request_with(Mojo::UserAgent->new);
159            
160             =head1 DESCRIPTION
161              
162             L implements OAuth 1.0 request authentication according to
163             L. It does not implement the user
164             agent requests needed for the complete OAuth 1.0 authorization flow; it only
165             prepares and signs requests, leaving the rest up to your application. It can
166             authenticate requests for L, L, L,
167             and can be extended to operate on other types of requests.
168              
169             Some user agents can be configured to automatically authenticate each request
170             with a L object.
171              
172             # LWP::UserAgent
173             my $ua = LWP::UserAgent->new;
174             $ua->add_handler(request_prepare => sub { $oauth->authenticate($_[0]) });
175            
176             # Mojo::UserAgent
177             my $ua = Mojo::UserAgent->new;
178             $ua->on(start => sub { $oauth->authenticate($_[1]->req) });
179              
180             =head1 RETRIEVING ACCESS TOKENS
181              
182             The process of retrieving access tokens and token secrets for authorization on
183             behalf of a user may differ among various APIs, but it follows this general
184             format (error checking is left as an exercise to the reader):
185              
186             use WWW::OAuth;
187             use WWW::OAuth::Util 'form_urldecode';
188             use HTTP::Tiny;
189             my $ua = HTTP::Tiny->new;
190             my $oauth = WWW::OAuth->new(
191             client_id => $client_id,
192             client_secret => $client_secret,
193             );
194            
195             # Request token request
196             my $res = $oauth->authenticate({ method => 'POST', url => $request_token_url },
197             { oauth_callback => $callback_url })->request_with($ua);
198             my %res_data = @{form_urldecode $res->{content}};
199             my ($request_token, $request_secret) = @res_data{'oauth_token','oauth_token_secret'};
200            
201             Now, the returned request token must be used to construct a URL for the user to
202             go to and authorize your application. The exact method differs by API. The user
203             will usually be redirected to the C<$callback_url> passed earlier after
204             authorizing, with a verifier token that can be used to retrieve the access
205             token and secret.
206            
207             # Access token request
208             $oauth->token($request_token);
209             $oauth->token_secret($request_secret);
210             my $res = $oauth->authenticate({ method => 'POST', url => $access_token_url },
211             { oauth_verifier => $verifier_token })->request_with($ua);
212             my %res_data = @{form_urldecode $res->{content}};
213             my ($access_token, $access_secret) = @res_data{'oauth_token','oauth_token_secret'};
214            
215             Finally, the access token and secret can now be stored and used to authorize
216             your application on behalf of this user.
217              
218             $oauth->token($access_token);
219             $oauth->token_secret($access_secret);
220              
221             =head1 ATTRIBUTES
222              
223             L implements the following attributes.
224              
225             =head2 client_id
226              
227             my $client_id = $oauth->client_id;
228             $oauth = $oauth->client_id($client_id);
229              
230             Client ID used to identify application (sometimes called an API key or consumer
231             key). Required for all requests.
232              
233             =head2 client_secret
234              
235             my $client_secret = $oauth->client_secret;
236             $oauth = $oauth->client_secret($client_secret);
237              
238             Client secret used to authenticate application (sometimes called an API secret
239             or consumer secret). Required for all requests.
240              
241             =head2 token
242              
243             my $token = $oauth->token;
244             $oauth = $oauth->token($token);
245              
246             Request or access token used to identify resource owner. Leave undefined for
247             temporary credentials requests (request token requests).
248              
249             =head2 token_secret
250              
251             my $token_secret = $oauth->token_secret;
252             $oauth = $oauth->token_secret($token_secret);
253              
254             Request or access token secret used to authenticate on behalf of resource
255             owner. Leave undefined for temporary credentials requests (request token
256             requests).
257              
258             =head2 signature_method
259              
260             my $method = $oauth->signature_method;
261             $oauth = $oauth->signature_method($method);
262              
263             Signature method, can be C, C<HMAC-SHA1>, a coderef that implements </td> </tr> <tr> <td class="h" > <a name="264">264</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> the C<RSA-SHA1> method, or an object that implements the C<RSA-SHA1> method </td> </tr> <tr> <td class="h" > <a name="265">265</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> with a C<sign> method like L<Crypt::OpenSSL::RSA>. Defaults to C<HMAC-SHA1>. </td> </tr> <tr> <td class="h" > <a name="266">266</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="267">267</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head1 METHODS </td> </tr> <tr> <td class="h" > <a name="268">268</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="269">269</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<WWW::OAuth> implements the following methods. </td> </tr> <tr> <td class="h" > <a name="270">270</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="271">271</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head2 authenticate </td> </tr> <tr> <td class="h" > <a name="272">272</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="273">273</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> $container = $oauth->authenticate($container, \%oauth_params); </td> </tr> <tr> <td class="h" > <a name="274">274</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> my $container = $oauth->authenticate($http_request, \%oauth_params); </td> </tr> <tr> <td class="h" > <a name="275">275</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> my $container = $oauth->authenticate(Basic => { method => 'GET', url => $url }, \%oauth_params); </td> </tr> <tr> <td class="h" > <a name="276">276</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="277">277</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> Wraps the HTTP request in a container with L<WWW::OAuth::Util/"oauth_request">, </td> </tr> <tr> <td class="h" > <a name="278">278</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> then sets the Authorization header using L</"authorization_header"> to sign the </td> </tr> <tr> <td class="h" > <a name="279">279</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> request for OAuth 1.0. An optional hashref of OAuth parameters will be passed </td> </tr> <tr> <td class="h" > <a name="280">280</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> through to L</"authorization_header">. Returns the container object. </td> </tr> <tr> <td class="h" > <a name="281">281</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="282">282</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head2 authorization_header </td> </tr> <tr> <td class="h" > <a name="283">283</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="284">284</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> my $auth_header = $oauth->authorization_header($container, \%oauth_params); </td> </tr> <tr> <td class="h" > <a name="285">285</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> my $auth_header = $oauth->authorization_header($http_request, \%oauth_params); </td> </tr> <tr> <td class="h" > <a name="286">286</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> my $auth_header = $oauth->authorization_header(Basic => { method => 'GET', url => $url }, \%oauth_params); </td> </tr> <tr> <td class="h" > <a name="287">287</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="288">288</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> Forms an OAuth 1.0 signed Authorization header for the passed request. As in </td> </tr> <tr> <td class="h" > <a name="289">289</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L</"authenticate">, the request may be specified in any form accepted by </td> </tr> <tr> <td class="h" > <a name="290">290</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<WWW::OAuth::Util/"oauth_request">. OAuth parameters (starting with C<oauth_>) </td> </tr> <tr> <td class="h" > <a name="291">291</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> may be optionally specified in a hashref and will override any generated OAuth </td> </tr> <tr> <td class="h" > <a name="292">292</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> parameters of the same name (they should not be present in the request URL or </td> </tr> <tr> <td class="h" > <a name="293">293</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> body parameters). Returns the signed header value. </td> </tr> <tr> <td class="h" > <a name="294">294</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="295">295</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head1 HTTP REQUEST CONTAINERS </td> </tr> <tr> <td class="h" > <a name="296">296</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="297">297</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> Request containers provide a unified interface for L</"authenticate"> to parse </td> </tr> <tr> <td class="h" > <a name="298">298</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> and update HTTP requests. They must perform the L<Role::Tiny> role </td> </tr> <tr> <td class="h" > <a name="299">299</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<WWW::OAuth::Request>. Custom container classes can be instantiated </td> </tr> <tr> <td class="h" > <a name="300">300</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> directly or via L<WWW::OAuth::Util/"oauth_request">. </td> </tr> <tr> <td class="h" > <a name="301">301</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="302">302</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head2 Basic </td> </tr> <tr> <td class="h" > <a name="303">303</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="304">304</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<WWW::OAuth::Request::Basic> contains the request attributes directly, for </td> </tr> <tr> <td class="h" > <a name="305">305</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> user agents such as L<HTTP::Tiny> that do not use request objects. </td> </tr> <tr> <td class="h" > <a name="306">306</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="307">307</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head2 HTTP_Request </td> </tr> <tr> <td class="h" > <a name="308">308</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="309">309</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<WWW::OAuth::Request::HTTP_Request> wraps a L<HTTP::Request> object, which </td> </tr> <tr> <td class="h" > <a name="310">310</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> is compatible with several user agents including L<LWP::UserAgent>, </td> </tr> <tr> <td class="h" > <a name="311">311</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<HTTP::Thin>, and L<Net::Async::HTTP>. </td> </tr> <tr> <td class="h" > <a name="312">312</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="313">313</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head2 Mojo </td> </tr> <tr> <td class="h" > <a name="314">314</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="315">315</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<WWW::OAuth::Request::Mojo> wraps a L<Mojo::Message::Request> object, </td> </tr> <tr> <td class="h" > <a name="316">316</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> which is used by L<Mojo::UserAgent> via L<Mojo::Transaction>. </td> </tr> <tr> <td class="h" > <a name="317">317</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="318">318</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head1 BUGS </td> </tr> <tr> <td class="h" > <a name="319">319</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="320">320</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> Report any issues on the public bugtracker. </td> </tr> <tr> <td class="h" > <a name="321">321</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="322">322</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head1 AUTHOR </td> </tr> <tr> <td class="h" > <a name="323">323</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="324">324</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> Dan Book <dbook@cpan.org> </td> </tr> <tr> <td class="h" > <a name="325">325</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="326">326</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head1 COPYRIGHT AND LICENSE </td> </tr> <tr> <td class="h" > <a name="327">327</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="328">328</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> This software is Copyright (c) 2015 by Dan Book. </td> </tr> <tr> <td class="h" > <a name="329">329</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="330">330</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> This is free software, licensed under: </td> </tr> <tr> <td class="h" > <a name="331">331</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="332">332</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> The Artistic License 2.0 (GPL Compatible) </td> </tr> <tr> <td class="h" > <a name="333">333</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="334">334</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> =head1 SEE ALSO </td> </tr> <tr> <td class="h" > <a name="335">335</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> &nbsp; </td> </tr> <tr> <td class="h" > <a name="336">336</a> </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td > &nbsp; </td> <td class="s"> L<Net::OAuth>, L<Mojolicious::Plugin::OAuth2> </td> </tr> </table> </body> </html>