File Coverage

lib/OAuthomatic.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             package OAuthomatic;
3             # ABSTRACT: automate setup of access to OAuth-secured resources. Intended especially for use in console scripts, ad hoc applications etc.
4              
5             # FIXME: option to hardcode client_cred
6              
7              
8 1     1   783 use Moose;
  0            
  0            
9             our $VERSION = '0.0201'; # VERSION
10             use namespace::sweep;
11             # FIXME: switch to Moo
12             use MooseX::AttributeShortcuts;
13             use Carp;
14             use Path::Tiny;
15             use Encode qw/encode decode/;
16             use Const::Fast 0.014;
17             use Try::Tiny;
18             use Scalar::Util qw/reftype/;
19              
20             use OAuthomatic::Server;
21             use OAuthomatic::Config;
22             use OAuthomatic::Caller;
23             use OAuthomatic::SecretStorage;
24             use OAuthomatic::OAuthInteraction;
25             use OAuthomatic::UserInteraction;
26             use OAuthomatic::Internal::UsageGuard;
27             use OAuthomatic::Internal::Util qw/serialize_json parse_http_msg_json/;
28             use OAuthomatic::ServerDef qw/oauthomatic_predefined_for_name/;
29              
30             ###########################################################################
31             # Construction support, fixed attributes
32             ###########################################################################
33              
34             const my @_CONFIG_ATTRIBUTES => (
35             'app_name', 'password_group', 'browser',
36             'html_dir', 'debug',
37             );
38              
39              
40             has 'config' => (
41             is => 'ro', isa => 'OAuthomatic::Config', required => 1,
42             handles => \@_CONFIG_ATTRIBUTES);
43              
44              
45             has 'server' => (
46             is => 'ro', isa => 'OAuthomatic::Server', required => 1,
47             handles => ['site_name']);
48              
49              
50             # Promoting params to config if necessary and remapping server
51             around BUILDARGS => sub {
52             my $orig = shift;
53             my $class = shift;
54             my $objargs = $class->$orig(@_);
55             unless(exists $objargs->{config}) {
56             my @ctx_args;
57             foreach my $attr (@_CONFIG_ATTRIBUTES) {
58             if(exists $objargs->{$attr}) {
59             push @ctx_args, ($attr => $objargs->{$attr});
60             delete $objargs->{attr};
61             }
62             }
63             $objargs->{config} = OAuthomatic::Config->new(@ctx_args);
64             } else {
65             foreach my $attr (@_CONFIG_ATTRIBUTES) {
66             if(exists $objargs->{$attr}) {
67             OAuthomatic::Error::Generic->throw(
68             ident => "Bad parameter",
69             extra => "You can not specify config and $attr at the same time");
70             }
71             }
72             }
73              
74             if(exists($objargs->{server})) {
75             my $server = $objargs->{server};
76             unless( ref($server) ) {
77             $objargs->{server} = oauthomatic_predefined_for_name($server);
78             }
79             elsif( reftype($server) eq 'HASH') {
80             $objargs->{server} = OAuthomatic::Server->new(%{$objargs->{server}});
81             }
82             }
83              
84             return $objargs;
85             };
86              
87             ###########################################################################
88             # Pluggable behaviours
89             ###########################################################################
90              
91              
92             has 'secret_storage' => (is => 'lazy', does => 'OAuthomatic::SecretStorage');
93              
94             has 'oauth_interaction' => (is => 'lazy', does => 'OAuthomatic::OAuthInteraction');
95              
96             has 'user_interaction' => (is => 'lazy', does => 'OAuthomatic::UserInteraction');
97              
98             # Helper object used and shared by both default interactions
99             has '_micro_web' => (is => 'lazy');
100              
101             sub _build_secret_storage {
102             my ($self) = @_;
103             require OAuthomatic::SecretStorage::Keyring;
104             print "[OAuthomatic] Constructing default secret_storage\n" if $self->debug;
105             return OAuthomatic::SecretStorage::Keyring->new(
106             config => $self->config, server => $self->server);
107             }
108              
109             sub _build_user_interaction {
110             my ($self) = @_;
111             require OAuthomatic::UserInteraction::ViaMicroWeb;
112             print "[OAuthomatic] Constructing default user_interaction\n" if $self->debug;
113             return OAuthomatic::UserInteraction::ViaMicroWeb->new(
114             micro_web => $self->_micro_web);
115             }
116              
117             sub _build_oauth_interaction {
118             my ($self) = @_;
119             require OAuthomatic::OAuthInteraction::ViaMicroWeb;
120             print "[OAuthomatic] Constructing default oauth_interaction\n" if $self->debug;
121             return OAuthomatic::OAuthInteraction::ViaMicroWeb->new(
122             micro_web => $self->_micro_web);
123             }
124              
125             sub _build__micro_web {
126             my ($self) = @_;
127             require OAuthomatic::Internal::MicroWeb;
128             print "[OAuthomatic] Constructing MicroWeb object\n" if $self->debug;
129             return OAuthomatic::Internal::MicroWeb->new(
130             config => $self->config, server => $self->server);
131             }
132              
133             ###########################################################################
134             # Calling object and basic credentials management
135             ###########################################################################
136              
137              
138             # This is communicating object. It may be in various states
139             # modelled by client_cred and token_cred (both set - it is authorized
140             # and ready for any use, only client_cred - it has defined app tokens
141             # but must be authorized, none set - it is useless)
142             has '_caller' => (is => 'lazy', isa => 'OAuthomatic::Caller',
143             handles => [
144             'client_cred', 'token_cred',
145             ]);
146              
147             sub _build__caller {
148             my ($self) = @_;
149              
150             my $restored_client_cred = $self->secret_storage->get_client_cred();
151             if($restored_client_cred) {
152             print "[OAuthomatic] Loaded saved client (app) tokens. Key: ",
153             $restored_client_cred->key, "\n" if $self->debug;
154             }
155              
156             my $restored_token_cred = $self->secret_storage->get_token_cred();
157             if($restored_token_cred) {
158             print "[OAuthomatic] Loaded saved access tokens. Token: ",
159             $restored_token_cred->token, "\n" if $self->debug;
160             }
161              
162             my $caller = OAuthomatic::Caller->new(
163             config => $self->config,
164             server => $self->server,
165             client_cred => $restored_client_cred,
166             token_cred => $restored_token_cred);
167              
168             return $caller;
169             }
170              
171             # Updates client_cred both in-memory and in storage
172             sub _update_client_cred {
173             my ($self, $new_cred) = @_;
174             return if OAuthomatic::Types::ClientCred->equal($new_cred, $self->client_cred);
175              
176             if($new_cred) {
177             $self->secret_storage->save_client_cred($new_cred);
178             print "[OAuthomatic] Saved client credentials for future. Key: ", $new_cred->key, "\n" if $self->debug;
179             } else {
180             $self->secret_storage->clear_client_cred;
181             print "[OAuthomatic] Dropped saved client credentials\n" if $self->debug;
182             }
183              
184             $self->client_cred($new_cred);
185              
186             # Changed client means access is no longer valid
187             $self->_update_token_cred(undef);
188             return;
189             }
190              
191             # Updates token_cred both in-memory and in storage. $force param ignores identity check
192             # (to be used if we know we did incomplete update)
193             sub _update_token_cred {
194             my ($self, $new_cred, $force) = @_;
195             return if !$force && OAuthomatic::Types::TokenCred->equal($new_cred, $self->token_cred);
196              
197             if($new_cred) {
198             $self->secret_storage->save_token_cred($new_cred);
199             print "[OAuthomatic] Saved access credentials for future. Token: ", $new_cred->token, "\n" if $self->debug;
200             } else {
201             $self->secret_storage->clear_token_cred;
202             print "[OAuthomatic] Dropped saved access credentials\n" if $self->debug;
203             }
204              
205             $self->token_cred($new_cred);
206             return;
207             }
208              
209              
210             sub erase_client_cred {
211             my ($self) = @_;
212              
213             $self->_update_client_cred(undef);
214             return;
215             }
216              
217              
218             sub erase_token_cred {
219             my ($self) = @_;
220             $self->_update_token_cred(undef);
221             return;
222             }
223              
224             ###########################################################################
225             # Actual OAuth setup
226             ###########################################################################
227              
228             # Those are guards to keep track of supporting objects (mostly
229             # in-process web) activity (we may initiate supporting objects at
230             # various moments but close them after we know we are authorized)
231             has '_user_interaction_guard' => (
232             is=>'lazy', builder => sub {
233             return OAuthomatic::Internal::UsageGuard->new(obj => $_[0]->user_interaction);
234             });
235             has '_oauth_interaction_guard' => (
236             is=>'lazy', builder => sub {
237             return OAuthomatic::Internal::UsageGuard->new(obj => $_[0]->oauth_interaction);
238             });
239              
240             # Ensures app tokens are known
241             sub _ensure_client_cred_known {
242             my ($self) = @_;
243              
244             return if $self->_caller->client_cred;
245              
246             print "[OAuthomatic] Application tokens not available, prompting user\n" if $self->debug;
247              
248             $self->_user_interaction_guard->prepare;
249              
250             my $client_cred = $self->user_interaction->prompt_client_credentials()
251             or OAuthomatic::Error::Generic->throw(
252             ident => "Client credentials missing",
253             extra => "Can't proceed without client credentials. Restart app and supply them.");
254              
255             # We save them straight away, in memory to use, in storage to keep them in case of crash
256             # or Ctrl-C (later we will clear them if they turn out wrong).
257             $self->_update_client_cred($client_cred);
258             return;
259             }
260              
261             # Ensures access tokens are known
262             sub _ensure_token_cred_known {
263             my ($self) = @_;
264             return if $self->_caller->token_cred;
265              
266             # To proceed we must have client credentials
267             $self->_ensure_client_cred_known;
268              
269             my $site_name = $self->site_name;
270             my $oauth_interaction = $self->oauth_interaction;
271             my $user_interaction = $self->user_interaction;
272              
273             print "[OAuthomatic] Application is not authorized to $site_name, initiating access-granting sequence\n" if $self->debug;
274              
275             $self->_oauth_interaction_guard->prepare;
276             $self->_user_interaction_guard->prepare;
277              
278             my $temporary_cred;
279             # We loop to retry in case entered app tokens turn out wrong
280             while(! $temporary_cred) {
281             $self->_ensure_client_cred_known; # Get new app keys if old were dropped
282             print "[OAuthomatic] Constructing authorization url\n" if $self->debug;
283             try {
284             $temporary_cred = $self->_caller->create_authorization_url(
285             $oauth_interaction->callback_url);
286             } catch {
287             my $error = $_;
288             if($error->isa("OAuthomatic::Error::HTTPFailure")) {
289             if($error->is_new_client_key_required) {
290             print STDERR $error, "\n";
291             print "\n\nReceived error suggests wrong client key.\nDropping it and retrying initialization.\n\n";
292             $self->erase_client_cred;
293             } else {
294             $error->throw;
295             }
296             } elsif($error->isa("OAuthomatic::Error")) {
297             $error->throw;
298             } else {
299             OAuthomatic::Error::Generic->throw(
300             ident => "Unknown error during authorization",
301             extra => $error);
302             }
303             };
304             }
305              
306             print "[OAuthomatic] Leading user to authorization page\n" if $self->debug;
307             $user_interaction->visit_oauth_authorize_page($temporary_cred->authorize_page);
308              
309             # Wait for post-auth redirect
310             my $verifier_cred = $oauth_interaction->wait_for_oauth_grant;
311              
312             print "[OAuthomatic] Got authorization (verification for token: " . $verifier_cred->token . "), requesting access token\n" if $self->debug;
313              
314             my $token_cred = $self->_caller->create_token_cred(
315             $temporary_cred, $verifier_cred);
316              
317             print "[OAuthomatic] Got access token: " . $token_cred->token, "\n" if $self->debug;
318              
319             # Now save those values
320             $self->_update_token_cred($token_cred, 'force');
321              
322             # Close supporting objects if they were started
323             $self->_user_interaction_guard->finish;
324             $self->_oauth_interaction_guard->finish;
325              
326             return;
327             }
328              
329              
330             sub ensure_authorized {
331             my ($self) = @_;
332             $self->_ensure_client_cred_known;
333             $self->_ensure_token_cred_known;
334             return;
335             }
336              
337             ######################################################################
338             # Making requests
339             ######################################################################
340              
341              
342             sub execute_request {
343             my ($self, @args) = @_;
344             $self->ensure_authorized;
345             my $reply;
346              
347             # Loop to retry on some failures
348             while(1) {
349             try {
350             $reply = $self->_caller->execute_oauth_request(@args);
351             } catch {
352             my $error = $_;
353             if($error->isa("OAuthomatic::Error::HTTPFailure")) {
354             if($error->is_new_client_key_required) {
355             print STDERR $error, "\n";
356             print "\n\nReceived error suggests wrong client key.\nDropping it to enforce re-initialization.\n\n";
357             $self->erase_client_cred;
358             # Will redo loop
359             } elsif($error->is_new_token_required) {
360             print STDERR $error, "\n";
361             print "\n\nReceived error suggests wrong token.\nDropping it to enforce re-initialization.\n\n";
362             $self->erase_token_cred;
363             # will redo loop
364             } else {
365             $error->throw;
366             }
367             } elsif($error->isa("OAuthomatic::Error")) {
368             $error->throw;
369             } else {
370             OAuthomatic::Error::Generic->throw(
371             ident => "Unknown error during execution",
372             extra => $error);
373             }
374             };
375             last if $reply;
376             };
377             return $reply;
378             }
379              
380              
381             ## no critic (RequireArgUnpacking)
382             sub build_request {
383             my $self = shift;
384             $self->ensure_authorized;
385             return $self->_caller->build_oauth_request(@_);
386             }
387             ## use critic
388              
389              
390             sub get {
391             my ($self, $url, $url_args) = @_;
392             my $r = $self->execute_request(
393             method => "GET", url => $url, url_args => $url_args);
394             return $r->decoded_content;
395             }
396              
397              
398             sub get_xml {
399             my ($self, $url, $url_args) = @_;
400             my $r = $self->execute_request(
401             method => "GET", url => $url, url_args => $url_args,
402             content_type => "application/xml; charset=utf-8");
403             return $r->decoded_content;
404             }
405              
406              
407             sub get_json {
408             my ($self, $url, $url_args) = @_;
409              
410             my $r = $self->execute_request(
411             method => "GET", url => $url, url_args => $url_args);
412              
413             return parse_http_msg_json($r, 'force'); # FIXME: or error on content-type mismatch?
414             }
415              
416              
417             sub post {
418             my $self = shift;
419             my $url = shift;
420             my @args = (method => "POST", url => $url);
421             if(@_ > 1) {
422             push @args, (url_args => shift);
423             }
424             my $body = shift;
425             if(reftype($body) eq 'HASH') {
426             push @args, (body_form => $body);
427             } else {
428             push @args, (body => $body);
429             }
430              
431             my $r = $self->execute_request(@args);
432             return $r->decoded_content;
433             }
434              
435              
436             sub post_xml {
437             my $self = shift;
438             my $url = shift;
439             my @args = (method => "POST",
440             url => $url,
441             content_type => 'application/xml; charset=utf-8');
442             if(@_ > 1) {
443             push @args, (url_args => shift);
444             }
445             my $body = shift;
446             push @args, (body => $body);
447              
448             my $r = $self->execute_request(@args);
449             return $r->decoded_content;
450             }
451              
452              
453             sub post_json {
454             my $self = shift;
455             my $url = shift;
456             my @args = (method => "POST",
457             url => $url,
458             content_type => 'application/json; charset=utf-8');
459             if(@_ > 1) {
460             push @args, (url_args => shift);
461             }
462             push @args, (body => serialize_json(shift));
463              
464             my $r = $self->execute_request(@args);
465              
466             return parse_http_msg_json($r);
467             }
468              
469              
470             sub put {
471             my $self = shift;
472             my $url = shift;
473             my @args = (method => "PUT", url => $url);
474             if(@_ > 1) {
475             push @args, (url_args => shift);
476             }
477             my $body = shift;
478             if(reftype($body) eq 'HASH') {
479             push @args, (body_form => $body);
480             } else {
481             push @args, (body => $body);
482             }
483              
484             my $r = $self->execute_request(@args);
485             return $r->decoded_content;
486             }
487              
488              
489             sub put_xml {
490             my $self = shift;
491             my $url = shift;
492             my @args = (method => "PUT",
493             url => $url,
494             content_type => 'application/xml; charset=utf-8');
495             if(@_ > 1) {
496             push @args, (url_args => shift);
497             }
498             my $body = shift;
499             push @args, (body => $body);
500              
501             my $r = $self->execute_request(@args);
502             return $r->decoded_content;
503             }
504              
505              
506             sub put_json {
507             my $self = shift;
508             my $url = shift;
509             my @args = (method => "PUT",
510             url => $url,
511             content_type => 'application/json; charset=utf-8');
512             if(@_ > 1) {
513             push @args, (url_args => shift);
514             }
515             push @args, (body => serialize_json(shift));
516              
517             my $r = $self->execute_request(@args);
518              
519             return parse_http_msg_json($r);
520             }
521              
522              
523             sub delete_ {
524             my ($self, $url, $url_args) = @_;
525             my $r = $self->execute_request(
526             method => "DELETE", url => $url, url_args => $url_args);
527             return $r->decoded_content;
528             }
529              
530              
531             # FIXME: base url prepended to urls not starting with http?
532              
533             1;
534              
535             __END__
536              
537             =pod
538              
539             =encoding UTF-8
540              
541             =head1 NAME
542              
543             OAuthomatic - automate setup of access to OAuth-secured resources. Intended especially for use in console scripts, ad hoc applications etc.
544              
545             =head1 VERSION
546              
547             version 0.0201
548              
549             =head1 SYNOPSIS
550              
551             Construct the object:
552              
553             my $oauthomatic = OAuthomatic->new(
554             app_name => "News trend parser",
555             password_group => "OAuth tokens (personal)",
556             server => OAuthomatic::Server->new(
557             # OAuth protocol URLs, formally used in the protocol
558             oauth_temporary_url => 'https://some.site/api/oauth/request_token',
559             oauth_authorize_page => 'https://some.site/api/oauth/authorize',
560             oauth_token_url => 'https://some.site/api/oauth/access_token',
561             # Extra info about remote site, not required (but may make users happier)
562             site_name => "SomeSite.com",
563             site_client_creation_page => "https://some.site.com/settings/oauth_apps",
564             site_client_creation_desc => "SomeSite applications page",
565             site_client_creation_help =>
566             "Click Create App button and fill the form.\n"
567             . "Use AppToken as client key and AppSecret as client secret.\n"),
568             );
569              
570             and profit:
571              
572             my $info = $oauthomatic->get_json(
573             'https://some.site.com/api/get_issues',
574             { type => 'bug', page_len => 10, release => '7.3' });
575              
576             On first run user (maybe just you) will be led through OAuth
577             initialization sequence, but the script need not care.
578              
579             =head1 DESCRIPTION
580              
581             B<WARNING:> I<This is early release. Things may change (although I won't
582             change crucial APIs without good reason).>
583              
584             Main purpose of this module: make it easy to start scripting around
585             some OAuth-controlled site (at the moment, OAuth 1.0a is
586             supported). The user needs only to check site docs for appropriate
587             URLs, construct OAuthomatic object, and go.
588              
589             I wrote this module as I always struggled with using OAuth-secured
590             APIs from perl. Modules I found on CPAN were mostly low-level,
591             not-too-well documented, and - worst of all - required my scripts to
592             handle whole „get keys, acquire permissions, save tokens” sequence.
593              
594             OAuthomatic is very opinionated. It shows instructions in English. It
595             uses L<Passwd::Keyring::Auto> to save (and restore) sensitive data. It
596             assumes application keys are to be provided by the user on first run
597             (not distributed with the script). It spawns web browser (and
598             temporary in-process webserver to back it). It provides a few HTML
599             pages and they are black-on-white, 14pt font, without pictures.
600              
601             Thanks to all those assumptions it usually just works, letting the
602             script author to think about job at hand instead of thinking about
603             authorization. And, once script grows to application, all those
604             opinionated parts can be tweaked or substituted where necessary.
605              
606             =head1 PARAMETERS
607              
608             =head2 server
609              
610             Server-related parameters (in particular, all crucial URLs), usually
611             found in appropriate server developer docs.
612              
613             There are three ways to specify this parameter
614              
615             =over 4
616              
617             =item *
618              
619             by providing L<OAuthomatic::Server> object instance. For example:
620              
621             OAuthomatic->new(
622             # ... other params
623             server => OAuthomatic::Server->new(
624             oauth_temporary_url => 'https://api.linkedin.com/uas/oauth/requestToken',
625             oauth_authorize_page => 'https://api.linkedin.com/uas/oauth/authenticate',
626             oauth_token_url => 'https://api.linkedin.com/uas/oauth/accessToken',
627             # ...
628             ));
629              
630             See L<OAuthomatic::Server> for detailed description of all parameters.
631              
632             =item *
633              
634             by providing hash reference of parameters. This is equivalent to
635             example above, but about 20 characters shorter:
636              
637             OAuthomatic->new(
638             # ... other params
639             server => {
640             oauth_temporary_url => 'https://api.linkedin.com/uas/oauth/requestToken',
641             oauth_authorize_page => 'https://api.linkedin.com/uas/oauth/authenticate',
642             oauth_token_url => 'https://api.linkedin.com/uas/oauth/accessToken',
643             # ...
644             });
645              
646             =item *
647              
648             by providing name of predefined server. As there exists L<OAuthomatic::ServerDef::LinkedIn> module:
649              
650             OAuthomatic->new(
651             # ... other params
652             server => 'LinkedIn',
653             );
654              
655             See L<OAuthomatic::ServerDef> for more details about predefined servers.
656              
657             =back
658              
659             =head2 app_name
660              
661             Symbolic application name. Used in various prompts. Set to something
662             script users will recognize (script name, application window name etc).
663              
664             Examples: C<build_publisher.pl>, C<XyZ sync scripts>.
665              
666             =head2 password_group
667              
668             Password group/folder used to distinguish saved tokens (a few
669             scripts/apps will share the same tokens if they refer to the same
670             password_group). Ignored if you provide your own L</secret_storage>.
671              
672             Default value: C<OAuthomatic tokens> (remember to change if you have
673             scripts working on few different accounts of the same website).
674              
675             =head2 browser
676              
677             Command used to spawn the web browser.
678              
679             Default value: best guess (using L<Browser::Open>).
680              
681             Set to empty string to avoid spawning browser at all and show
682             instructions (I<Open web browser on https://....>) on the console
683             instead.
684              
685             =head2 html_dir
686              
687             Directory containing HTML templates and related resources for pages
688             generated by OAuthomatic (post-authorization page, application tokens
689             prompt and confirmation).
690              
691             To modify their look and feel, copy C<oauthomatic_html> directory from
692             OAuthomatic distribution somewhere, edit to your taste and provide
693             resulting directory as C<html_dir>.
694              
695             By default, files distributed with OAuthomatic are used.
696              
697             =head2 debug
698              
699             Make object print various info to STDERR. Useful while diagnosing
700             problems.
701              
702             =head1 ADDITIONAL PARAMETERS
703              
704             =head2 config
705              
706             Object gathering all parameters except server. Usually constructed
707             under the hood, but may be useful if you need those params for sth else
708             (especially, if you customize object behaviour). For example:
709              
710             my $server = OAuthomatic::Server->new(...);
711             my $config = OAuthomatic::Config->new(
712             app_name => ...,
713             password_group => ...,
714             ... and the rest ...);
715             my $oauthomatic = OAuthomatic->new(
716             server => $server,
717             config => $config, # instead of normal params
718             user_interaction => OAuthomatic::UserInteraction::ConsolePrompts->new(
719             config => $config, server => $server));
720              
721             =head2 secret_storage
722              
723             Pluggable behaviour: modify the method used to persistently save and
724             restore various OAuth tokens. By default
725             L<OAuthomatic::SecretStorage::Keyring> (which uses
726             L<Passwd::Keyring::Auto> storage) is used, but any object implementing
727             L<OAuthomatic::SecretStorage> role can be substituted instead.
728              
729             =head2 oauth_interaction
730              
731             Pluggable behaviour: modify the way application uses to capture return
732             redirect after OAuth access is granted. By default temporary web
733             server is started on local address (it suffices to handle redirect to
734             localhost) and used to capture traffic, but any object implementing
735             L<OAuthomatic::OAuthInteraction> role can be substituted instead.
736              
737             In case default is used, look and feel of the final page can be
738             modified using L</html_dir>.
739              
740             =head2 user_interaction
741              
742             Pluggable behaviour: modify the way application uses to prompt user
743             for application keys. By default form is shown in the browser, but any object
744             implementing L<OAuthomatic::UserInteraction> role can be substituted instead.
745              
746             Note: you can use L<OAuthomatic::UserInteraction::ConsolePrompts>
747             to be prompted in the console.
748              
749             In case default is used, look and feel of the pages can be
750             modified using L</html_dir>.
751              
752             =head1 METHODS
753              
754             =head2 erase_client_cred
755              
756             $oa->erase_client_cred();
757              
758             Drops current client (app) credentials both from the object and, possibly, from storage.
759              
760             Use if you detect error which prove they are wrong, or if you want to forget them for privacy/security reasons.
761              
762             =head2 erase_token_cred
763              
764             $oa->erase_token_cred();
765              
766             Drops access (app) credentials both from the object and, possibly, from storage.
767              
768             Use if you detect error which prove they are wrong.
769              
770             =head2 ensure_authorized
771              
772             $oa->ensure_authorized();
773              
774             Ensure object is ready to make calls.
775              
776             If initialization sequence happened in the past and appropriate tokens
777             are available, this method restores them.
778              
779             If not, it performs all the work required to setup OAuth access to
780             given website: asks user for application keys (or loads them if
781             already known), leads the user through application authorization
782             sequence, preserve acquired tokens for future runs.
783              
784             Having done all that, it leaves object ready to make OAuth-signed
785             calls (actual signatures are calculated using L<Net::OAuth>.
786              
787             Calling this method is not necessary - it will be called automatically
788             before first request is executed, if not done earlier.
789              
790             =head2 execute_request
791              
792             $oa->execute_request(
793             method => $method, url => $url, url_args => $args,
794             body => $body,
795             content_type => $content_type)
796              
797             $oa->execute_request(
798             method => $method, url => $url, url_args => $args,
799             body_form => $body_form,
800             content_type => $content_type)
801              
802             Make OAuth-signed request to given url. Lowest level method, see below
803             for methods which add additional glue or require less typing.
804              
805             Parameters:
806              
807             =over 4
808              
809             =item method
810              
811             One of C<'GET'>, C<'POST'>, C<'PUT'>, C<'DELETE'>.
812              
813             =item url
814              
815             Actual URL to call (C<'http://some.site.com/api/...'>)
816              
817             =item url_args (optional)
818              
819             Additional arguments to escape and add to the URL. This is simply shortcut,
820             three calls below are equivalent:
821              
822             $c->execute_oauth_request(method => "GET",
823             url => "http://some.where/api?x=1&y=2&z=a+b");
824              
825             $c->execute_oauth_request(method => "GET",
826             url => "http://some.where/api",
827             url_args => {x => 1, y => 2, z => 'a b'});
828              
829             $c->execute_oauth_request(method => "GET",
830             url => "http://some.where/api?x=1",
831             url_args => {y => 2, z => 'a b'});
832              
833             =item body_form OR body
834              
835             Exactly one of those must be specified for POST and PUT (none for GET or DELETE).
836              
837             Specifying C<body_form> means, that we are creating www-urlencoded
838             form. Specified values will be rendered appropriately and whole message
839             will get proper content type. Example:
840              
841             $c->execute_oauth_request(method => "POST",
842             url => "http://some.where/api",
843             body_form => {par1 => 'abc', par2 => 'd f'});
844              
845             Note that this is not just a shortcut for setting body to already
846             serialized form. Case of urlencoded form is treated in a special way
847             by OAuth (those values impact OAuth signature). To avoid signature
848             verification errors, OAuthomatic will reject such attempts:
849              
850             # WRONG AND WILL FAIL. Use body_form if you post form.
851             $c->execute_oauth_request(method => "POST",
852             url => "http://some.where/api",
853             body => 'par1=abc&par2=d+f',
854             content_type => 'application/x-www-form-urlencoded');
855              
856             Specifying C<body> means, that we post non-form body (for example
857             JSON, XML or even binary data). Example:
858              
859             $c->execute_oauth_request(method => "POST",
860             url => "http://some.where/api",
861             body => "<product><item-no>3434</item-no><price>334.22</price></product>",
862             content_type => "application/xml; charset=utf-8");
863              
864             Value of body can be either binary string (which will be posted as-is), or
865             perl unicode string (which will be encoded according to the content type, what by
866             default means utf-8).
867              
868             Such content is not covered by OAuth signature, so less secure (at
869             least if it is posted over non-SSL connection).
870              
871             For longer bodies, references are supported:
872              
873             $c->execute_oauth_request(method => "POST",
874             url => "http://some.where/api",
875             body => \$body_string,
876             content_type => "application/xml; charset=utf-8");
877              
878             =item content_type
879              
880             Used to set content type of the request. If missing, it is set to
881             C<text/plain; charset=utf-8> if C<body> param is specified and to
882             C<application/x-www-form-urlencoded; charset=utf-8> if C<body_form>
883             param is specified.
884              
885             Note that module author does not test behaviour on encodings different
886             than utf-8 (although they may work).
887              
888             =back
889              
890             Returns L<HTTP::Response> object.
891              
892             Throws structural exception on HTTP (40x, 5xx) and technical (like
893             network) failures.
894              
895             Example:
896              
897             my $result = $oauthomatic->make_request(
898             method => "GET", url => "https://some.api/get/things",
899             url_args => {name => "Thingy", count => 4});
900             # $result is HTTP::Response object and we know request succeeded
901             # on HTTP level
902              
903             =head2 build_request
904              
905             $oa->build_request(method => $method, url => $url, url_args => $args,
906             body_form => $body_form, body => $body,
907             content_type => $content_type)
908              
909             Build appropriate HTTP::Request, ready to be executed, with proper
910             headers and signature, but do not execute it. Useful if you prefer
911             to use your own HTTP client.
912              
913             See L<OAuthomatic::Caller/build_oauth_request> for the meaning of
914             parameters.
915              
916             Note: if you are executing requests yourself, consider detecting cases
917             of wrong client credentials, obsolete token credentials etc, and
918             calling or L</erase_client_cred> or L</erase_token_cred>.
919             The L<OAuthomatic::Error::HTTPFailure> may be of help.
920              
921             =head2 get
922              
923             my $reply = $ua->get($url, { url => 'args', ...);
924              
925             Shortcut. Make OAuth-signed GET request, ensure request succeeded and
926             return it's body without parsing it (but decoding it from transport encoding).
927              
928             =head2 get_xml
929              
930             my $reply = $ua->get($url, { url => 'args', ...);
931              
932             Shortcut. Make OAuth-signed GET request, ensure request succeeded and
933             return it's body. Body is not parsed, it remains to be done in the outer program (there are
934             so many XML parsers I did not want to vote for one).
935              
936             This is almost equivalent to L</get> (except it sets request content
937             type to C<application/xml>), mainly used to clearly signal intent.
938              
939             =head2 get_json
940              
941             my $reply = $oa->get_json($url, {url=>args, ...});
942             # $reply is hash or array ref
943              
944             Shortcut. Make OAuth-signed GET request, ensure it succeeded, parse result as JSON,
945             return resulting structure.
946              
947             Example:
948              
949             my $result = $oauthomatic->get_json(
950             "https://some.api/things", {filter => "Thingy", count => 4});
951             # Grabs https://some.api/things?filter=Thingy&count=4 and parses as JSON
952             # $result is hash or array ref
953              
954             =head2 post
955              
956             my $reply = $ua->post($url, { body=>args, ... });
957             my $reply = $ua->post($url, { url=>args, ...}, { body=>args, ... });
958             my $reply = $ua->post($url, "body content");
959             my $reply = $ua->post($url, { url=>args, ...}, "body content");
960             my $reply = $ua->post($url, $ref_to_body_content);
961             my $reply = $ua->post($url, { url=>args, ...}, $ref_to_body_content);
962              
963             Shortcut. Make OAuth-signed POST request, ensure request succeeded and
964             return reply body without parsing it.
965              
966             May take two or three parameters. In two-parameter form it takes URL
967             to POST and body. In three-parameter, it takes URL, additional URL
968             params (to be added to URI), and body.
969              
970             Body may be specified as:
971              
972             =over 4
973              
974             =item *
975              
976             Hash reference, in which case contents of this hash are treated as
977             form fields, urlencoded and whole request is executed as urlencoded
978             POST.
979              
980             =item *
981              
982             Scalar or reference to scalar, in which case it is pasted verbatim as post body.
983              
984             =back
985              
986             Note: use use L</execute_request> for more control on parameters (in
987             particular, content type).
988              
989             =head2 post_xml
990              
991             my $reply = $ua->post($url, "<xml>content</xml>");
992             my $reply = $ua->post($url, { url=>args, ...}, "<xml>content</xml>");
993             my $reply = $ua->post($url, $ref_to_xml_content);
994             my $reply = $ua->post($url, { url=>args, ...}, $ref_to_xml_content);
995              
996             Shortcut. Make OAuth-signed POST request, ensure request succeeded and
997             return reply body without parsing it.
998              
999             May take two or three parameters. In two-parameter form it takes URL
1000             to POST and body. In three-parameter, it takes URL, additional URL
1001             params (to be added to URI), and body.
1002              
1003             This is very close to L</post> (XML is neither rendered, nor parsed here),
1004             used mostly to set proper content-type and to clearly signal intent in the code.
1005              
1006             =head2 post_json
1007              
1008             my $reply = $oa->post_json($url, { json=>args, ... });
1009             my $reply = $oa->post_json($url, { url=>args, ...}, { json=>args, ... });
1010             my $reply = $oa->post_json($url, "json content");
1011             my $reply = $oa->post_json($url, { url=>args, ...}, "json content");
1012             # $reply is hash or arrayref constructed by parsing output
1013              
1014             Make OAuth-signed POST request. Parameter is formatted as JSON, result
1015             also i parsed as JSON.
1016              
1017             May take two or three parameters. In two-parameter form it takes URL
1018             and JSON body. In three-parameter, it takes URL, additional URL params
1019             (to be added to URI), and JSON body.
1020              
1021             JSON body may be specified as:
1022              
1023             =over 4
1024              
1025             =item *
1026              
1027             Hash or array reference, in which case contents of this reference are serialized to JSON
1028             and then used as request body.
1029              
1030             =item *
1031              
1032             Scalar or reference to scalar, in which case it is treated as already serialized JSON
1033             and posted verbatim as post body.
1034              
1035             =back
1036              
1037             Example:
1038              
1039             my $result = $oauthomatic->post_json(
1040             "https://some.api/things/prettything", {
1041             mode => 'simple',
1042             }, {
1043             name => "Pretty Thingy",
1044             description => "This is very pretty",
1045             tags => ['secret', 'pretty', 'most-important'],
1046             }, count => 4);
1047             # Posts to https://some.api/things/prettything?mode=simple
1048             # the following body (formatting and ordering may be different):
1049             # {
1050             # "name": "Pretty Thingy",
1051             # "description": "This is very pretty",
1052             # "tags": ['secret', 'pretty', 'most-important'],
1053             # }
1054              
1055             =head2 put
1056              
1057             my $reply = $ua->put($url, { body=>args, ... });
1058             my $reply = $ua->put($url, { url=>args, ...}, { body=>args, ... });
1059             my $reply = $ua->put($url, "body content");
1060             my $reply = $ua->put($url, { url=>args, ...}, "body content");
1061             my $reply = $ua->put($url, $ref_to_body_content);
1062             my $reply = $ua->put($url, { url=>args, ...}, $ref_to_body_content);
1063              
1064             Shortcut. Make OAuth-signed PUT request, ensure request succeeded and
1065             return reply body without parsing it.
1066              
1067             May take two or three parameters. In two-parameter form it takes URL
1068             to PUT and body. In three-parameter, it takes URL, additional URL
1069             params (to be added to URI), and body.
1070              
1071             Body may be specified in the same way as in L</post>: as scalar, scalar
1072             reference, or as hash reference which would be urlencoded.
1073              
1074             =head2 put_xml
1075              
1076             my $reply = $ua->put($url, "<xml>content</xml>");
1077             my $reply = $ua->put($url, { url=>args, ...}, "<xml>content</xml>");
1078             my $reply = $ua->put($url, $ref_to_xml_content);
1079             my $reply = $ua->put($url, { url=>args, ...}, $ref_to_xml_content);
1080              
1081             Shortcut. Make OAuth-signed PUT request, ensure request succeeded and
1082             return reply body without parsing it.
1083              
1084             May take two or three parameters. In two-parameter form it takes URL
1085             to PUT and body. In three-parameter, it takes URL, additional URL
1086             params (to be added to URI), and body.
1087              
1088             This is very close to L</put> (XML is neither rendered, nor parsed here),
1089             used mostly to set proper content-type and to clearly signal intent in the code.
1090              
1091             =head2 put_json
1092              
1093             my $reply = $oa->put_json($url, { json=>args, ... });
1094             my $reply = $oa->put_json($url, { url=>args, ...}, { json=>args, ... });
1095             my $reply = $oa->put_json($url, "json content");
1096             my $reply = $oa->put_json($url, { url=>args, ...}, "json content");
1097             # $reply is hash or arrayref constructed by parsing output
1098              
1099             Make OAuth-signed PUT request. Parameter is formatted as JSON, result
1100             also i parsed as JSON.
1101              
1102             May take two or three parameters. In two-parameter form it takes URL
1103             and JSON body. In three-parameter, it takes URL, additional URL params
1104             (to be added to URI), and JSON body.
1105              
1106             JSON body may be specified just as in L</post_json>: as hash or array
1107             reference (to be serialized) or as scalar or scalar reference
1108             (treated as already serialized).
1109              
1110             Example:
1111              
1112             my $result = $oauthomatic->put_json(
1113             "https://some.api/things/prettything", {
1114             mode => 'simple',
1115             }, {
1116             name => "Pretty Thingy",
1117             description => "This is very pretty",
1118             tags => ['secret', 'pretty', 'most-important'],
1119             }, count => 4);
1120             # PUTs to https://some.api/things/prettything?mode=simple
1121             # the following body (formatting and ordering may be different):
1122             # {
1123             # "name": "Pretty Thingy",
1124             # "description": "This is very pretty",
1125             # "tags": ['secret', 'pretty', 'most-important'],
1126             # }
1127              
1128             =head2 delete_
1129              
1130             $oa->delete_($url);
1131             $oa->delete_($url, {url => args, ...});
1132              
1133             Shortcut. Executes C<DELETE> on given URL. Note trailing underscore in the name
1134             (to avoid naming conflict with core perl function).
1135              
1136             Returns reply body content, if any.
1137              
1138             =head1 ATTRIBUTES
1139              
1140             =head2 client_cred
1141              
1142             OAuth application identifiers - client_key and client_secret.
1143             As L<OAuthomatic::Types::ClientCred> object.
1144              
1145             Mostly used internally but can be of use if you need (or prefer) to
1146             use OAuthomatic only for initialization, but make actual calls using
1147             some other means.
1148              
1149             Note that you must call L</ensure_authorized> to bo be sure this object is set.
1150              
1151             =head2 token_cred
1152              
1153             OAuth application identifiers - access_token and access_token_secret.
1154             As L<OAuthomatic::Types::TokenCred> object.
1155              
1156             Mostly used internally but can be of use if you need (or prefer) to
1157             use OAuthomatic only for initialization, but make actual calls using
1158             some other means.
1159              
1160             Note that you must call L</ensure_authorized> to bo be sure this object is set.
1161              
1162             =head1 THANKS
1163              
1164             Keith Grennan, for writing L<Net::OAuth>, which this module uses to
1165             calculate and verify OAuth signatures.
1166              
1167             Simon Wistow, for writing L<Net::OAuth::Simple>, which inspired some
1168             parts of my module.
1169              
1170             E. Hammer-Lahav for well written and understandable RFC 5849.
1171              
1172             =head1 SOURCE REPOSITORY
1173              
1174             Source code is maintained in L<Mercurial|http://mercurial.selenic.com>
1175             repository at L<bitbucket.org/Mekk/perl-oauthomatic|https://bitbucket.org/Mekk/perl-oauthomatic>:
1176              
1177             hg clone https://bitbucket.org/Mekk/perl-oauthomatic
1178              
1179             See C<README-development.pod> in source distribution for info how to
1180             build module from source.
1181              
1182             =head1 ISSUE TRACKER
1183              
1184             Issues can be reported at:
1185              
1186             =over
1187              
1188             =item *
1189              
1190             L<Bitbucket issue tracker|https://bitbucket.org/Mekk/perl-oauthomatic/issues>
1191              
1192             =item *
1193              
1194             L<CPAN bug tracker|https://rt.cpan.org/Dist/Display.html?Queue=OAuthomatic>
1195              
1196             =back
1197              
1198             The former is slightly preferred but feel free using CPAN tracker if you find it more usable.
1199              
1200             =head1 AUTHOR
1201              
1202             Marcin Kasperski <Marcin.Kasperski@mekk.waw.pl>
1203              
1204             =head1 COPYRIGHT AND LICENSE
1205              
1206             This software is copyright (c) 2015 by Marcin Kasperski.
1207              
1208             This is free software; you can redistribute it and/or modify it under
1209             the same terms as the Perl 5 programming language system itself.
1210              
1211             =cut