File Coverage

lib/Google/RestApi/Auth/OAuth2Client.pm
Criterion Covered Total %
statement 27 74 36.4
branch 0 14 0.0
condition 1 2 50.0
subroutine 7 14 50.0
pod 1 7 14.2
total 36 111 32.4


line stmt bran cond sub pod time code
1             package Google::RestApi::Auth::OAuth2Client;
2              
3             our $VERSION = '1.0.4';
4              
5 1     1   811 use Google::RestApi::Setup;
  1         2  
  1         50  
6              
7             # this was taken from Net::Google::DataAPI::Auth::OAuth2 and had a moose-ectomy. this will
8             # get rid of warnings about switching to Moo instead of Moose::Any.
9             #
10             # NOTE NOTE NOTE: to generate a config file and token file for use by this module, see:
11             # bin/google_restapi_oauth_token_creator in this package.
12              
13 1     1   15125 use Net::OAuth2::Client ();
  1         74114  
  1         29  
14 1     1   11 use Net::OAuth2::Profile::WebServer ();
  1         4  
  1         23  
15 1     1   5 use Storable qw( retrieve );
  1         2  
  1         45  
16 1     1   6 use URI ();
  1         3  
  1         20  
17              
18 1     1   7 use parent 'Google::RestApi::Auth';
  1         3  
  1         11  
