File Coverage

lib/Google/Ads/Common/OAuth2ApplicationsHandler.pm
Criterion Covered Total %
statement 36 76 47.3
branch 0 6 0.0
condition 0 26 0.0
subroutine 12 17 70.5
pod 3 3 100.0
total 51 128 39.8


line stmt bran cond sub pod time code
1             # Copyright 2013, Google Inc. All Rights Reserved.
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14              
15             package Google::Ads::Common::OAuth2ApplicationsHandler;
16              
17 1     1   3463 use strict;
  1         7  
  1         116  
18 1     1   25 use warnings;
  1         2  
  1         104  
19 1     1   25 use version;
  1         5  
  1         19  
20 1         858 use base qw(Google::Ads::Common::OAuth2BaseHandler
21 1     1   245 Google::Ads::Common::OAuthApplicationsHandlerInterface);
  1         5  
22              
23             # The following needs to be on one line because CPAN uses a particularly hacky
24             # eval() to determine module versions.
25 1     1   24 use Google::Ads::Common::Constants; our $VERSION = ${Google::Ads::Common::Constants::VERSION};
  1         5  
  1         114  
26              
27 1     1   27 use Class::Std::Fast;
  1         7  
  1         32  
28 1     1   454 use HTTP::Request::Common;
  1         8  
  1         217  
29 1     1   20 use LWP::UserAgent;
  1         3  
  1         49  
30 1     1   13 use URI::Escape;
  1         6  
  1         197  
31              
32 1     1   18 use constant OAUTH2_BASE_URL => "https://accounts.google.com/o/oauth2";
  1         7  
  1         200  
33 1         845 use constant OAUTH2_TOKEN_INFO_URL =>
34 1     1   11 "https://www.googleapis.com/oauth2/v1/tokeninfo";
  1         9  
35              
36             # Class::Std-style attributes. Need to be kept in the same line.
37             # These need to go in the same line for older Perl interpreters to understand.
38             my %client_secret_of : ATTR(:name :default<>);
39             my %access_type_of : ATTR(:name :default);
40             my %prompt_of : ATTR(:name :default);
41             my %refresh_token_of : ATTR(:name :default<>);
42             my %redirect_uri_of :
43             ATTR(:name :default);
44             my %additional_scopes_of : ATTR(:name :default<>);
45              
46             # Methods from Google::Ads::Common::AuthHandlerInterface
47             sub initialize : CUMULATIVE(BASE FIRST) {
48 0     0 1   my ($self, $api_client, $properties) = @_;
49 0           my $ident = ident $self;
50              
51             $client_secret_of{$ident} = $properties->{oAuth2ClientSecret}
52 0   0       || $client_secret_of{$ident};
53             $access_type_of{$ident} = $properties->{oAuth2AccessType}
54 0   0       || $access_type_of{$ident};
55             $prompt_of{$ident} = $properties->{oAuth2ApprovalPrompt}
56 0   0       || $prompt_of{$ident};
57             $refresh_token_of{$ident} = $properties->{oAuth2RefreshToken}
58 0   0       || $refresh_token_of{$ident};
59             $redirect_uri_of{$ident} = $properties->{oAuth2RedirectUri}
60 0   0       || $redirect_uri_of{$ident};
61             $additional_scopes_of{$ident} = $properties->{oAuth2AdditionalScopes}
62 0   0       || $additional_scopes_of{$ident};
63 1     1   13 }
  1         5  
  1         27  
