Newb Q (sorry!): $fb->get_user_token_by_cookie($cookie_value) dies on accessing cookie for logged out user
Beeblbrox opened this issue · comments
my %cookies = CGI::Simple::Cookie->fetch;
if (($cookies{ $fb->js_cookie_name }) && ($cookie=$cookies{ $fb->js_cookie_name }->value)) {
# 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); ## i get a bad croak here
This works great for a user that is already logged in, or hasn't visited the site before. But an unlogged in user revisiting actually gets to see the app secret in the croak, e.g. they log out on another tab and refresh the page in the other (I assume Furl reporting the failed request() ?) I noted although expiry may be an issue it's also when the user has actively logged out of FB
Anyway, what I wanted to ask is there any way I can test the JS SDK cookie value token for validity before passing it to the $fb->get_user_token_by_cookie (and thus avoiding croak with GET/POST string displaying my secret)? That or any way of catching the error within the existing object before it croaks?
Apologies if this is a trivial issue, I know the examples are meant to be a guide but I have no idea how to proceed.
Thanks for bringing that up, @Beeblbrox .
I'm not really sure about the detailed flow you went through, but when user logs out from Facebook or your app, the access token associated with your app becomes invalid.
e.g. When user logs out on another browser tab.
I can think of 2 options:
- Check login status with FB.getLoginStatus() when user hit login button, and if user is not logged in, use FB.login() to let her login so appropriate cookie value can be set and
$fb->get_user_token_by_cookie()
works. - When
$fb->get_user_token_by_cookie()
failes, redirect the user to login dialog and let her go through the login steps. - Do both of above.
I think, as a failover, at least the second option is necessary to let users have chance to login.
Thanks.
Hi thankyou for getting back to me I know you must be very busy :-) The problem is that when $fb->get_user_token_by_cookie fails it fails with a fatal so I cannot do any more. erm I try to explain what happens:
If there is no cookie, the user logs in to the JSSDK and gets a valid token and the perl script works fine.
If there is a cookie and it is logged in the perl script fetches it fine and all is good.
If there is a cookie but it is not valid $fb->get_user_token_by_cookie() fails with a fatal - i cannot redirect or do anything more in the perl script. This i understand about letting users have a chance to login, but I need to catch $fb->get_user_token_by_cookie failing without it causing a fatal on the script. I wish I could explain better somehow.... the second step you suggest says when it fails I redirect etc. - but when it fails I can do nothing more - if i could do a redirect it would mean that the fail wasn't fatal but it is :-( Unless there is some basic perl of getting around this I do not know.
I would happily show the flow I go through in an appropriate way on this site but I do not think this post is where I am supposed to show it? I am new to github too.
OK here goes. The following code dies with this error if you bring the script up, logout of facebook on another tab, and refresh the script in the browser:
Software error:
100:- OAuthException:Code was invalid or expired.
GET /oauth/access_token?redirect_uri=&client_secret=#############&client_id=############&code=AQDX482ihved02AG2Xq4F9TeCOKUbN1z69_iC5LdeYk9cxtGJVoInUNZSUIn0fEkAoiOo06eGmR98ydkf43OWrR0xQcck_ygN4lYOjpLas20GOLekVGWUZdbnwppKRllD8IB_20OeXVxnwjCTHj8-wtuaplXEdp8_6XgoCmbZbfRLxxT_aU9VdiR_9DugJPk--iVVvzSc1x3lfEuLIKIEXHzHd2iENkiRXOEANJQhz3bq26ywCW2XSdzmqYHa-5bXbST5FdPavKwcRrff9yhjjRfbsNPb0aHcShS_xSzZpaeXK-V02eFY-6usjEslYhCShZjQFQ2h_ulSVL6W3BlRQY- HTTP/1.1
Connection: keep-alive
User-Agent: Facebook::OpenGraph/1.23
Authorization: OAuth HASH(0x13a7a70)
Content-Length: 0
Host: graph.facebook.com
print<<"print_tag";
<html>
<head>
</head>
<body>
<script>
// standard login script from here.////////////////////////////////////////////////////
// This is called with the results from from FB.getLoginStatus().
function statusChangeCallback(response) {
console.log('statusChangeCallback');
console.log(response);
// The response object is returned with a status field that lets the
// app know the current login status of the person.
// Full docs on the response object can be found in the documentation
// for FB.getLoginStatus().
if (response.status === 'connected') {
// Logged into your app and Facebook.
testAPI();
} else if (response.status === 'not_authorized') {
// The person is logged into Facebook, but not your app.
document.getElementById('status').innerHTML = 'Please log ' +
'into this app.';
} else {
// The person is not logged into Facebook, so we're not sure if
// they are logged into this app or not.
document.getElementById('status').innerHTML = 'Please log ' +
'into Facebook.';
}
}
// This function is called when someone finishes with the Login
// Button. See the onlogin handler attached to it in the sample
// code below.
function checkLoginState() {
FB.getLoginStatus(function(response) {
statusChangeCallback(response);
});
}
window.fbAsyncInit = function() {
FB.init({
appId : 'myID',
cookie : true, // enable cookies to allow the server to access
// the session
xfbml : true, // parse social plugins on this page
version : 'v2.2' // use version 2.2
});
// Now that we've initialized the JavaScript SDK, we call
// FB.getLoginStatus(). This function gets the state of the
// person visiting this page and can return one of three states to
// the callback you provide. They can be:
//
// 1. Logged into your app ('connected')
// 2. Logged into Facebook, but not your app ('not_authorized')
// 3. Not logged into Facebook and can't tell if they are logged into
// your app or not.
//
// These three cases are handled in the callback function.
FB.getLoginStatus(function(response) {
statusChangeCallback(response);
});
};
// Load the SDK asynchronously
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
// Here we run a very simple test of the Graph API after login is
// successful. See statusChangeCallback() for when this call is made.
function testAPI() {
console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Successful login for: ' + response.name);
document.getElementById('status').innerHTML =
'Hello, ' + response.name + '!';
});
}
// to here //////////////////////////////////////////////////////////////////////////////
</script>
<!--
Below we include the Login Button social plugin. This button uses
the JavaScript SDK to present a graphical Login button that triggers
the FB.login() function when clicked.
-->
<fb:login-button scope="public_profile,email" onlogin="checkLoginState();">
</fb:login-button>
<div id="status">
</div>
print_tag
print '<form method="post" action="login.cgi" enctype="multipart/form-data" name="login">';
print "$failure_message<br />";
print "Username:".$query->textfield('user');
print "Password:".$query->password_field('pass');
print<<"print_tag";
<p>
<input type="submit" name="Submit" value="Submit">
<input type="reset" name="Reset" value="Reset">
</p>
</form>
</body>
</html>
print_tag
}
my $fb = Facebook::OpenGraph->new(+{
app_id => myID,
secret => 'mySecret',
namespace => 'fbgamebookstest',
redirect_uri => 'mylogin.cgi'
});
my $app_token_ref =$fb->get_app_token;
$fb->set_access_token($app_token_ref);
my %cookies = CGI::Simple::Cookie->fetch;
my $cookie;
if (($cookies{ $fb->js_cookie_name }) && ($cookie=$cookies{ $fb->js_cookie_name }->value)) {
# 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.
if(my $token_ref = $fb->get_user_token_by_cookie($cookie)){
# {
# "access_token" : "new_token_string_qwerty",
# "expires" : 5752 # named expires_in as of v2.3
# };
$fb->set_access_token($token_ref->{access_token});
}
else{
die "my way";
}
my $fbID=$fb->fetch('me');
foreach $key( keys %{$fbID}){
print "$key : ".${$fbID}{$key}."<br />";
}
my $res = $fb->publish_action('Post', +{achievement => 'anID'});
print "res=";
foreach $key( keys %{$res}){
print "$key : ".${$res}{$key}."<br />";
}
}
else{
# die;
}
Note it only happens once, refreshing the script after that and the error disappears and just leaves login buttons, but once is enough to give away the app secret....
About the "fatal error" you are referring to, that's the expected behavior, @Beeblbrox .
$fb-> get_user_token_by_cookie
croaks because the cookie is set, but Facebook Platform returns error status saying "Code was invalid or expired".
You should catch this by wrapping $fb-> get_user_token_by_cookie
with eval
or other exception catcher module to properly handle.
And when I say "proper handling" I mean the second option I previously mentioned.
OK, thankyou @oklahomer. I had narrowed this problem down a little and found that the SDK loading took more time than the perl script to execute (obviously) so that it only happened once because once the SDK had loaded it squashed the offending leftover cookie (at least I think that's what happens), but that was too late to stop the script from trying to auth an invalid token. I had considered using 'sleep' with an nph cgi to give it time to load but i think eval would be better as you say. Thank you again for taking time out to look :-)
(perhaps the synopsis could have a change to use an eval block to reflect this? Like:
# User is not logged in yet, but cookie is set by JS SDK on previous visit.
my $token_ref;
eval{$token_ref = $fb->get_user_token_by_cookie($cookie)} or print "Your Facebook session maybe has expired. Please login to Facebook";
unless ($@){....
works for me, maybe useful for beginners!)
I'll consider that for next release.
Thanks.
OK sorry for waking you on a dead thread - I discovered (after a long absence from coding) that the error was utterly mine and my useless testbed. I can only apologise really profusely for wasting your time on a non-issue - I do know how frustrating this can be. I make no excuses. :-(
Thanks for your sincere response.
I understand how it goes when working on new APIs and client libraries, so don't worry.
Kind of have a similar experience myself 😄