19              
20             sub new {
21 141     141 1 371 my $class = shift;
22              
23 141         760 my %p = @_;
24             # order is important, resolve the overall config file first.
25 141         680 resolve_config_file_path(\%p, 'config_file');
26 141         496 resolve_config_file_path(\%p, 'token_file');
27              
28 140         805 my $self = merge_config_file(%p);
29 140         373 state $check = compile_named(
30             config_dir => ReadableDir, { optional => 1 },
31             config_file => ReadableFile, { optional => 1 },
32             token_file => ReadableFile, { optional => 1 },
33             client_id => Str,
34             client_secret => Str,
35             scope => ArrayRef[Str], { optional => 1 },
36             state => Str, { default => '' },
37             redirect_uri => Str, { default => 'urn:ietf:wg:oauth:2.0:oob' },
38             site => Str, { default => 'https://accounts.google.com' },
39             authorize_path => Str, { default => '/o/oauth2/auth' },
40             access_token_path => Str, { default => '/o/oauth2/token' },
41             userinfo_url => Str, { default => 'https://www.googleapis.com/oauth2/v1/userinfo' },
42             );
43 140         12550 $self = $check->(%$self);
44              
45             $self->{scope} ||= [ # when added to default above, check silently fails to compile.
46 140   50     12784 'https://www.googleapis.com/auth/userinfo.profile',
47             'https://www.googleapis.com/auth/userinfo.email',
48             ];
49              
50 140         882 return bless $self, $class;
51             }
52              
53             sub headers {
54 0     0     my $self = shift;
55 0 0         return $self->{headers} if $self->{headers};
56              
57             $self->access_token(
58             refresh_token => retrieve($self->{token_file})->{refresh_token},
59 0           auto_refresh => 1,
60             );
61 0           $self->refresh_token();
62 0           my $access_token = $self->access_token()->access_token();
63 0           INFO("Successfully attained access token");
64              
65 0           $self->{headers} = [ Authorization => "Bearer $access_token" ];
66              
67 0           return $self->{headers};
68             }
69              
70             sub authorize_url {
71 0     0 0   my $self = shift;
72 0           my $server = $self->oauth2_webserver();
73             return $server->authorize(
74 0           scope => join(' ', @{ $self->{scope} }), @_
  0            
75             );
76             }
77              
78             sub access_token {
79 0     0 0   my $self = shift;
80              
81 0 0         if (scalar @_ == 1) {
    0          
82 0           state $check = compile(Str);
83 0           my ($code) = $check->(@_);
84 0           my $server = $self->oauth2_webserver();
85 0           $self->{access_token} = $server->get_access_token($code);
86 0           DEBUG("Created access token:\n", Dump($self->{access_token}));
87             } elsif (@_) {
88 0           state $check = compile_named(
89             refresh_token => Str,
90             auto_refresh => Bool,
91             );
92 0           my $p = $check->(@_);
93 0           $p->{profile} = $self->oauth2_webserver();
94             # DEBUG("Building access token from:\n", Dump($p)); # shows secret in the logs.
95 0           $self->{access_token} = Net::OAuth2::AccessToken->new(%$p);
96             }
97              
98 0           return $self->{access_token};
99             }
100              
101             sub refresh_token {
102 0     0 0   my ($self, $refresh_token) = @_;
103 0           DEBUG("About to refresh token");
104 0           my $server = $self->oauth2_webserver();
105 0           $server->update_access_token($self->access_token());
106 0           return $self->access_token()->refresh();
107             }
108              
109             sub oauth2_client {
110 0     0 0   my $self = shift;
111 0 0         if (!$self->{oauth2_client}) {
112 0           DEBUG("Creating OAuth2 client");
113             $self->{oauth2_client} = Net::OAuth2::Client->new(
114             $self->{client_id},
115             $self->{client_secret},
116             site => $self->{site},
117             authorize_path => $self->{authorize_path},
118             access_token_path => $self->{access_token_path},
119             refresh_token_path => $self->{access_token_path},
120 0           );
121             }
122 0           return $self->{oauth2_client};
123             }
124              
125             sub oauth2_webserver {
126 0     0 0   my $self = shift;
127 0 0         if (!$self->{oauth2_webserver}) {
128 0           DEBUG("Creating OAuth2 web server");
129 0           my $client = $self->oauth2_client();
130             $self->{oauth2_webserver} = $client->web_server(
131             redirect_uri => $self->{redirect_uri},
132             state => $self->{state},
133 0           );
134             }
135 0           return $self->{oauth2_webserver};
136             }
137              
138             # not currently used
139             sub userinfo {
140 0     0 0   my $self = shift;
141 0           my $token = $self->access_token();
142 0           my $url = URI->new($self->{userinfo_url});
143 0           my $res = $token->get($url);
144 0 0         $res->is_success or LOGDIE 'userinfo request failed: ' . $res->as_string;
145 0 0         my %res_params = $self->oauth2_webserver()->params_from_response($res)
146             or LOGDIE 'params_from_response for userinfo response failed';
147 0           return \%res_params;
148             }
149              
150             1;
151              
152             __END__
153              
154             =head1 NAME
155              
156             Google::RestApi::Auth::OAuth2Client - OAuth2 support for Google Rest APIs
157              
158             =head1 SYNOPSIS
159              
160             use Google::RestApi::Auth::OAuth2Client;
161              
162             my $oauth2 = Google::RestApi::Auth::OAuth2Client->new(
163             client_id => 'xxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
164             client_secret => 'mys3cr33333333333333t',
165             scope => ['http://spreadsheets.google.com/feeds/'],
166              
167             # with web apps, redirect_uri is needed:
168             # redirect_uri => 'http://your_app.sample.com/callback',
169             );
170             my $url = $oauth2->authorize_url();
171              
172             # you can add optional parameters:
173             my $url = $oauth2->authorize_url(
174             access_type => 'offline',
175             approval_prompt => 'force',
176             );
177              
178             # generate an access token from the code returned from Google:
179             my $token = $oauth2->access_token($code);
180            
181             =head1 DESCRIPTION
182              
183             Google::RestApi::Auth::OAuth2Client interacts with google OAuth 2.0 service
184             and creates the 'Authorization' header for use in Furl or LWP::UserAgent.
185              
186             To generate a config file and token file for use by this moudle, see:
187             bin/google_restapi_oauth_token_creator in this pacakage.
188              
189             This was copied from Net::Google::DataAPI::Auth::OAuth2 and modified
190             to fit this framework. The other framework was dated and produced
191             constant warnings to upgrade from Moose to Moo. I removed Moose since I
192             didn't use Moose anywhere else in this framework.
193              
194             =head1 ATTRIBUTES
195              
196             =head2 sub new
197              
198             =over 2
199              
200             config_file: Optional YAML configuration file that can specify any
201             or all of the following args:
202             client_id: The OAuth2 client id supplied by Google.
203             client_secret: The OAuth2 client secret supplied by Google.
204             token_file: The file path to the previously saved token (see OAUTH2
205             SETUP below). If a config_file is passed, the dirname of the config
206             file is tried to resolve the token_file (same directory) if the
207             directory portion is omitted.
208              
209             You can specify any of the arguments in the optional YAML config file.
210             Any passed in arguments will override what is in the config file.
211              
212             =item * client_id
213              
214             client id. You can get it at L<https://code.google.com/apis/console#access>.
215              
216             =item * client_secret
217              
218             The client secret paired with the client id.
219              
220             =item * scope
221              
222             URL identifying the service(s) to be accessed. You can see the list
223             of the urls to use at: L<http://code.google.com/intl/en-US/apis/gdata/faq.html#AuthScopes>
224              
225             =item * redirect_url
226              
227             OAuth2 redirect url. 'urn:ietf:wg:oauth:2.0:oob' will be used if you don't specify it.
228              
229             =back
230              
231             See L<https://developers.google.com/accounts/docs/OAuth2> for details.
232              
233             =head1 OAUTH2 SETUP
234              
235             This class depends on first creating an OAuth2 token session file
236             that you point to via the 'token_file' config param passed via 'new'.
237             See bin/google_restapi_oauth_token_creator and follow the instructions to
238             save your token file.
239              
240             =head1 AUTHOR
241              
242             Robin Murray E<lt>mvsjes@cpan.ork<gt>, copied and modifed from Net::Google::DataAPI::Auth::OAuth2.
243              
244             =head1 SEE ALSO
245              
246             L<OAuth2>
247              
248             L<Google::DataAPI::Auth::OAuth2>
249              
250             L<https://developers.google.com/accounts/docs/OAuth2>
251              
252             =head1 LICENSE
253              
254             This library is free software; you can redistribute it and/or modify
255             it under the same terms as Perl itself.