File Coverage

blib/lib/OAuth/Lite2/Client/WebServer.pm
Criterion Covered Total %
statement 43 45 95.5
branch n/a
condition n/a
subroutine 15 15 100.0
pod n/a
total 58 60 96.6


line stmt bran cond sub pod time code
1             package OAuth::Lite2::Client::WebServer;
2 1     1   2193 use strict;
  1         2  
  1         26  
3 1     1   5 use warnings;
  1         1  
  1         24  
4 1     1   6 use base 'Class::ErrorHandler';
  1         2  
  1         68  
5 1     1   5 use bytes ();
  1         2  
  1         13  
6              
7 1     1   5 use URI;
  1         2  
  1         13  
8 1     1   5 use Carp ();
  1         10  
  1         14  
9 1     1   5 use Try::Tiny qw/try catch/;
  1         2  
  1         59  
10 1     1   5 use LWP::UserAgent;
  1         1  
  1         18  
11 1     1   4 use MIME::Base64 qw(encode_base64);
  1         2  
  1         35  
12 1     1   4 use HTTP::Request;
  1         1  
  1         19  
13 1     1   4 use HTTP::Headers;
  1         1  
  1         16  
14 1     1   4 use Params::Validate qw(HASHREF);
  1         2  
  1         35  
15 1     1   5 use OAuth::Lite2;
  1         2  
  1         14  
16 1     1   4 use OAuth::Lite2::Util qw(build_content);
  1         2  
  1         47  
17 1     1   36 use OAuth::Lite2::Client::TokenResponseParser;
  0            
  0            
