Why are my Facebook access tokens expring in Javascript - facebook

I am creating a website using Parse, and I have run into an issue. When I let my browser sit around for a couple hours, calls to the graph API don't seem to work anymore. The response from the graph api is:
Error validating access token: Session has expired on Wednesday,
29-Apr-15 00:00:00 PDT. The current time is Wednesday, 29-Apr-15
21:34:32 PDT.
Of course, my token has expired... but now I can't find a way to easily refresh my access token without sending the user back through the login process... which isn't an ideal workflow.
A glimmer of hope in the Facebook Javascript documentation has me wondering if I am potentially just doing something wrong. If not, their documentation is horribly misleading.
Also keep in mind that the access tokens that are generated in
browsers generally have a lifetime of only a couple of hours and are
automatically refreshed by the JavaScript SDK. If you are making calls
from a server, you will need to generate a long lived token, which is
covered at length in our access token documentation.
source: https://developers.facebook.com/docs/facebook-login/login-flow-for-web/v2.3#token
What can we do to automatically refresh this token?

The JS SDK should automatically provide fresh access tokens, yes – although I am not sure if it does so if you just have the app open for a couple of hours without interacting with it (meaning: reloading the page, so that the JS SDK gets re-initialized as well). Since this is not a very common use case, it might not.
I would suggest that you try and use FB.getLoginStatus to get a fresh access token. (There should be no need to then set this access token explicitly on subsequent API calls; that the SDK will handle itself.)
This method uses an internally cached result, to avoid having to make a request to Facebook every time it is called. Since the expiry time of a token is known, I would expect the SDK to be able to decide itself if an actual request is necessary again – but if it doesn’t, you can also force a new request by setting the second parameter to true. (This is however not recommendable for every call, so I would try and see if calling the method without it is able to resolve the problem already.)

A simple workaround is to make an API call before the current access_token expired.
Let's say the token expires every 2 hours, you can, for example, make a simple /me call every 1H30 :
/**
* Make an API Call to refresh the access_token
*/
function tokenRefresh() {
FB.api("/me", function (response) {
// the token is refreshed
});
}
// call it every 1H30
var tokenTimeout = window.setTimeout(tokenRefresh, 5400000);
If you don't want that code be executed when your user uses the app, so when it will be useless, you can, each time you make a request, reset the timeout in the callback :
window.clearTimeout(tokenTimeout);
var tokenTimeout = window.setTimeout(tokenRefresh, 5400000);
Therefore, your app makes a call each 1H30 that passed without an API call.

Related

I can't get my generated App Access Token to work

