Skip to content

Commit

Permalink
Merge 7b51510 into a0dbcd6
Browse files Browse the repository at this point in the history
  • Loading branch information
oklahomer committed Mar 29, 2015
2 parents a0dbcd6 + 7b51510 commit 570ca9a
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 26 deletions.
34 changes: 23 additions & 11 deletions lib/Facebook/OpenGraph.pm
Expand Up @@ -269,9 +269,17 @@ sub _get_token {
# It, however, returnes no "expires" parameter on some edge cases.
# e.g. Your app requests manage_pages permission.
# https://developers.facebook.com/bugs/597779113651383/
if ($response->is_api_version_eq_or_later_than('v2.3')) {
# As of v2.3, to be compliant with RFC 6749, response is JSON formatted
# as described below.
# {"access_token": <TOKEN>, "token_type":<TYPE>, "expires_in":<TIME>}
# https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.3#confirm
return $response->as_hashref;
}

my $res_content = $response->content;
my $token_ref = +{ URI->new('?'.$res_content)->query_form };
croak qq{can't get access_token properly: $res_content}
my $token_ref = +{ URI->new("?$res_content")->query_form };
croak "can't get access_token properly: $res_content"
unless $token_ref->{access_token};

return $token_ref;
Expand Down Expand Up @@ -1062,29 +1070,31 @@ on your callback endpoint which is specified on C<eredirect_uri>. Give the
returning access token to C<set_access_token()> and you can act on behalf of
the user.
FYI: I<expires> is B<NOT> returned on some edge cases. The detail and schenario
should be found at L<https://developers.facebook.com/bugs/597779113651383/>.
FYI: I<expires> or I<expires_in> is B<NOT> returned on some edge cases. The
detail and reproductive scenario should be found at
L<https://developers.facebook.com/bugs/597779113651383/>.
# On OAuth callback page which you specified on $fb->redirect_uri.
my $req = Plack::Request->new($env);
my $token_ref = $fb->get_user_token_by_code($req->query_param('code'))
my $token_ref = $fb->get_user_token_by_code($req->query_param('code'));
my $access_token = $token_ref->{access_token};
my $expires = $token_ref->{expires};
my $expires = $token_ref->{expires}; # named expires_in as of v2.3
=head3 C<< $fb->get_user_token_by_cookie($cookie_value) >>
Obtain user access token based on the cookie value that is set by JS SDK.
Cookie name should be determined with C<js_cookie_name()>.
FYI: I<expires> is B<NOT> returned on some edge cases. The detail and schenario
should be found at L<https://developers.facebook.com/bugs/597779113651383/>.
FYI: I<expires> or I<expires_in> is B<NOT> returned on some edge cases. The
detail and reproductive schenario should be found at
L<https://developers.facebook.com/bugs/597779113651383/>.
if (my $cookie = $c->req->cookie( $fb->js_cookie_name )) {
# User is not logged in yet, but cookie is set by JS SDK on previous visit.
my $token_ref = $fb->get_user_token_by_cookie($cookie);
# {
# "access_token" : "new_token_string_qwerty",
# "expires" : 5752
# "expires" : 5752 # named expires_in as of v2.3
# };
}
else {
Expand All @@ -1097,12 +1107,14 @@ Exchange short lived access token for long lived one. Short lived tokens are
ones that you obtain with C<get_user_token_by_code()>. Usually long lived
tokens live about 60 days while short lived ones live about 2 hours.
FYI: I<expires> is B<NOT> returned on some edge cases. The detail and schenario
should be found at L<https://developers.facebook.com/bugs/597779113651383/>.
FYI: I<expires> or I<expires_in> is B<NOT> returned on some edge cases. The
detail and reproductive schenario should be found at
L<https://developers.facebook.com/bugs/597779113651383/>.
my $extended_token_ref = $fb->exchange_token($token_ref->{access_token});
my $access_token = $extended_token_ref->{access_token};
my $expires = $extended_token_ref->{expires};
# named expires_in as of v2.3
If you loved how old offline_access permission worked and are looking for a
substitute you might want to try this.
Expand Down
42 changes: 42 additions & 0 deletions lib/Facebook/OpenGraph/Response.pm
Expand Up @@ -31,6 +31,31 @@ sub req_content { shift->{req_content} }
sub json { shift->{json} }
sub etag { shift->header('etag') }

sub api_version {
my $self = shift;
return $self->header('facebook-api-version');
}

sub is_api_version_eq_or_later_than {
my ($self, $comparing_version) = @_;
croak 'comparing version is not given.' unless $comparing_version;

my $decimal_comparing_version = ( $comparing_version =~ s/\A v //rx + 0 );
my $decimal_response_version = ( $self->api_version =~ s/\A v //rx + 0 );

return $decimal_comparing_version <= $decimal_response_version;
}

sub is_api_version_eq_or_older_than {
my ($self, $comparing_version) = @_;
croak 'comparing version is not given.' unless $comparing_version;

my $decimal_comparing_version = ( $comparing_version =~ s/\A v //rx + 0 );
my $decimal_response_version = ( $self->api_version =~ s/\A v //rx + 0 );

return $decimal_comparing_version >= $decimal_response_version;
}

sub header {
my ($self, $key) = @_;

Expand Down Expand Up @@ -224,6 +249,23 @@ Returns specified header field value.
my $res = $fb->request('GET', 'go.hagiwara');
my $etag = $res->header('etag'); # "a376a57cb3a4bd3a3c6a53fca06b0fd5badee50b"
=head3 C<< $res->api_version >>
By checking facebook-api-version header value, it returns API version that
current API call actually experienced. This may differ from the one you
specified. See
L<https://developers.facebook.com/docs/apps/changelog#v2_1>
=head3 C<< $res->is_api_version_eq_or_older_than($comparing_version) >>
Compare $comparing_version with the facebook-api-version header value and
returns TRUE when facebook-api-version is older than the given version.
=head3 C<< $res->is_api_version_eq_or_later_than($comparing_version) >>
Compare $comparing_version with the facebook-api-version header value and
returns TRUE when facebook-api-version is newer than the given version.
=head3 C<< $res->is_success >>
Returns if status is 2XX or 304. 304 is added to handle $fb->fetch_with_etag();
Expand Down
5 changes: 4 additions & 1 deletion t/002_retrieve/01_get_app_token.t
Expand Up @@ -33,7 +33,10 @@ subtest 'get' => sub {
1,
200,
'OK',
['Content-Type' => 'text/plain; charset=UTF-8'],
[
'Content-Type' => 'text/plain; charset=UTF-8',
'facebook-api-version' => 'v2.0'
],
'access_token=123456789|SSSeFWB-0EQ0qyipMdmNpJJJJjk',
);
},
Expand Down
88 changes: 85 additions & 3 deletions t/002_retrieve/02_get_user_token.t
Expand Up @@ -4,6 +4,7 @@ use Test::More;
use Test::Exception;
use Test::Mock::Furl;
use Facebook::OpenGraph;
use JSON 2 qw(encode_json);

subtest 'success' => sub {

Expand Down Expand Up @@ -34,12 +35,15 @@ subtest 'success' => sub {
);
is $args{method}, 'GET', 'HTTP GET method';
is $args{content}, '', 'content';

return (
1,
200,
'OK',
['Content-Type' => 'text/plain; charset=UTF-8'],
[
'Content-Type' => 'text/plain; charset=UTF-8',
'facebook-api-version' => 'v2.2',
],
sprintf('access_token=%s&expires=%d', $token, $expires),
);
},
Expand All @@ -63,7 +67,85 @@ subtest 'success' => sub {
'token',
);
is $fb->access_token, undef, 'no access_token is set';
$fb->set_access_token($token_ref->{access_token});
$fb->set_access_token($token_ref->{access_token});
is $fb->access_token, $token, 'acess token is set';

};

subtest 'using v2.3' => sub {
# https://developers.facebook.com/docs/apps/changelog#v2_3_changes
# he response format of https://www.facebook.com/v2.3/oauth/access_token
# returned when you exchange a code for an access_token now return valid
# JSON instead of being URL encoded. The new format of this response is
# {"access_token": <TOKEN>, "token_type":<TYPE>, "expires_in":<TIME>}.
# We made this update to be compliant with section 5.1 of RFC 6749.

my $code = 'XXXXXXXXXXXXXXXXXXXXXX';
my $app_id = 123456789;
my $token = '123456789XXXXXXXXXXX';
my $expires = 5183814;
my $token_type = 'bearer';

$Mock_furl_http->mock(
request => sub {
my ($mock, %args) = @_;
is_deeply $args{headers}, [], 'no particular header';
my $uri = $args{url};
is $uri->scheme, 'https', 'scheme';
is $uri->host, 'graph.facebook.com', 'host';
is $uri->path, '/oauth/access_token', 'path';
is_deeply(
+{
$uri->query_form,
},
+{
client_secret => 'secret',
client_id => $app_id,
code => $code,
redirect_uri => 'http://sample.com/auth_cb',
},
'query',
);
is $args{method}, 'GET', 'HTTP GET method';
is $args{content}, '', 'content';

return (
1,
200,
'OK',
[
'Content-Type' => 'text/plain; charset=UTF-8',
'facebook-api-version' => 'v2.3',
],
encode_json(+{
access_token => $token,
expires_in => $expires,
token_type => $token_type,
})
);
},
);

# Redirect_uri should be exactly the same as the one
# that you used on $fb->auth_uri
my $fb = Facebook::OpenGraph->new(+{
app_id => $app_id,
secret => 'secret',
redirect_uri => 'http://sample.com/auth_cb',
});
my $token_ref = $fb->get_user_token_by_code($code);

is_deeply(
$token_ref,
+{
access_token => $token,
expires_in => $expires,
token_type => $token_type,
},
'token',
);
is $fb->access_token, undef, 'no access_token is set';
$fb->set_access_token($token_ref->{access_token});
is $fb->access_token, $token, 'acess token is set';

};
Expand Down
11 changes: 7 additions & 4 deletions t/002_retrieve/14_exchange_token.t
Expand Up @@ -34,13 +34,16 @@ subtest 'success' => sub {
);
is $args{method}, 'GET', 'HTTP GET method';
is $args{content}, '', 'content';

# returns long-lived access token
return (
1,
200,
'OK',
['Content-Type' => 'text/plain; charset=UTF-8'],
[
'Content-Type' => 'text/plain; charset=UTF-8',
'facebook-api-version' => 'v2.1',
],
sprintf(
'access_token=%s&expires=%d',
$long_lived_token,
Expand All @@ -49,7 +52,7 @@ subtest 'success' => sub {
);
},
);

my $fb = Facebook::OpenGraph->new(+{
app_id => $app_id,
secret => 'secret',
Expand Down Expand Up @@ -97,7 +100,7 @@ subtest 'w/o app_id' => sub {
qr/app_id and secret must be set /,
'app_id is not set',
);

};

done_testing;
18 changes: 12 additions & 6 deletions t/002_retrieve/15_get_user_token_by_cookie.t
Expand Up @@ -48,17 +48,20 @@ subtest 'success' => sub {
);
is $args{method}, 'GET', 'HTTP GET method';
is $args{content}, '', 'content';

return (
1,
200,
'OK',
['Content-Type' => 'text/plain; charset=UTF-8'],
[
'Content-Type' => 'text/plain; charset=UTF-8',
'facebook-api-version' => 'v2.1',
],
sprintf('access_token=%s&expires=%d', $token, $expires),
);
},
);

my $fb = Facebook::OpenGraph->new(+{
app_id => $app_id,
secret => $secret,
Expand Down Expand Up @@ -148,17 +151,20 @@ subtest 'expires is not returned from FB' => sub {
);
is $args{method}, 'GET', 'HTTP GET method';
is $args{content}, '', 'content';

return (
1,
200,
'OK',
['Content-Type' => 'text/plain; charset=UTF-8'],
[
'Content-Type' => 'text/plain; charset=UTF-8',
'facebook-api-version' => 'v2.1',
],
sprintf('access_token=%s', $token),
);
},
);

my $fb = Facebook::OpenGraph->new(+{
app_id => $app_id,
secret => $secret,
Expand Down

0 comments on commit 570ca9a

Please sign in to comment.