File Coverage

blib/lib/Net/Facebook/Oauth2.pm
Criterion Covered Total %
statement 80 136 58.8
branch 24 76 31.5
condition 3 32 9.3
subroutine 14 19 73.6
pod 8 10 80.0
total 129 273 47.2


line stmt bran cond sub pod time code
1             package Net::Facebook::Oauth2;
2              
3 2     2   58388 use strict;
  2         8  
  2         51  
4 2     2   8 use warnings;
  2         3  
  2         42  
5 2     2   1098 use LWP::UserAgent;
  2         101014  
  2         63  
6 2     2   17 use URI;
  2         3  
  2         39  
7 2     2   9 use URI::Escape;
  2         4  
  2         202  
8 2     2   1212 use JSON::MaybeXS;
  2         12229  
  2         161  
9 2     2   14 use Carp;
  2         2  
  2         3299  
10              
11             our $VERSION = '0.11';
12              
13             sub new {
14 7     7 1 154681 my ($class,%options) = @_;
15 7         22 my $self = {};
16 7         30 $self->{options} = \%options;
17              
18 7 50       30 my $api_version = defined $options{api_version} ? $options{api_version} : 'v3.1';
19              
20 7 100       33 if (!defined $options{access_token}){
21 3 100       13 croak "You must provide your application id in new()\nNet::Facebook::Oauth2->new( application_id => '...' )" unless defined $self->{options}->{application_id};
22 2 100       18 croak "You must provide your application secret in new()\nNet::Facebook::Oauth2->new( application_secret => '...' )" unless defined $self->{options}->{application_secret};
23             }
24              
25 5 50       16 if (defined $options{access_token_url}) {
26 0 0       0 croak "cannot pass access_token_url AND api_version" if defined $options{api_version};
27 0         0 $self->{access_token_url} = $options{access_token_url};
28             }
29             else {
30 5         15 $self->{access_token_url} = "https://graph.facebook.com/$api_version/oauth/access_token";
31             }
32              
33 5 50       14 if (defined $options{authorize_url}) {
34 0 0       0 croak "cannot pass authorize_url AND api_version" if defined $options{api_version};
35 0         0 $self->{authorize_url} = $options{authorize_url};
36             }
37             else {
38 5         16 $self->{authorize_url} = "https://www.facebook.com/$api_version/dialog/oauth";
39             }
40              
41 5 50       14 if (defined $options{debug_token_url}) {
42 0 0       0 croak "cannot pass debug_token_url AND api_version" if defined $options{api_version};
43 0         0 $self->{debug_token_url} = $options{debug_token_url};
44             }
45             else {
46 5         34 $self->{debug_token_url} = "https://graph.facebook.com/$api_version/debug_token";
47             }
48              
49 5   66     300 $self->{browser} = $options{browser} || LWP::UserAgent->new;
50 5   50     10121 $self->{display} = $options{display} || 'page'; ## other values popup and wab
51 5         10 $self->{access_token} = $options{access_token};
52              
53 5         28 return bless($self, $class);
54             }
55              
56             sub get_authorization_url {
57 0     0 1 0 my ($self,%params) = @_;
58              
59 0   0     0 $params{callback} ||= $self->{options}->{callback};
60 0 0       0 croak "You must pass a callback parameter with Oauth v2.0" unless defined $params{callback};
61              
62 0 0       0 $params{display} = $self->{display} unless defined $params{display};
63 0         0 $self->{options}->{callback} = $params{callback};
64              
65             my $url = $self->{authorize_url}
66             ."?client_id="
67             .uri_escape($self->{options}->{application_id})
68             ."&redirect_uri="
69 0         0 .uri_escape($params{callback});
70              
71 0 0       0 if ($params{scope}) {
72 0         0 my $scope = join(',', @{$params{scope}});
  0         0  
73 0 0       0 $url .= '&scope=' . $scope if $scope;
74             }
75             # state is now required:
76 0 0       0 $url .= '&state=' . (defined $params{state} ? $params{state} : time);
77              
78 0 0       0 $url .= '&response_type=' . $params{response_type} if $params{response_type};
79 0 0       0 $url .= '&auth_type=' . $params{auth_type} if $params{auth_type};
80 0         0 $url .= "&display=".$params{display};
81              
82 0         0 return $url;
83             }
84              
85              
86             sub get_access_token {
87 0     0 1 0 my ($self,%params) = @_;
88 0   0     0 $params{callback} ||= $self->{options}->{callback};
89 0   0     0 $params{code} ||= $self->{options}->{code};
90              
91 0 0       0 croak "You must pass a code parameter with Oauth v2.0" unless defined $params{code};
92 0 0       0 croak "You must pass callback URL" unless defined $params{callback};
93 0         0 $self->{options}->{code} = $params{code};
94              
95             ###generating access token URL
96             my $getURL = $self->{access_token_url}
97             ."?client_id="
98             .uri_escape($self->{options}->{application_id})
99             ."&redirect_uri="
100             .uri_escape($params{callback})
101             ."&client_secret="
102             .uri_escape($self->{options}->{application_secret})
103 0         0 ."&code=$params{code}";
104              
105 0         0 my $response = $self->{browser}->get($getURL);
106 0         0 my $json = decode_json($response->content());
107              
108 0 0 0     0 if (!$response->is_success || exists $json->{error}){
    0          
109             ##got an error response from facebook. die and display error message
110 0         0 croak "'" . $json->{error}->{type}. "'" . " " .$json->{error}->{message};
111             }
112             elsif ($json->{access_token}) {
113             ##everything is ok proccess response and extract access token
114 0         0 return $self->{access_token} = $json->{access_token};
115             }
116             else {
117 0         0 croak "can't get access token from " . $response->content();
118             }
119             }
120              
121             sub get_long_lived_token {
122 0     0 1 0 my ($self,%params) = @_;
123              
124 0 0       0 if (!$self->{access_token}) {
125 0 0       0 croak "You must pass the access_token" unless defined $params{access_token};
126             }
127              
128             my $getURL = $self->{access_token_url}
129             . '?grant_type=fb_exchange_token'
130             . '&client_id=' . uri_escape($self->{options}->{application_id})
131             . '&client_secret=' . uri_escape($self->{options}->{application_secret})
132             . '&fb_exchange_token=' . uri_escape($self->{access_token} || $params{access_token})
133 0   0     0 ;
134 0         0 my $response = $self->{browser}->get($getURL);
135 0         0 my $json = decode_json($response->content());
136 0 0 0     0 if (!$response->is_success || exists $json->{error}) {
    0          
137 0         0 croak "'" . $json->{error}->{type}. "'" . " " .$json->{error}->{message};
138             }
139             elsif ($json->{access_token}) {
140 0         0 return $self->{access_token} = $json->{access_token};
141             }
142             else {
143 0         0 croak "can't get long lived access token from " . $response->content();
144             }
145             }
146              
147             sub debug_token {
148 0     0 1 0 my ($self,%params) = @_;
149             croak "You must pass the 'input' access token to inspect"
150 0 0       0 unless defined $params{input};
151              
152             # NOTE: according to FB's documentation, instead of passing an
153             # app token we can simply pass access_token=app_id|app_secret:
154             # https://developers.facebook.com/docs/facebook-login/access-tokens/#apptokens
155             my $getURL = $self->{debug_token_url}
156             . "?input_token=" . uri_escape($params{input})
157             . "&access_token="
158             . join('|',
159             uri_escape($self->{options}->{application_id}),
160             uri_escape($self->{options}->{application_secret})
161 0         0 );
162              
163 0         0 my $response = $self->{browser}->get($getURL);
164 0         0 my $json = decode_json($response->content());
165 0 0 0     0 if (!$response->is_success || exists $json->{error}){
    0 0        
    0 0        
166             ##got an error response from facebook. die and display error message
167 0         0 croak "'" . $json->{error}->{type}. "'" . " " .$json->{error}->{message};
168             }
169             elsif (!exists $json->{data}->{app_id} || !exists $json->{data}->{user_id}) {
170 0         0 return;
171             }
172             elsif (!$params{skip_check} && (''.$json->{data}->{app_id}) ne (''.$self->{options}->{application_id}) ) {
173 0         0 return;
174             }
175 0         0 return $json->{data};
176             }
177              
178              
179             sub get {
180 7     7 1 86 my ($self,$url,$params) = @_;
181 7 100       22 unless ($self->_has_access_token($url)) {
182 5 50       18 croak "You must pass access_token" unless defined $self->{access_token};
183 5 100       19 $url .= $self->{_has_query} ? '&' : '?';
184 5         11 $url .= "access_token=" . $self->{access_token};
185             }
186              
187             ##construct the new url
188 7         14 my @array;
189              
190 7         11 while ( my ($key, $value) = each(%{$params})){
  12         44  
191 5         27 $value = uri_escape($value);
192 5         284 push(@array, "$key=$value");
193             }
194              
195 7         18 my $string = join('&', @array);
196 7 100       24 $url .= "&".$string if $string;
197              
198 7         33 my $response = $self->{browser}->get($url);
199 7         88078 my $content = $response->content();
200 7         169 return $self->_content($content);
201             }
202              
203             sub post {
204 2     2 1 43 my ($self,$url,$params) = @_;
205 2 100       23 unless ($self->_has_access_token($url)) {
206 1 50       122 croak "You must pass access_token" unless defined $self->{access_token};
207 1         8 $params->{access_token} = $self->{access_token};
208             }
209 2         19 my $response = $self->{browser}->post($url,$params);
210 2         123192 my $content = $response->content();
211 2         33 return $self->_content($content);
212             }
213              
214             sub delete {
215 1     1 1 6 my ($self,$url,$params) = @_;
216 1 50       4 unless ($self->_has_access_token($url)) {
217 1 50       4 croak "You must pass access_token" unless defined $self->{access_token};
218 1         3 $params->{access_token} = $self->{access_token};
219             }
220 1         7 my $response = $self->{browser}->delete($url,$params);
221 1         60 my $content = $response->content();
222 1         48 return $self->_content($content);
223             }
224              
225             sub as_hash {
226 0     0 0 0 my ($self) = @_;
227 0         0 return decode_json($self->{content});
228             }
229              
230             sub as_json {
231 3     3 0 5 my ($self) = @_;
232 3         11 return $self->{content};
233             }
234              
235             sub _content {
236 10     10   254 my ($self,$content) = @_;
237 10         23 $self->{content} = $content;
238 10         235 return $self;
239             }
240              
241             sub _has_access_token {
242 10     10   107 my ($self, $url) = @_;
243 10         68 my $uri = URI->new($url);
244 10         20304 my %q = $uri->query_form;
245             #also check if we have a query and save result
246 10         1533 $self->{_has_query} = $uri->query();
247 10 100       123 if (grep { $_ eq 'access_token' } keys %q) {
  5         20  
248 3         15 return 1;
249             }
250 7         35 return;
251             }
252              
253             1;
254             __END__