I'm trying to use my generated App Access Token with requests to the Graph API, but for some unknown reason it simply will not work. I always get the dreaded Invalid OAuth access token signature error.
Now, let me clarify: I'm pretty sure I know what I'm doing. I did get it to work with a different FB app. I used the /oauth/access_token call to generate the App Access Token, and I can use that Token successfully in further graph calls for that FB app.
But when I try it for my new FB application, it fails. This is the Rails snippet:
app_access_token = URI.escape(ENV["FACEBOOK_APP_ACCESS_TOKEN"])
url = "https://graph.facebook.com/debug_token?input_token=#{user_access_token}&access_token=#{app_access_token}"
response = HTTParty.get(url)
I've also tried not-encoding the token; as expected, it doesn't work (and shouldn't).
I've double- and triple-checked my App ID and App Secret, and re-ran the generation (via curl), and I think it's all correct and copy/pasted without error. The only possibly-notable difference between the generated token of my old app and that of my new one is that the latter has a hyphen in it. Is that relevant?
I am currently working around this by using <App ID>|<App Secret> in place of the generated token, but I'd really like to get back to doing it the right way.
Argh! It turns out that I had a few extra characters that somehow got attached to the end of my generated token!
I don't know how those got there, but that was the problem. Just dumb old human error.
EDIT: Oh my god, every Facebook developer should know this page:
https://developers.facebook.com/tools/accesstoken/
Why isn't that linked from the docs about the access token? It would have saved me so much time and probably prevented my mistake!

How to have users 'reconnect' with soundcloud on each page reload?

I'm using the Javascript SDK inside a node.js (Express) App.
Connecting Users works fine, but the connection does not persist between page reloads.
Is this the default behaviour, or is the User supposed to stay connected in new requests?
Do I have to use OAuth token authentication and if so, how can this be done with the JS-SDK?
Inside the "Permission"-Popup, Users are already logged in with soundlcoud, though.
(just have to click the "connect" button each time)
Figured I'd share my answer for those who are unsatisfied with the current answers for automated oauth:
Retrieving access_token:
I had to define get and set cookie functions and then I use the functions to set and retrieve a function holding the access token. I'm not going to give these functions for conciseness but you can easily find them with a google search. I then use this line of code to get the SC access token (once the user has authenticated for the first time)
SC.accessToken()
Setting token:
So this is kind of just an elephant in the room in my opinion that for some reason no one has mentioned. How in the **** do you connect w/ SC using the access token? Do you set it as oauth param? On each call pass it? Well, after experimenting with putting the parameter in every single place I could think, I found out you have to do something like this:
SC.initialize({
client_id: '[removed for security reasons]',
client_secret: '[removed for security reasons]',
redirect_uri: '[removed for security reasons]',
access_token: getCookie("sc_lm2"),
scope: 'non-expiring'
});
//Where "sc_lm2" is the name of my cookie
Hope the helps! Took me a while to figure this out for such a simple thing
EDIT
Using PHP and Wordpress:
$json = wp_remote_get("http://api.soundcloud.com/users/[user_id]/tracks.json?client_id=[client_id]");
$soundcloudData = json_decode($json['body'],true);
(substitue cURL functionality if you're not using Wordpress). #krafty I assume you would just change the endpoint from "/tracks" to "/users" but I can't say I have ever really needed to grab anything but tracks using the Soundcloud API. Hope this helps, though I'm not sure I fully understand what it is that you are trying to accomplish (or rather, how exactly you're going about it) - are you trying to allow user logins? If you want to explain fully what you're trying to accomplish and the steps you're taking I'd be happy to take a crack at it.
Yep, this is the way to do it, officially. :)
For the Next SoundCloud site, we store the token in localStorage (until the user logs out, of course). In each AJAX request to the API from the front end, we put the oauth token in a request header:
jqXHR.setRequestHeader('Authorization', 'OAuth ' + the_oauth_token);

Facebook PHP SDK - getLoginUrl() - state value

I am using the PHP SDK getLoginUrl() function which works perfectly to log the user in. Once the user is redirected back to my page, the URL can come in two forms, see in the following link subsection 3: http://developers.facebook.com/docs/authentication/server-side/
Part of the return URL is a ?state= value. This value is supposed to be used to prevent Cross Site Request Forgery: http://developers.facebook.com/docs/reference/dialogs/oauth/
Though, using the getLoginUrl() method I can never set a state value as it is not one of the parameters: http://developers.facebook.com/docs/reference/php/facebook-getLoginUrl/
So how can I utilize the state-value to log a user into facebook and prevent CSRF?
So how can I utilize the state-value to log a user into facebook and prevent CSRF?
This is being automatically handled by the Facebook PHP SDK. If you were about to write your own API calls to Facebook, you would need to submit the state manually (if desired) as per Facebook's OAuth documentation.
When you create a login url with BaseFacebook::getLoginUrl(), the first thing the function does is to establish CSRF token state1, which creates a hash using PHP's core mt_rand(), uniqid() and md5() functions and also stores the value as a session variable.
When the user gets redirected back to your page the, FBSDK checks if the submitted state matches the state value in the session. If the values indeed match, the state is cleared from the Facebook object and from the session, so all subsequent getLoginUrl() requests would get a new state variable.2
Theoretically you could use your own state value with FBSDK by writing it to fb_<your_app_id>_state session variable before constructing the Facebook-object, as the BaseFacebook's constructor and establishCSRFTokenState() both check if the state already exists in the session.
But that would probably introduce more complexity than is necessary.
see BaseFacebook::establishCSRFTokenState()
see BaseFacebook::getCode()

