File Coverage

blib/lib/Net/OAuth.pm
Criterion Covered Total %
statement 33 33 100.0
branch 4 4 100.0
condition 6 8 75.0
subroutine 8 8 100.0
pod 1 5 20.0
total 52 58 89.6


line stmt bran cond sub pod time code
1             package Net::OAuth;
2 12     12   276991 use warnings;
  12         31  
  12         386  
3 12     12   63 use strict;
  12         21  
  12         360  
4 12     12   60 use Carp;
  12         26  
  12         7964  
5              
6             sub PROTOCOL_VERSION_1_0() {1}
7             sub PROTOCOL_VERSION_1_0A() {1.001}
8              
9             sub OAUTH_VERSION() {'1.0'}
10              
11             our $VERSION = '0.28';
12             our $SKIP_UTF8_DOUBLE_ENCODE_CHECK = 0;
13             our $PROTOCOL_VERSION = PROTOCOL_VERSION_1_0;
14              
15             sub request {
16 11     11 0 952 my $self = shift;
17 11         19 my $what = shift;
18 11         53 return $self->message($what . ' Request');
19             }
20              
21             sub response {
22 5     5 0 2217 my $self = shift;
23 5         11 my $what = shift;
24 5         25 return $self->message($what . ' Response');
25             }
26              
27             sub message {
28 17     17 1 32 my $self = shift;
29 17   33     96 my $base_class = ref $self || $self;
30 17         46 my $type = camel(shift);
31 17         52 my $class = $base_class . '::' . $type;
32 17         45 smart_require($class, 1);
33 16         214 return $class;
34             }
35              
36             sub camel {
37 17     17 0 27 my @words;
38 17         42 foreach (@_) {
39 17         101 while (/([A-Za-z0-9]+)/g) {
40 53         114 (my $word = $1) =~ s/authentication/auth/i;
41 53         192 push @words, $word;
42             }
43             }
44 17         141 my $name = join('', map("\u$_", @words));
45             }
46              
47             our %ALREADY_REQUIRED = (); # class_name => rv of ->require
48              
49             sub smart_require {
50 62     62 0 98 my $required_class = shift;
51 62   100     475 my $croak_on_error = shift || 0;
52 62 100       196 unless (exists $ALREADY_REQUIRED{$required_class}) {
53 28         1838 $ALREADY_REQUIRED{$required_class} = eval "require $required_class";
54 28 100 100     825 croak $@ if $@ and $croak_on_error;
55             }
56 61         294 return $ALREADY_REQUIRED{$required_class};
57             }
58              
59             =head1 NAME
60              
61             Net::OAuth - OAuth 1.0 for Perl
62              
63             =head1 SYNOPSIS
64              
65             # Web Server Example (Dancer)
66              
67             # This example is simplified for illustrative purposes, see the complete code in /demo
68              
69             # Note that client_id is the Consumer Key and client_secret is the Consumer Secret
70              
71             use Dancer;
72             use Net::OAuth::Client;
73              
74             sub client {
75             Net::OAuth::Client->new(
76             config->{client_id},
77             config->{client_secret},
78             site => 'https://www.google.com/',
79             request_token_path => '/accounts/OAuthGetRequestToken?scope=https%3A%2F%2Fwww.google.com%2Fm8%2Ffeeds%2F',
80             authorize_path => '/accounts/OAuthAuthorizeToken',
81             access_token_path => '/accounts/OAuthGetAccessToken',
82             callback => uri_for("/auth/google/callback"),
83             session => \&session,
84             );
85             }
86              
87             # Send user to authorize with service provider
88             get '/auth/google' => sub {
89             redirect client->authorize_url;
90             };
91              
92             # User has returned with token and verifier appended to the URL.
93             get '/auth/google/callback' => sub {
94              
95             # Use the auth code to fetch the access token
96             my $access_token = client->get_access_token(params->{oauth_token}, params->{oauth_verifier});
97              
98             # Use the access token to fetch a protected resource
99             my $response = $access_token->get('/m8/feeds/contacts/default/full');
100              
101             # Do something with said resource...
102              
103             if ($response->is_success) {
104             return "Yay, it worked: " . $response->decoded_content;
105             }
106             else {
107             return "Error: " . $response->status_line;
108             }
109             };
110              
111             dance;
112              
113             =head1 IMPORTANT
114              
115             Net::OAuth provides a low-level API for reading and writing OAuth messages.
116              
117             You probably should start with L.
118              
119             =head1 ABSTRACT
120              
121             OAuth is
122              
123             "An open protocol to allow secure API authentication in a simple and standard method from desktop and web applications."
124              
125             In practical terms, OAuth is a mechanism for a Consumer to request protected resources from a Service Provider on behalf of a user.
126              
127             Please refer to the OAuth spec: L
128              
129             Net::OAuth provides:
130              
131             =over
132              
133             =item * classes that encapsulate OAuth messages (requests and responses).
134              
135             =item * message signing
136              
137             =item * message serialization and parsing.
138              
139             =item * 2-legged requests (aka. tokenless requests, aka. consumer requests), see L
140              
141             =back
142              
143             Net::OAuth does not provide:
144              
145             =over
146              
147             =item * Consumer or Service Provider encapsulation
148              
149             =item * token/nonce/key storage/management
150              
151             =back
152              
153             =head1 DESCRIPTION
154              
155             =head2 OAUTH MESSAGES
156              
157             An OAuth message is a set of key-value pairs. The following message types are supported:
158              
159             Requests
160              
161             =over
162              
163             =item * Request Token (Net::OAuth::RequestTokenRequest)
164              
165             =item * Access Token (Net::OAuth::AccessTokenRequest)
166              
167             =item * User Authentication (Net::OAuth::UserAuthRequest)
168              
169             =item * Protected Resource (Net::OAuth::ProtectedResourceRequest)
170              
171             =item * Consumer Request (Net::OAuth::ConsumerRequest) (2-legged / token-less request)
172              
173             =back
174              
175             Responses
176              
177             =over
178              
179             =item * Request Token (Net::OAuth::RequestTokenResponse)
180              
181             =item * Access Token (Net::OAuth:AccessTokenResponse)
182              
183             =item * User Authentication (Net::OAuth::UserAuthResponse)
184              
185             =back
186              
187             Each OAuth message type has one or more required parameters, zero or more optional parameters, and most allow arbitrary parameters.
188              
189             All OAuth requests must be signed by the Consumer. Responses from the Service Provider, however, are not signed.
190              
191             To create a message, the easiest way is to use the factory methods (Net::OAuth->request, Net::OAuth->response, Net::OAuth->message). The following method invocations are all equivalent:
192              
193             $request = Net::OAuth->request('user authentication')->new(%params);
194             $request = Net::OAuth->request('user_auth')->new(%params);
195             $request = Net::OAuth->request('UserAuth')->new(%params);
196             $request = Net::OAuth->message('UserAuthRequest')->new(%params);
197              
198             The more verbose way is to use the class directly:
199              
200             use Net::OAuth::UserAuthRequest;
201             $request = Net::OAuth::UserAuthRequest->new(%params);
202              
203             You can also create a message by deserializing it from a Authorization header, URL, query hash, or POST body
204              
205             $request = Net::OAuth->request('protected resource')->from_authorization_header($ENV{HTTP_AUTHORIZATION}, %api_params);
206             $request = Net::OAuth->request('protected resource')->from_url($url, %api_params);
207             $request = Net::OAuth->request('protected resource')->from_hash({$q->Vars}, %api_params); # CGI
208             $request = Net::OAuth->request('protected resource')->from_hash($c->request->params, %api_params); # Catalyst
209             $response = Net::OAuth->response('request token')->from_post_body($response_content, %api_params);
210              
211             Note that the deserialization methods (as opposed to new()) expect OAuth protocol parameters to be prefixed with 'oauth_', as you would expect in a valid OAuth message.
212              
213             Before sending a request, the Consumer must first sign it:
214              
215             $request->sign;
216              
217             When receiving a request, the Service Provider should first verify the signature:
218              
219             die "Signature verification failed" unless $request->verify;
220              
221             When sending a message the last step is to serialize it and send it to wherever it needs to go. The following serialization methods are available:
222              
223             $response->to_post_body # a application/x-www-form-urlencoded POST body
224              
225             $request->to_url # the query string of a URL
226              
227             $request->to_authorization_header # the value of an HTTP Authorization header
228              
229             $request->to_hash # a hash that could be used for some other serialization
230              
231             =head2 API PARAMETERS vs MESSAGE PARAMETERS
232              
233             Net::OAuth defines 'message parameters' as parameters that are part of the transmitted OAuth message. These include any protocol parameter (prefixed with 'oauth_' in the message), and any additional message parameters (the extra_params hash).
234              
235             'API parameters' are parameters required to build a message object that are not transmitted with the message, e.g. consumer_secret, token_secret, request_url, request_method.
236              
237             There are various methods to inspect a message class to see what parameters are defined:
238              
239             $request->required_message_params;
240             $request->optional_message_params;
241             $request->all_message_params;
242             $request->required_api_params;
243             $request->optional_api_params;
244             $request->all_api_params;
245             $request->all_params;
246              
247             E.g.
248              
249             use Net::OAuth;
250             use Data::Dumper;
251             print Dumper(Net::OAuth->request("protected resource")->required_message_params);
252              
253             $VAR1 = [
254             'consumer_key',
255             'signature_method',
256             'timestamp',
257             'nonce',
258             'token'
259             ];
260              
261             =head2 ACCESSING PARAMETERS
262              
263             All parameters can be get/set using accessor methods. E.g.
264              
265             my $consumer_key = $request->consumer_key;
266             $request->request_method('POST');
267              
268             =head2 THE REQUEST_URL PARAMETER
269              
270             Any query parameters in the request_url are removed and added to the extra_params hash when generating the signature.
271              
272             E.g. the following requests are pretty much equivalent:
273              
274             my $request = Net::OAuth->request('Request Token')->new(
275             %params,
276             request_url => 'https://photos.example.net/request_token',
277             extra_params => {
278             foo => 'bar'
279             },
280             );
281              
282             my $request = Net::OAuth->request('Request Token')->new(
283             %params,
284             request_url => 'https://photos.example.net/request_token?foo=bar',
285             );
286              
287             Calling $request->request_url will still return whatever you set it to originally. If you want to get the request_url with the query parameters removed, you can do:
288              
289             my $url = $request->normalized_request_url;
290              
291             =head2 SIGNATURE METHODS
292              
293             The following signature methods are supported:
294              
295             =over
296              
297             =item * PLAINTEXT
298              
299             =item * HMAC-SHA1
300              
301             =item * HMAC-SHA256
302              
303             =item * RSA-SHA1
304              
305             =back
306              
307             The signature method is determined by the value of the signature_method parameter that is passed to the message constructor.
308              
309             If an unknown signature method is specified, the signing/verification will throw an exception.
310              
311             =head3 PLAINTEXT SIGNATURES
312              
313             This method is a trivial signature which adds no security. Not recommended.
314              
315             =head3 HMAC-SHA1 SIGNATURES
316              
317             This method is available if you have Digest::HMAC_SHA1 installed. This is by far the most commonly used method.
318              
319             =head3 HMAC-SHA256 SIGNATURES
320              
321             This method is available if you have Digest::SHA installed.
322              
323             =head3 RSA-SHA1 SIGNATURES
324              
325             To use RSA-SHA1 signatures, pass in a Crypt::OpenSSL::RSA object (or any object that can do $o->sign($str) and/or $o->verify($str, $sig))
326              
327             E.g.
328              
329             Consumer:
330              
331             use Crypt::OpenSSL::RSA;
332             use File::Slurp;
333             $keystring = read_file('private_key.pem');
334             $private_key = Crypt::OpenSSL::RSA->new_private_key($keystring);
335             $request = Net::OAuth->request('request token')->new(%params);
336             $request->sign($private_key);
337            
338             Service Provider:
339              
340             use Crypt::OpenSSL::RSA;
341             use File::Slurp;
342             $keystring = read_file('public_key.pem');
343             $public_key = Crypt::OpenSSL::RSA->new_public_key($keystring);
344             $request = Net::OAuth->request('request token')->new(%params);
345             if (!$request->verify($public_key)) {
346             die "Signature verification failed";
347             }
348              
349             Note that you can pass the key in as a parameter called 'signature_key' to the message constructor, rather than passing it to the sign/verify method, if you like.
350              
351             =head2 CONSUMER REQUESTS
352              
353             To send a request without including a token, use a Consumer Request:
354              
355             my $request = Net::OAuth->request('consumer')->new(
356             consumer_key => 'dpf43f3p2l4k3l03',
357             consumer_secret => 'kd94hf93k423kf44',
358             request_url => 'http://provider.example.net/profile',
359             request_method => 'GET',
360             signature_method => 'HMAC-SHA1',
361             timestamp => '1191242096',
362             nonce => 'kllo9940pd9333jh',
363             );
364              
365             $request->sign;
366              
367             See L
368              
369             =head2 I18N
370              
371             Per the OAuth spec, when making the signature Net::OAuth first encodes parameters to UTF-8. This means that any parameters you pass to Net::OAuth, if they might be outside of ASCII character set, should be run through Encode::decode() (or an equivalent PerlIO layer) first to decode them to Perl's internal character sructure.
372              
373             =head2 OAUTH 1.0A
374              
375             Background:
376              
377             L
378              
379             L
380              
381             Net::OAuth defaults to OAuth 1.0 spec compliance, and supports OAuth 1.0 Rev A with an optional switch:
382              
383             use Net::OAuth
384             $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
385              
386             It is recommended that any new projects use this switch if possible, and existing projects move to supporting this switch as soon as possible. Probably the easiest way for existing projects to do this is to turn on the switch and run your test suite. The Net::OAuth constructor will throw an exception where the new protocol parameters (callback, callback_confirmed, verifier) are missing.
387              
388             Internally, the Net::OAuth::Message constructor checks $Net::OAuth::PROTOCOL_VERSION and attempts to load the equivalent subclass in the Net::OAuth::V1_0A:: namespace. So if you instantiate a Net::OAuth::RequestTokenRequest object, you will end up with a Net::OAuth::V1_0A::RequestTokenRequest (a subclass of Net::OAuth::RequestTokenRequest) if the protocol version is set to PROTOCOL_VERSION_1_0A. You can also select a 1.0a subclass on a per-message basis by passing
389            
390             protocol_version => Net::OAuth::PROTOCOL_VERSION_1_0A
391              
392             in the API parameters hash.
393              
394             If you are not sure whether the entity you are communicating with is 1.0A compliant, you can try instantiating a 1.0A message first and then fall back to 1.0 if that fails:
395              
396             use Net::OAuth
397             $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
398             my $is_oauth_1_0 = 0;
399             my $response = eval{Net::OAuth->response('request token')->from_post_body($res->content)};
400             if ($@) {
401             if ($@ =~ /Missing required parameter 'callback_confirmed'/) {
402             # fall back to OAuth 1.0
403             $response = Net::OAuth->response('request token')->from_post_body(
404             $res->content,
405             protocol_version => Net::OAuth::PROTOCOL_VERSION_1_0
406             );
407             $is_oauth_1_0 = 1; # from now on treat the server as OAuth 1.0 compliant
408             }
409             else {
410             die $@;
411             }
412             }
413              
414             At some point in the future, Net::OAuth will default to Net::OAuth::PROTOCOL_VERSION_1_0A.
415              
416             =head1 DEMO
417              
418             There is a demo Consumer CGI in this package, also available online at L
419              
420             =head1 SEE ALSO
421              
422             L
423              
424             Check out L - it has a simpler API that may be more to your liking
425              
426             Check out L for a Twitter-specific OAuth API
427              
428             Check out L for a Netflix-specific OAuth API
429              
430             =head1 TODO
431              
432             =over
433              
434             =item * Support for repeating/multivalued parameters
435              
436             =item * Add convenience methods for SPs
437              
438             Something like:
439            
440             # direct from CGI.pm object
441             $request = Net::OAuth->request('Request Token')->from_cgi_query($cgi, %api_params);
442            
443             # direct from Catalyst::Request object
444             $request = Net::OAuth->request('Request Token')->from_catalyst_request($c->req, %api_params);
445            
446             # from Auth header and GET and POST params in one
447             local $/;
448             my $post_body = ;
449             $request = Net::OAuth->request('Request Token')->from_auth_get_and_post(
450             $ENV{HTTP_AUTHORIZATION},
451             $ENV{QUERY_STRING},
452             $post_body,
453             %api_params
454             );
455              
456             =back
457              
458             =head1 AUTHOR
459              
460             Keith Grennan, C<< >>
461              
462             =head1 COPYRIGHT & LICENSE
463              
464             Copyright 2009 Keith Grennan, all rights reserved.
465              
466             This program is free software; you can redistribute it and/or modify it
467             under the same terms as Perl itself.
468              
469             =cut
470              
471             1;