64              
65             # Methods from Google::Ads::Common::OAuthHandlerInterface
66             sub get_authorization_url {
67 0     0 1   my ($self, $state) = @_;
68              
69 0   0       $state ||= "";
70 0           my ($client_id, $redirect_uri, $access_type, $prompt) = (
71             $self->get_client_id(), $self->get_redirect_uri(),
72             $self->get_access_type(), $self->get_prompt());
73              
74 0           return OAUTH2_BASE_URL . "/auth?response_type=code" . "&client_id=" .
75             uri_escape($client_id) . "&redirect_uri=" . $redirect_uri . "&scope=" .
76             $self->_formatted_scopes() . "&access_type=" . $access_type .
77             "&prompt=" . $prompt . "&state=" . uri_escape($state);
78             }
79              
80             sub issue_access_token {
81 0     0 1   my ($self, $authorization_code) = @_;
82              
83 0           my $body =
84             "code=" . uri_escape($authorization_code) . "&client_id=" .
85             uri_escape($self->get_client_id()) . "&client_secret=" .
86             uri_escape($self->get_client_secret()) . "&redirect_uri=" .
87             uri_escape($self->get_redirect_uri()) . "&grant_type=authorization_code";
88              
89 0           push my @headers, "Content-Type" => "application/x-www-form-urlencoded";
90 0           my $request =
91             HTTP::Request->new("POST", OAUTH2_BASE_URL . "/token", \@headers, $body);
92 0           my $res = $self->get___user_agent()->request($request);
93              
94 0 0         if (!$res->is_success()) {
95 0           return $res->decoded_content();
96             }
97              
98 0           my $content_hash = $self->__parse_auth_response($res->decoded_content());
99              
100 0           $self->set_access_token($content_hash->{access_token});
101 0           $self->set_refresh_token($content_hash->{refresh_token});
102 0           $self->set_access_token_expires(time + $content_hash->{expires_in});
103              
104 0           return undef;
105             }
106              
107             # Internal methods
108              
109             sub _refresh_access_token {
110 0     0     my $self = shift;
111              
112 0 0 0       if (
      0        
113             !(
114             $self->get_client_id()
115             && $self->get_client_secret()
116             && $self->get_refresh_token()))
117             {
118 0           return 0;
119             }
120              
121 0           my $body =
122             "refresh_token=" . uri_escape($self->get_refresh_token()) .
123             "&client_id=" . uri_escape($self->get_client_id()) . "&client_secret=" .
124             uri_escape($self->get_client_secret()) . "&grant_type=refresh_token";
125              
126 0           push my @headers, "Content-Type" => "application/x-www-form-urlencoded";
127 0           my $request =
128             HTTP::Request->new("POST", OAUTH2_BASE_URL . "/token", \@headers, $body);
129 0           my $res = $self->get___user_agent()->request($request);
130              
131 0 0         if (!$res->is_success()) {
132 0           warn($res->decoded_content());
133 0           return 0;
134             }
135              
136 0           my $content_hash = $self->__parse_auth_response($res->decoded_content());
137              
138 0           $self->set_access_token($content_hash->{access_token});
139 0           $self->set_access_token_expires(time + $content_hash->{expires_in});
140              
141 0           return 1;
142             }
143              
144             sub _formatted_scopes {
145 0     0     my $self = shift;
146 0           die "Need to be implemented by subclass";
147             }
148              
149             1;
150              
151             =pod
152              
153             =head1 NAME
154              
155             Google::Ads::Common::OAuth2ApplicationsHandler
156              
157             =head1 DESCRIPTION
158              
159             A generic abstract implementation of L
160             that supports OAuth2 for Web/Install Applications semantics.
161              
162             It is meant to be specialized and its L<_scope> and L<_formatted_scopes> methods
163             be properly implemented.
164              
165             =head1 ATTRIBUTES
166              
167             Each of these attributes can be set via
168             Google::Ads::Common::OAuth2ApplicationsHandler->new().
169              
170             Alternatively, there is a get_ and set_ method associated with each attribute
171             for retrieving or setting them dynamically.
172              
173             =head2 api_client
174              
175             A reference to the API client used to send requests.
176              
177             =head2 client_id
178              
179             OAuth2 client id obtained from the Google APIs Console.
180              
181             =head2 client_secret
182              
183             OAuth2 client secret obtained from the Google APIs Console.
184              
185             =head2 access_type
186              
187             OAuth2 access type to be requested when following the authorization flow. It
188             defaults to offline but it can be set to online.
189              
190             =head2 prompt
191              
192             OAuth2 prompt to be used when following the authorization flow. It
193             defaults to consent.
194              
195             =head2 redirect_uri
196              
197             Redirect URI as set for you in the Google APIs console, to which the
198             authorization flow will callback with the verification code. Defaults to
199             urn:ietf:wg:oauth:2.0:oob for the installed applications flow.
200              
201             =head2 access_token
202              
203             Stores an OAuth2 access token after the authorization flow is followed or for
204             you to manually set it in case you had it previously stored.
205             If this is manually set this handler will verify its validity before preparing
206             a request.
207              
208             =head2 refresh_token
209              
210             Stores an OAuth2 refresh token in case of an offline L is
211             requested. It is automatically used by the handler to request new access tokens
212             i.e. when they expire or found invalid.
213              
214             =head2 additional_scopes
215              
216             Stores additional OAuth2 scopes as a comma-separated string.
217             The scope defines which services the tokens
218             are allowed to access e.g. https://www.googleapis.com/auth/analytics
219              
220             =head1 METHODS
221              
222             =head2 initialize
223              
224             Initializes the handler with properties such as the client_id and
225             client_secret to use for generating authorization requests.
226              
227             =head3 Parameters
228              
229             =over
230              
231             =item *
232              
233             A required I with a reference to the API client object handling the
234             requests against the API.
235              
236             =item *
237              
238             A hash reference with the following keys:
239             {
240             # Refer to the documentation of the L property.
241             oAuth2ClientId => "client-id",
242             # Refer to the documentation of the L property.
243             oAuth2ClientSecret => "client-secret",
244             # Refer to the documentation of the L property.
245             oAuth2AccessType => "access-type",
246             # Refer to the documentation of the L property.
247             oAuth2ApprovalPrompt => "approval-prompt",
248             # Refer to the documentation of the L property.
249             oAuth2AccessToken => "access-token",
250             # Refer to the documentation of the L property.
251             oAuth2RefreshToken => "refresh-token",
252             # Refer to the documentation of the L property.
253             oAuth2RedirectUri => "secret",
254             }
255              
256             =back
257              
258             =head2 is_auth_enabled
259              
260             Refer to L documentation of this
261             method.
262              
263             =head2 prepare_request
264              
265             Refer to L documentation of this
266             method.
267              
268             =head2 get_authorization_url
269              
270             Refer to L documentation
271             of this method.
272              
273             =head2 issue_access_token
274              
275             Refer to L documentation
276             of this method.
277              
278             =head1 LICENSE AND COPYRIGHT
279              
280             Copyright 2013 Google Inc.
281              
282             Licensed under the Apache License, Version 2.0 (the "License");
283             you may not use this file except in compliance with the License.
284             You may obtain a copy of the License at
285              
286             http://www.apache.org/licenses/LICENSE-2.0
287              
288             Unless required by applicable law or agreed to in writing, software
289             distributed under the License is distributed on an "AS IS" BASIS,
290             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
291             See the License for the specific language governing permissions and
292             limitations under the License.
293              
294             =head1 REPOSITORY INFORMATION
295              
296             $Rev: $
297             $LastChangedBy: $
298             $Id: $
299              
300             =cut