Users want to use my facebook app for many hours without refreshing the browser.
But token expires in 2 hours. Now I ask users to refresh the page but that's annoying.
I don't want to ask offline access permissions because it will scare some users.
The best solution will be somehow "relogin" and get new token without refreshing the page.
Is it possible?
I would subscribe to the expiry trigger (I think this is authResponseChange), then automate another login check. It won't be a perfect solution as it could trigger a pop up (if they have logged out for example) automatically, which a lot of browsers may block. You could instead, when the token expires, check if they will need to complete a pop up, and display a notification on your page somewhere saying 'Facebook needs your attention to continue', then only launch the pop up from their response, which would stop the pop up being blocked.
FB.Event.subscribe('auth.authResponseChange', function(response) {
// do something with response
FB.login(){
// refresh their session - or use JS to display a notification they can
// click to prevent pop up issues
}
});
An algorithm to workout on this
Ask for permission from the user
Save the token
Periodically check for an access token is near to expire or not
If its in verse of expiry, embed some dummy iframe, which redirects to the facebook homepage. - Extend auth token without refreshing the page
This should refresh the token. You might need to generate another token or continue with the same. Whatever be required, can be done without refreshing the page.
Have you thought of using ajax? After two hours you will check, if user is still active. If so, you send axax request to URL, where his session details will be updated. example:
$(document).ready(function(){
setInterval('update_session()', 5500000);
})
update_session(){
$.post({
URL: ..., // script to update session on server
data:{ /* username, password */ },
})
}
and the server-side just takes username and password from post or and runs relogin.
Try acquiring tokens with the offline_access permission.
I presume, guess this is not possible,FB architecture would not allow it. And why is offline_access such a problem!!!!!!...anyway offline_access is the best optimal solution I guess....
Unfortunately I believe this is impossible by design (if you mean for it to happen without user intervention). If the user is still logged in to Facebook you can redirect the top-level page to Facebook and it will bounce you right back with a new code (as it sounds like you are doing already), but that is only possible because of the Facebook cookie that it can check. If you try to do anything from your server, it will be rejected because that cookie will not accompany the request. Same goes for trying to make a call to facebook from javascript -- since your code is running in a different domain, the cookie will not accompany the call and Facebook will reject it. The only way that Facebook can even know who the user is, and that they are still logged in, is to see that cookie. And the only way that can happen is if the browser itself is redirected to the facebook.com domain.
It's worth mentioning also that Facebook has blocked the only logical workaround, i.e. loading the oauth url in an iframe. If you try it you will see that they detect the page is being loaded in an iframe and output a page with a link on it which does a top-level redirect to break out of the frame. So not only does this approach not work, it's clear that Facebook has specifically made it impossible as part of their architecture.
Edit: If what you mean to do is not avoid the refresh altogether but just have it happen automatically when a new token is needed, you can do something like this:
$status=0;
$data=#file_get_contents("https://graph.facebook.com/me?access_token=$token");
foreach ($http_response_header as $rh) if (substr($rh, 0, 4)=='HTTP') list(,$status,)=explode(' ', $rh, 3);
if ($status==200)
{
//token is good, proceed
}
else
{
//token is expired, get new one
$fburl="http://www.facebook.com/dialog/oauth?client_id=APP_ID&redirect_uri=".urlencode('http://apps.facebook.com/yourapp/thispage.php');
echo "<html>\n<body>\n<script>top.location='$fburl';</script>\n</body>\n</html>\n";
exit;
}
This is assuming you have something before this code that will process a signed_request parameter if it is present and assign a value to $token (either explicit code of your own or the appropriate SDK entries). The shown code can then be used anywhere you need to check if $token is still valid before proceeding.
If you get the access_token without specifying any expiry to them they will not expire ..
atleast not till the time user either changes his Fb credentials or de registers your application ..
I presume you are using the iframe signed_request parameter to get your access token. One method of achieving what you require is to use the oAuth 2.0 method of aquiring an access token. This is more prolonged in the first instance; your server and Facebook's have to exchange credentials which can be slow, but it means that you will be given a code that can be exchanged for an access token regularly, meaning your server can maintain the session periodically (probably from an ajax call from the client). You would then pass this new access_token to the client, and use it in your dialog call for your requests (gifts).
Hope that helps.
Spabby
Have a look at https://developers.facebook.com/docs/offline-access-deprecation/#extend_token
basically you extend the token with
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
that will give you new token with new expiry time (it should be 60d but I'm noticing similar bug like described here https://developers.facebook.com/bugs/347831145255847/?browse=search_4f5b6e51b18170786854060 )
Related
Ok, so what I am trying to do is a bit odd, so I can't find anything that gives me even a remote idea about how to do this.
I need to access my personal profile posts:
FB.api("/" + myPersonalUserId + "/feed", {limit: 5}, function(data){
console.log(data);
// do stuff with my user info
});
in order to display them on my personal webite, similar to a dynamic blog. But I want it to automatically retrieve these posts without my having to be signed in on each computer that wants to view my site.
Before you get sidetracked on the init, I am using an app and app id that my personal user account has verified access to all permissions.
I know it will require the use of an access token, but how do I get a valid access token without being logged into that computer?
Honestly, I'm starting to question if it is even possible, but if anyone knows how I could accomplish this, that would be awesome!
The best way to achieve this is to just cache the data in your own database and refresh it whenver the user uses your App again.
If that´s not good enough, you have to generate and store an Extended User Token. How to create one is explained in the docs:
https://developers.facebook.com/docs/facebook-login/access-tokens
http://www.devils-heaven.com/facebook-access-tokens/
Extended User Tokens are valid for 60 days, there is no User Token that is valid forever. And you should never use Tokens directly on the client, because some user could just copy it from the source. Tokens are meant to be secret, so use it on the server only. You don´t need to use the PHP SDK, a simple CURL call to the Graph API will do it:
https://graph.facebook.com/[your-app-scoped-id]/feed?access_token=[extended-user-token]
Ok, so I found a solution similar to the one above, but offers a permanent access token.
first, build a url:
url = 'https://graph.facebook.com/v2.5/' + {app user Id, not public Id} + '/feed';
url += '?access_token=' + {app Id} + '|' + {app secret};
url += '&fields=id,name,message,full_picture,created_time'; // these scopes should be approved by corresponding user
url += '&limit=5';
then run it by calling a simple ajax request. These variables should be served from the server through ajax, not hardcoded on the client
This question has been asked a few times on Stack, but there have been no real answers. Let me try to explain my situation anyways.
We use an application that uses Facebook OAuth2 login. This login used to work fine till last week, and all of a sudden it is troubling us now.
Application Flow:
Step 1: User presses login with Facebook button on our website
Step 2: Redirected to Facebook login/authorization page
Step 3: On authorizing the app, the callback comes to our application, with a short lived "code" param.
Step 4: This "code" param would be exchanged for a 60 day Access token using "https://graph.facebook.com/oauth/access_token" URL.
Error in Step 4:
When we try to exchange the short living "code" for the access token, we get this error from Facebook.
{"error":{"message":"This authorization code has been used.","type":"OAuthException","code":100}}
Observation:
For users who are newly coming to the application, the above-said error does not occur.
For a returning user this call fails with the above-said error.
Our application is live for more than 9 months now, and this error has come only in the past 7-10 days. We have had thousands of users using it successfully prior to that.
What I already got from Forums:
Here is my interpretation of what I read. May be inaccurate.
Facebook has some weird policy that necessitates the app developer to maintain the temporary 10 minute code until the 60 day code that was obtained during the first login expires. So we should create a cookie with the Access token on the user's browser. I was even able to see people modifying their code in order to create the cookies.
What's really bothering me?
The suggested solutions assumes that the cookie that they create would be present in the user's browser always. This is a bad assumption to make, as the cookie may be erased at any time.
I have another app Id/app secret that I use for my development (i.e localhost), and that works perfectly. The login happens fine out there, But its only the product machine that has the problem.
This problem didn't happen on the production machine for nearly 10 months since we launched the app, and it has come all of a sudden. Worst of all, I am unable to get any record of recent changes that breaks this flow.
Edit:
Platform: Python, Google Appengine. We do not use any Facebook SDKs, we make direct HTTP Calls to all the login URLs.
Call that fails : https://graph.facebook.com/oauth/access_token - we are passing the appId, secret and code (obtained from facebook) within 20 seconds of the first call happening.
Hope there is enough information here to show that our code is not totally incorrect. Any tips/pointers from people who have encountered and solved this problem is Welcome. If its a Facebook bug, and the Facebook dev comes to notice, I would be even happier.
I got round this issue by using a random GUID which is appended to each callback url i pass into facebook. It seems the code that facebook returns is made up of a few parts including the redirect_uri parameter you have to specify. By using this GUID trick, your app continues to work but facebook thinks it's a different URL hence generating a new code.
If you store that GUID in a temporary session, it's always the same. Here's a very cut down version of what I mean. I'm using C# but the solution will be the same:
Before i start the oauth process:
Session["facebook_buster"] = System.Guid.NewGuid().ToString();
Then to kick off the login:
var facebook = new FacebookClient();
var loginUrl = facebook.GetLoginUrl(new
{
client_id = ...,
redirect_uri = ..."/facebook/oauthcallback?buster=" + Session["facebook_buster"].ToString(),
display = "popup",
scope = "publish_stream,user_photos"
});
And then in my callback method, when I want to exchange that code for a new access_token:
var facebook = new FacebookClient();
dynamic result = facebook.Post("oauth/access_token", new
{
client_id = ...,
client_secret = ...,
redirect_uri = ..."/facebook/oauthcallback?buster=" + Session["facebook_buster"].ToString(),
code = Request["code"] // this is the returned code from the first method
});
Note in that second method i'm using the same session key so that the authorization code is successful.
Been testing this all morning by revoking permissions / manually changing my stored access_token (in my db) / removing my stored access_token completely and it works every time.
Hope this helps!
I struggled with this today for a while too. Not sure if you're using the Facebook PHP class (from what you wrote, it seems you don't), however, it could be a pointer anyways - the problem was that the Facebook PHP library seems to obtain the token from the code automatically and I was trying to do it again.
I'm utilizing the Facebook PHP SDK on its own. I do not want to use the JS SDK at all.
Because getUser(); from the SDK can return a user id even if the user is not logged in, I have opted for using a try/catch statement to check if the user is logged in.
try
{
$me = $CI->facebook->api('/me');
$CI->our_fb['is_fb']='YES';
echo "hello";
}
catch(FacebookApiException $e)
{
echo "catch";
}
This statement is included in the global include file of all of my files (for simplicity).
So, depending on the situation, I generate a Facebook login URL. The expected functionality is that the user logins to Facebook, authorises the app, is returned to the redirect URI set in the login URL at which point the try statement will execute, and $CI->our_fb['is_fb'] will be set.
This is however not happening.
If the user is already logged into Facebook and the app is authorised, it works perfectly. SUCCESS
If the user is not logged into Facebook, once redirected the variable is not set. FAILURE
If the user is logged in but the app is NOT authorised after redirect the variable is not set. FAILURE.
In the latter two cases if you simply refresh the page, the variable is set - SUCCESS. Refreshing the page is however unnecessary/pointless extra effort.
My problem is that if you need to login to FB/or authorise the app e.g the first time you login with FB, you have an additional unneeded refresh, and I don't know why.
I suspect it is something to do with the cookie/session? Which saves the access token that I assume is returned/passed to the SDK automatically not being set at the same time?
Anyone got any ideas?
If you're having an app on facebook (tab or canvas). PHP SDK only get the User ID on initial loading of a page because a signed_request is sent with the request to your app.
But, when the app refreshes, the signed_request is lost (as it's facebook who send it).
So, in this case, you can append the signed_request to every URLs your use in your app - but that's really not optimal as the signed_request won't be regenarated - neither refreshed.
Your only real option is to rely on the JS SDK to set cookie correctly and allow getUser to work as expected. This is required because you're considered as a third-party app in Facebook (being in an iframe) and most browser will block you from setting cookies - so you need a work around handled by the JS SDK for you. You can search for cross-domain cookies or third-party cookie for explanation about the workarounds, but these workaround only work via JS scripting and iframe management.
Also, be sure to setup the JS SDK correctly: channel file, cookie allowed, and send P3P headers (for IE).
You can also check this related question: A proper approach to FB auth
About website, the same mostly stays (but you have no signed_request). At this point, seriously consider using the JS SDK as it's way easier. Or else, you can make sure your app flow follow these guidelines: https://developers.facebook.com/docs/concepts/login/login-architecture/
The way I am seeing this is, you are trying to avoid that refresh if the user is not logged in and precedes to log in after the page has initially loaded.
So what you can do is make an ajax request to another page on your site, say for example id.php, which just loads the php sdk and echo $userid; and then you can grab the user id after login without the refresh.
Basically the cookie is used to save the signed request and session is used to save 'state', 'code', 'access_token', 'user_id'. If the above are present PHP SDK uses them, no matter if they are valid or not.
I think your problem lies in the CODE sent by facebook. Specifically these lines in base_facebook.php:
if ($code && $code != $this->getPersistentData('code')) {
$access_token = $this->getAccessTokenFromCode($code);
...
protected function getAccessTokenFromCode($code, $redirect_uri = null) {
if (empty($code)) {
return false;
}
if ($redirect_uri === null) {
$redirect_uri = $this->getCurrentUrl();
}
...
Because CODE is issued for specific url sometimes there is such situation: Visitor arrives on www.example.com. He givies permissions and is redirected to example.com/login. But the code is not valid there, so the getUserAccessToken returns false. When you refresh the page you get same urls and everything's fine.
You're on the right track of not using getUser() because as I wrote above it's taken from the session if available.
BASIC PROBLEM: I want my app to be able to make calls to the Facebook graph api about authorized users even while the user is away.
For example, I want the user (A) to authorize the app, then later I want user (B) to be able to use the app to view info about user (A)'s friends. Specifically: the "work" field. Yes, I am requesting those extended permissions (user_work_history, friends_work_history, etc). Currently my app has access to the logged-in user's friends work history, but not to any of the friends' work history of other users of the app.
Here's what I know already:
Adding offline_access to the scope parameter is the old way and it
no longer works.
The new way is with "long-lived" access tokens,
described here. These last for 60 days.
I need to exchange a normal access token to get the new extended token. The FB documentation says:
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
Here's what I don't know (and I'm hoping you can tell me):
How do I get the extended (aka "long-lived") access token using the Facebook PHP SDK? Currently, my code looks like this:
$facebook->getAccessToken();
Is there such a thing as this?:
$facebook->getExtendedAccessToken();
If not, is this what I should be doing?
$accessToken = $facebook->getAccessToken();
$extendedAccessToken = file_get_contents("https://graph.facebook.com/oauth/access_token?
client_id={$appId}&
client_secret={$secret}&
grant_type=fb_exchange_token&
fb_exchange_token={$accessToken}"
);
I've tried it and it doesn't work. I get this error:
Warning: file_get_contents(https://graph.facebook.com/oauth/access_token? client_id=#######& client_secret=#########& grant_type=fb_exchange_token& fb_exchange_token=##########) [function.file-get-contents]: failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request in /...
Does it work any differently if I switch to FQL instead of the graph api? I've read through the Facebook documentation many times, but the PHP sdk is not thoroughly documented and I can't find any examples of how this should work.
I finally figured this out on my own. The answer is pretty anti-climactic. It appears that newly created apps get 60 day access tokens automatically. I'm not sure if this is dependent on enabling the "depricate offline_access" setting in the Migrations section of the app settings. Leave it on to be safe.
So at the time of writing this, you can use the PHP SDK as follows: $facebook->getAccessToken();
(The reason my app wasn't working as expected was unrelated to the expiration of the access token.)
Just one more thing, to get long-lived access token using PHP SDK you should call $facebook->setExtendedAccessToken(); before $facebook->getAccessToken();
In the last Facebook PHP SDK 3.2.0 you have a new function setExtendedAccessToken()
that you have to call before getAccessToken();
Like this:
$user = $facebook->getUser();
$facebook->setExtendedAccessToken(); //long-live access_token 60 days
$access_token = $facebook->getAccessToken();
Actually newly created apps only get a 60 day access token automatically if you are using a server side call. If you are using the client-side endpoint as shown above in the question, even new apps will still receive a short-term token initially. see: https://developers.facebook.com/docs/roadmap/completed-changes/offline-access-removal/
I had the same HTTP/1.1 400 Bad Request error that you had when using the New Endpoint and the problem was if you copy the code Facebook gives you exactly and paste it into your app, there are actually spaces in between the params, meaning there's unnecessary spaces in the url and it won't get called correctly when passed into file_get_contents() even though it works okay when pasted in the browser. This took me way too long to figure out. Hope this helps somebody! Here is my complete working code to get the extended access token out of the new endpoint (replace x's with your values):
$extend_url = "https://graph.facebook.com/oauth/access_token?client_id=xxxxxxxxxxxx&client_secret=xxxxxxxxxxxxxxxxxxxxxx&grant_type=fb_exchange_token&fb_exchange_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$resp = file_get_contents($extend_url);
parse_str($resp,$output);
$extended_token = $output['access_token'];
echo $extended_token;
The selected answer is now outdated. Here are Facebook's instructions to swap a short-term token (provided in front-end) for a long-term token (server only):
https://developers.facebook.com/docs/facebook-login/access-tokens/refreshing/
Generate a Long-lived User or Page Access Token
You will need the following:
A valid User or Page Access Token
Your App ID
Your App Secret
Query the GET oath/access_token endpoint.
curl -i -X GET "https://graph.facebook.com/{graph-api-version}/oauth/access_token?
grant_type=fb_exchange_token
client_id={app-id}&
client_secret={app-secret}&
fb_exchange_token={your-access-token}"
Sample Response
{
"access_token":"{long-lived-access-token}",
"token_type": "bearer",
"expires_in": 5183944 //The number of seconds until the token expires
}
I have an application that integrates with Facebook using Oauth 2.
I can authorize with FB and query their REST and Graph APIs perfectly well, but when I authorize an active browser session is created with FB. I can then log-out of my application just fine, but the session with FB persists, so if anyone else uses the browser they will see the previous users FB account (unless the previous user manually logs out of FB also).
The steps I take to authorize are:
Call [LINK: graph.facebook.com/oauth/authorize?client_id...]
This step opens a Facebook login/connect window if the user's browser doesn't already have an active FB session. Once they log-in to facebook they redirect to my site with a code I can exchange for an oauth token.
Call [LINK: graph.facebook.com/oauth/access_token?client_id..] with the code from (1)
Now I have an Oauth Token, and the user's browser is logged into my site, and into FB.
I call a bunch of APIs to do stuff: i.e. [LINK: graph.facebook.com/me?access_token=..]
Lets say my user wants to log out of my site. The FB terms and conditions demand that I perform Single Sign Off, so when the user logs out of my site, they also are logged out of Facebook. There are arguments that this is a bit daft, but I'm happy to comply if there is any way of actually achieving that.
I have seen suggestions that:
A. I use the Javascript API to logout: FB.Connect.logout(). Well I tried using that, but it didn't work, and I'm not sure exactly how it could, as I don't use the Javascript API in any way on my site. The session isn't maintained or created by the Javascript API so I'm not sure how it's supposed to expire it either.
B. Use [LINK: facebook.com/logout.php]. This was suggested by an admin in the Facebook forums some time ago. The example given related to the old way of getting FB sessions (non-oauth) so I don't think I can apply it in my case.
C. Use the old REST api expireSession or revokeAuthorization. I tried both of these and while they do expire the Oauth token they don't invalidate the session that the browser is currently using so it has no effect, the user is not logged out of Facebook.
I'm really at a bit of a loose end, the Facebook documentation is patchy, ambiguous and pretty poor. The support on the forums is non-existant, at the moment I can't even log in to the facebook forum, and aside from that, their own FB Connect integration doesn't even work on the forum itself. Doesn't inspire much confidence.
Ta for any help you can offer.
Derek
ps. Had to change HTTPS to LINK, not enough karma to post links which is probably fair enough.
I was having the same problem. I also login using oauth (I am using RubyOnRails), but for logout, I do it with JavaScript using a link like this:
Logout
This first calls the onclick function and performs a logout on facebook, and then the normal /logout function of my site is called.
Though I would prefer a serverside solution as well, but at least it does what I want, it logs me out on both sites.
I am also quite new to the Facebook integration stuff and played around the first time with it, but my general feeling is that the documentation is pretty spread all over the place with lots of outdated stuff.
This works as of now - and is documented on facebook's site # http://developers.facebook.com/docs/authentication/. Not sure how recently it was added to the documentation, pretty sure it wasn't there when I checked Feb-2012
You can programmatically log the user our of Facebook by redirecting
the user to
https://www.facebook.com/logout.php?next=YOUR_REDIRECT_URL&access_token=USER_ACCESS_TOKEN
This solution no longer works with FaceBook's current API (seems it was unintended to begin with)
http://m.facebook.com/logout.php?confirm=1&next=http://yoursitename.com;
Try to give this link on you signout link or button where "yoursitename.com"
is where u want to redirect back after signout may be ur home page.
It works..
I can programmatically log user out Facebook by redirecting user to
https://www.facebook.com/logout.php?next=YOUR_REDIRECT_URL&access_token=USER_ACCESS_TOKEN
The URL supplied in the next parameter must be a URL with the same base domain as your application as defined in your app's settings.
More details: https://developers.facebook.com/docs/authentication
You can do this with the access_token:
$access_array = split("\|", $access_token);
$session_key = $access_array[1];
You can use that $session key in the PHP SDK to generate a functional logout URL.
$logoutUrl = $facebook->getLogoutUrl(array('next' => $logoutUrl, 'session_key' => $session_key));
This ends the browser's facebook session.
With PHP I'm doing:
logout.
if(isset($_GET['action']) && $_GET['action'] === 'logout'){
$facebook->destroySession();
header(WHERE YOU WANT TO REDIRECT TO);
exit();
}
Works and is nice and easy am just trying to find a logout button graphic now!
Here's an alternative to the accepted answer that works in the current (2.12) version of the API.
Logout
<script>
FB.init({
appId: '{your-app-id}',
cookie: true,
xfbml: true,
version: 'v2.12'
});
function logoutFromFacebookAndRedirect(redirectUrl) {
FB.getLoginStatus(function (response) {
if (response.status == 'connected')
FB.logout(function (response) {
window.location.href = redirectUrl;
});
else
window.location.href = redirectUrl;
});
}
</script>
the mobile solution suggested by Sumit works perfectly for AS3 Air:
html.location = "http://m.facebook.com/logout.php?confirm=1&next=http://yoursitename.com"
For Python developers that want to log user out straight from the backend
At the moment I'm writing this, the trick with m.facebook.com no longer works (at least for me) and user is redirected to the mobile FB login page which obviously is not good for UX.
Fortunately, FB PHP SDK has a semi-documented solution (in case the link doesn't lead to getLogoutUrl() function, just search look for it on that page). This is also mentioned in at least one other on StackOverflow: Facebook php SDK getLogoutUrl() problem.
BTW I've just noticed that Zach Greenberg got it right in this question, but I'm adding my answer as a summary for Python developers.
A note for Christoph's answer:
Facebook Oauth Logout
The logout function requires a callback function to be specified and will fail without
it, at least on Firefox. Chrome works without the callback.
FB.logout(function(response) {});
#Christoph: just adding someting . i dont think so this is a correct way.to logout at both places at the same time.(Logout).
Just add id to the anchor tag . <a id='fbLogOut' href="/logout" onclick="FB.logout();">Logout</a>
$(document).ready(function(){
$('#fbLogOut').click(function(e){
e.preventDefault();
FB.logout(function(response) {
// user is now logged out
var url = $(this).attr('href');
window.location= url;
});
});});
Update: This solution works and just a call to 'FB.logout()' doesn't work because browser wants a user interaction to actually call this function, so that it knows - it is a user not a script.
Logout
it's simple just type : $facebook->setSession(null); for logout