Refresh expired access tokens using serverside flow automatically

Well there seems to be quite a bit of confusion on this topic and I am struggling to get a clear answer, so here is my question...
I am using the serverside flow to obtain access tokens for my web app, I previously used offline_access which is now being depreciated so I need a way to refresh the token in the following situations:
1) User changes FB password
2) Token expires naturally
My app posts results to users FB walls so the refresh needs to be done automatically by our server (no cookies or OAuth dialogs)
I thought I could try to use the new endpoint described here
http://developers.facebook.com/roadmap/offline-access-removal/
, with the following piece of code (Java):
public static String refreshFBAccessToken(String existingAccessToken)
throws Exception{
//Currently not working
String refreshUrl = "https://graph.facebook.com/oauth/access_token?
client_id="+FacebookApp.appId+"
&client_secret="+FacebookApp.appSecret+"
&grant_type=fb_exchange_token
&fb_exchange_token="+existingAccessToken;
URL url = new URL(refreshUrl);
URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(),
url.getQuery(), null);
String result = readURL(uri.toURL());
String[] resultSplited = result.split("&");
return resultSplited[0].split("=")[1];
}
But this doesnt seem to work (I get a response 400), and when I re-read the documentation it seems this endpoint is used for tokens obtained using the client-side flow only...
So what about the serverside flow....?
Can someone tell me if the approach above is correct or there is another way?
Many thanks
From what I understand there is no server side flow for refreshing tokens.
The refresh token call needs to include the response of the user authentication process which is a short lived token.
You will need to include the refresh token process as part of the user login flow or if this doesn't work for your setup you will need to email the user asking them to come back!
I dont know java but syntax is very much like C#, so I can say,you are doing everything right.
But I doubt what does this function readURL do ?
If it works like get_file_contents() of php (i.e. if it does an HTTP get) , I guess thats not a right way to do .
Based on my experience on google's refresh token method, I think you should do an HTTP POST instead of HTTP GET to given url.

How to deal with expired access_token

The case:
We plug-in FB JS and init it with FB.init(). This call creates fbsr_NNNNN cookie. The cookie has session-limited expiration date (until browser is closed). We call FB.init() only once in this example. After that we call the pages that don't contain FB.init() invocations so it doesn't have a chance to renew the access_token
We perform authentication and make some server-side (PHP FB SDK) call, like /me
Wait for 30 minutes or something until FB session expires
Perform the /me request again and see "An active access token must be used to query information about the current user."
This happens because current php sdk implementation:
public function getSignedRequest() {
if (!$this->signedRequest) {
if (isset($_REQUEST['signed_request'])) {
$this->signedRequest = $this->parseSignedRequest(
$_REQUEST['signed_request']);
} else if (isset($_COOKIE[$this->getSignedRequestCookieName()])) {
$this->signedRequest = $this->parseSignedRequest(
$_COOKIE[$this->getSignedRequestCookieName()]);
}
}
return $this->signedRequest;
}
just takes the access_token from cookies as-is and in case of exception it doesn't clear it. So the code has no chance to return into normal workflow without manual cookie removing. Yes, if I delete the cookie - the code starts to work again (as long as there is no saved access_token and library fetches the new actual one).
So what workaround for this issue would you propose? What do you use? Do you think it is a bug?
UPD: seems like there is a possible workaround: to extend Facebook class and override the method that cleans persistent storages. For details look at discussion to the answer http://facebook.stackoverflow.com/a/8294559/251311
But I'm personally still sure that FB SDK should handle it without any additional hacks
First: I have no experience with Facebook itself, but the OAuth 2 RFC specifies a refresh_token - consider implementing it.
Second: Facebook returns an error, right? If that error occurs just unset the cookie. If that doesn't work with your current implementation you're doing something wrong - pretty much every Twitter library I have seen (also uses OAuth, albeit 1.0a) uses its own HTTP wrapper. Rather than giving back an URL to request you simply execute the request yourself.
Third: What if you simply set a timeout on the cookie? I'm rather sure OAuth also gives you an expires_in value, simply use it (do take 5 seconds off this value, because of network lag etc).