18             use OAuth::Lite2::Client::StateResponseParser;
19              
20             =head1 NAME
21              
22             OAuth::Lite2::Client::WebServer - OAuth 2.0 Web Server Profile Client
23              
24             =head1 SYNOPSIS
25              
26             my $client = OAuth::Lite2::Client::WebServer->new(
27             id => q{my_client_id},
28             secret => q{my_client_secret},
29             authorize_uri => q{http://example.org/authorize},
30             access_token_uri => q{http://example.org/token},
31             );
32              
33             # redirect user to authorize page.
34             sub start_authorize {
35             my $your_app = shift;
36             my $redirect_url = $client->uri_to_redirect(
37             redirect_uri => q{http://yourapp/callback},
38             scope => q{photo},
39             state => q{optional_state},
40             );
41              
42             $your_app->res->redirect( $redirect_url );
43             }
44              
45             # this method corresponds to the url 'http://yourapp/callback'
46             sub callback {
47             my $your_app = shift;
48              
49             my $code = $your_app->request->param("code");
50              
51             my $access_token = $client->get_access_token(
52             code => $code,
53             redirect_uri => q{http://yourapp/callback},
54             ) or return $your_app->error( $client->errstr );
55              
56             $your_app->store->save( access_token => $access_token->access_token );
57             $your_app->store->save( expires_at => time() + $access_token->expires_in );
58             $your_app->store->save( refresh_token => $access_token->refresh_token );
59             }
60              
61             sub refresh_access_token {
62             my $your_app = shift;
63              
64             my $access_token = $client->refresh_access_token(
65             refresh_token => $refresh_token,
66             ) or return $your_app->error( $client->errstr );
67              
68             $your_app->store->save( access_token => $access_token->access_token );
69             $your_app->store->save( expires_at => time() + $access_token->expires_in );
70             $your_app->store->save( refresh_token => $access_token->refresh_token );
71             }
72              
73              
74             sub access_to_protected_resource {
75             my $your_app = shift;
76              
77             my $access_token = $your_app->store->get("access_token");
78             my $expires_at = $your_app->store->get("expires_at");
79             my $refresh_token = $your_app->store->get("refresh_token");
80              
81             unless ($access_token) {
82             $your_app->start_authorize();
83             return;
84             }
85              
86             if ($expires_at < time()) {
87             $your_app->refresh_access_token();
88             return;
89             }
90              
91             my $req = HTTP::Request->new( GET => q{http://example.org/photo} );
92             $req->header( Authorization => sprintf(q{OAuth %s}, $access_token) );
93             my $agent = LWP::UserAgent->new;
94             my $res = $agent->request($req);
95             ...
96             }
97              
98              
99             =head1 DESCRIPTION
100              
101             Client library for OAuth 2.0 Web Server Profile.
102              
103             =head1 METHODS
104              
105             =head2 new( %params )
106              
107             =over 4
108              
109             =item id
110              
111             Client ID
112              
113             =item secret
114              
115             Client secret
116              
117             =item authorize_uri
118              
119             authorization page uri on auth-server.
120              
121             =item access_token_uri
122              
123             token endpoint uri on auth-server.
124              
125             =item refresh_token_uri
126              
127             refresh-token endpoint uri on auth-server.
128             if you omit this, access_token_uri is used instead.
129              
130             =item agent
131              
132             user agent. if you omit this, LWP::UserAgent's object is set by default.
133             You can use your custom agent or preset-agents.
134              
135             See also
136              
137             L
138             L
139             L
140              
141             =back
142              
143             =cut
144              
145             sub new {
146              
147             my $class = shift;
148              
149             my %args = Params::Validate::validate(@_, {
150             id => 1,
151             secret => 1,
152             # format => { optional => 1 },
153             authorize_uri => { optional => 1 },
154             access_token_uri => { optional => 1 },
155             refresh_token_uri => { optional => 1 },
156             agent => { optional => 1 },
157             });
158              
159             my $self = bless {
160             id => undef,
161             secret => undef,
162             authorize_uri => undef,
163             access_token_uri => undef,
164             refresh_token_uri => undef,
165             last_request => undef,
166             last_response => undef,
167             %args,
168             }, $class;
169              
170             unless ($self->{agent}) {
171             $self->{agent} = LWP::UserAgent->new;
172             $self->{agent}->agent(
173             join "/", __PACKAGE__, $OAuth::Lite2::VERSION);
174             }
175              
176             $self->{format} ||= 'json';
177             $self->{response_parser} = OAuth::Lite2::Client::TokenResponseParser->new;
178             $self->{state_response_parser} = OAuth::Lite2::Client::StateResponseParser->new;
179              
180             return $self;
181             }
182              
183             =head2 uri_to_redirect( %params )
184              
185             =cut
186              
187             sub uri_to_redirect {
188             my $self = shift;
189             my %args = Params::Validate::validate(@_, {
190             redirect_uri => 1,
191             state => { optional => 1 },
192             scope => { optional => 1 },
193             immediate => { optional => 1 },
194             uri => { optional => 1 },
195             extra => { optional => 1, type => HASHREF },
196             });
197              
198             my %params = (
199             response_type => 'code',
200             client_id => $self->{id},
201             redirect_uri => $args{redirect_uri},
202             );
203             $params{state} = $args{state} if $args{state};
204             $params{scope} = $args{scope} if $args{scope};
205             $params{immediate} = $args{immediate} if $args{immediate};
206              
207             if ($args{extra}) {
208             for my $key ( keys %{$args{extra}} ) {
209             $params{$key} = $args{extra}{$key};
210             }
211             }
212              
213             my $uri = $args{uri}
214             || $self->{authorize_uri}
215             || Carp::croak "uri not found";
216              
217             $uri = URI->new($uri);
218             $uri->query_form(%params);
219             return $uri->as_string;
220             }
221              
222             =head2 get_access_token( %params )
223              
224             execute verification,
225             and returns L object.
226              
227             =over 4
228              
229             =item code
230              
231             Authorization-code that is issued beforehand by server
232              
233             =item redirect_uri
234              
235             The URL that has used for user authorization's callback
236              
237             =back
238              
239             =cut
240              
241             sub get_access_token {
242             my $self = shift;
243              
244             my %args = Params::Validate::validate(@_, {
245             code => 1,
246             redirect_uri => 1,
247             server_state => { optional => 1 },
248             uri => { optional => 1 },
249             use_basic_schema => { optional => 1 },
250             # secret_type => { optional => 1 },
251             # format => { optional => 1 },
252             });
253              
254             unless (exists $args{uri}) {
255             $args{uri} = $self->{access_token_uri}
256             || Carp::croak "uri not found";
257             }
258              
259             # $args{format} ||= $self->{format};
260              
261             my %params = (
262             grant_type => 'authorization_code',
263             code => $args{code},
264             redirect_uri => $args{redirect_uri},
265             # format => $args{format},
266             );
267             $params{server_state} = $args{server_state} if $args{server_state};
268              
269             unless ($args{use_basic_schema}){
270             $params{client_id} = $self->{id};
271             $params{client_secret} = $self->{secret};
272             }
273              
274             # $params{secret_type} = $args{secret_type}
275             # if $args{secret_type};
276              
277             my $content = build_content(\%params);
278             my $headers = HTTP::Headers->new;
279             $headers->header("Content-Type" => q{application/x-www-form-urlencoded});
280             $headers->header("Content-Length" => bytes::length($content));
281             $headers->authorization_basic($self->{id}, $self->{secret})
282             if($args{use_basic_schema});
283             my $req = HTTP::Request->new( POST => $args{uri}, $headers, $content );
284              
285             my $res = $self->{agent}->request($req);
286             $self->{last_request} = $req;
287             $self->{last_response} = $res;
288              
289             my ($token, $errmsg);
290             try {
291             $token = $self->{response_parser}->parse($res);
292             } catch {
293             $errmsg = "$_";
294             };
295             return $token || $self->error($errmsg);
296             }
297              
298             =head2 refresh_access_token( %params )
299              
300             Refresh access token by refresh_token,
301             returns L object.
302              
303             =over 4
304              
305             =item refresh_token
306              
307             =back
308              
309             =cut
310              
311             sub refresh_access_token {
312             my $self = shift;
313              
314             my %args = Params::Validate::validate(@_, {
315             refresh_token => 1,
316             # secret_type => { optional => 1 },
317             # format => { optional => 1 },
318             uri => { optional => 1 },
319             use_basic_schema => { optional => 1 },
320             });
321              
322             unless (exists $args{uri}) {
323             $args{uri} = $self->{access_token_uri}
324             || Carp::croak "uri not found";
325             }
326              
327             # $args{format} ||= $self->{format};
328              
329             my %params = (
330             grant_type => 'refresh_token',
331             refresh_token => $args{refresh_token},
332             # format => $args{format},
333             );
334              
335             unless ($args{use_basic_schema}){
336             $params{client_id} = $self->{id};
337             $params{client_secret} = $self->{secret};
338             }
339              
340             # $params{secret_type} = $args{secret_type}
341             # if $args{secret_type};
342              
343             my $content = build_content(\%params);
344             my $headers = HTTP::Headers->new;
345             $headers->header("Content-Type" => q{application/x-www-form-urlencoded});
346             $headers->header("Content-Length" => bytes::length($content));
347             $headers->authorization_basic($self->{id}, $self->{secret})
348             if($args{use_basic_schema});
349             my $req = HTTP::Request->new( POST => $args{uri}, $headers, $content );
350              
351             my $res = $self->{agent}->request($req);
352             $self->{last_request} = $req;
353             $self->{last_response} = $res;
354              
355             my ($token, $errmsg);
356             try {
357             $token = $self->{response_parser}->parse($res);
358             } catch {
359             $errmsg = "$_";
360             };
361             return $token || $self->error($errmsg);
362              
363             }
364              
365             =head2 get_server_state
366              
367             Obtain L object.
368              
369             =cut
370              
371             sub get_server_state {
372             my $self = shift;
373              
374             my %args = Params::Validate::validate(@_, {
375             uri => { optional => 1 },
376             });
377              
378             unless (exists $args{uri}) {
379             $args{uri} = $self->{access_token_uri}
380             || Carp::croak "uri not found";
381             }
382              
383             my %params = (
384             grant_type => 'server_state',
385             client_id => $self->{id},
386             );
387              
388             my $content = build_content(\%params);
389             my $headers = HTTP::Headers->new;
390             $headers->header("Content-Type" => q{application/x-www-form-urlencoded});
391             $headers->header("Content-Length" => bytes::length($content));
392             my $req = HTTP::Request->new( POST => $args{uri}, $headers, $content );
393              
394             my $res = $self->{agent}->request($req);
395             $self->{last_request} = $req;
396             $self->{last_response} = $res;
397              
398             my ($state, $errmsg);
399             try {
400             $state = $self->{state_response_parser}->parse($res);
401             } catch {
402             $errmsg = "$_";
403             };
404             return $state || $self->error($errmsg);
405              
406             }
407              
408             =head2 last_request
409              
410             Returns a HTTP::Request object that is used
411             when you obtain or refresh access token last time internally.
412              
413             =head2 last_request
414              
415             Returns a HTTP::Response object that is used
416             when you obtain or refresh access token last time internally.
417              
418             =cut
419              
420             sub last_request { $_[0]->{last_request} }
421             sub last_response { $_[0]->{last_response} }
422              
423             =head1 AUTHOR
424              
425             Ryo Ito, Eritou.06@gmail.comE
426              
427             Lyo Kato, Elyo.kato@gmail.comE
428              
429             =head1 COPYRIGHT AND LICENSE
430              
431             Copyright (C) 2010 by Lyo Kato
432              
433             This library is free software; you can redistribute it and/or modify
434             it under the same terms as Perl itself, either Perl version 5.8.8 or,
435             at your option, any later version of Perl 5 you may have available.
436              
437             =cut
438              
439             1;