Facebook PHP SDK - getLoginUrl() - state value - facebook

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()

Related

How to configure ASP.NET Identity with Facebook login - strict redirect

In March of 2018 Facebook began requiring "Strict Mode" for redirect URIs. This means any redirect URI needs to be known in advance, and added to your app's profile (Valid OAuth Redirect URIs). The problem is when the redirect URI contains a dynamic parameter, like a state variable, guid, or user id, etc.
The answer I have found on many websites is that you can hold dynamic state in a "state" parameter. So if your allowed redirect uri is "www.example.com/signin" then this will also allow "www.example.com/signin?state=12344". However, this seems to be no longer allowed. The state parameter no longer seems to be ignored by this strict rule - it fails validation (there is a valid url checker on your facebook app settings page). To test this, I simply put "a.com" in the allow uri field, and "a.com?state=x" in the Redirect URI Validator, and it fails. Without the ?state-x it succeeds. What am I missing?
So I have two questions:
1) First, how to I find out what redirect url my app is actually sending to FB? I can't sniff my traffic since it's https (also required now by fb). I think I know what it is (https://www.example.com/signin-facebook?state=xxxxxx) but I can't be sure and there is no way to verify. The logic that calls FB is wrapped up in the MS Identity library.
2) Second, if it's the state parameter that is causing my login fails, is there any way to disable that in my ASP.NET Core app, or allow it in FB?
Just to clarify, here are two images showing that without the state parameter, the url is valid, but adding the state parameter makes it an invalid URL. Clearly the 'state' parameter is not being ignored by this strict rule checker, as many people have claimed. If I add the state param url to the list of allowed urls, it works but only with that exact state value, not with any different value.
The state parameter is dynamic to begin with (or at least it should be, because its original purpose is CSRF protection), so it is not taken into account when the URL is checked for a “strict” match to the one you have configured.
If you want to use
https://www.example.com/signin-facebook?state=xxxxxx
then configure
https://www.example.com/signin-facebook
as your Valid OAuth Redirect URI.
If you have other (static!) parameters besides the dynamic state, then those must be input into the field as well. You want to use
https://www.example.com/signin-facebook?action=foobar&state=xxxxxx
then the URL in your settings needs to be
https://www.example.com/signin-facebook?action=foobar
And since, as mentioned, the original purpose of this parameter is CSRF protection, it might still be a good idea to add a “random“ component to it, if the actual value you are trying to transport via it is “guessable”, or from a limited range of pre-defined values only.
In that case, I would probably go with an encoded JSON object as the parameter value -
state={"mystate":"foobar","random":8473628}
(Don’t forget to apply proper URL encoding, if your system doesn’t do that automatically.)

Why are my Facebook access tokens expring in Javascript

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.

pass data back to facebook app after authorisation

I am authotizing my app in the following way:
// authorize app!
$('#authApp').click(function(){
var oauth_url = 'https://www.facebook.com/dialog/oauth/';
oauth_url += '?client_id=#{app.id}';
oauth_url += '&redirect_uri=' + encodeURIComponent('https://www.facebook.com/pages/null/#{fbPageId()}/?sk=app_#{app.id}');
oauth_url += '&scope=user_likes,user_photos';
oauth_url += '&app_data=7B%27game%27%3A+%27key%27%7D';
oauth_url += '&state=sbSbsbSb';
As you can see I am setting the 'state' param as part of the query string.
Now when the user authorizes the app he is redirected to the redirect url.
However the data passed in the state parameter is not posted to my app nor is it part of my app's iframe query string. I was expecting to find it as part of the signed request, but no. This is the deserialized signed request posted back after authorisation
{ algorithm: 'HMAC-SHA256',
expires: 1348927200,
issued_at: 1348921162,
oauth_token: 'AAA...',
page: { id: '490...', liked: true, admin: false },
user: { country: 'ec', locale: 'en_US', age: { min: 21 } },
user_id: '1...' }
I do see that the state is included of the parent page's query string. I need to access that parameter from my app (running inside an iframe). I believe that I cannot just access the parent page's window location because of same origin policy restrictions.
I have read through the documentation and searched online. Persisting data across an app authotization needs to be done using the state parameter. However it is nowhere stated how to retrieve that state param once redirected back to your app.
This is from the facebook doc's regarding the state param:
A unique string used to maintain application state between the request
and callback. When Facebook redirects the user back to your
redirect_uri, this parameter's value will be included in the response.
You should use this to protect against Cross-Site Request Forgery.
Am I supposed to get the state data back from the parent's page query string?
Or am I doing something wrong?
* EDIT *
I am storing the user to user request Id in the state parameter. For example A invites B to participate in the app via a facebook request. Once B authorises the app A needs to be rewarded. So I need to know that B came to the app following A's invitation. Therefore I store the requestId in the state param, so once B has authorised the app I can take appropriate action.
* EDIT 2 (SOLUTION) **
If your redirect_uri is pointing to the Page Tab URL then facebook will NOT send back the state parameter! It will only be sent back if you redirect to the Canvas URL !!!!!
The Facebook documentation you referenced is a bit confusing. The only thing you should be doing with the state parameter is making sure you are not a victim of CSRF. Facebook's server side authentication flow gives an example of this in PHP. In short, you should be storing the state value in the session and then verifying that the session value is the same as what Facebook passes back to you in the request. The key line in their PHP example is:
if($_SESSION['state'] && ($_SESSION['state'] === $_REQUEST['state'])) {
// Continue with application logic here because state matches.
// Otherwise, exit immediately because you're a victim of CSRF!
So back to your problem. From your redirect URL and the response you are getting, it's obvious your app is on a Facebook Page Tab. See the authentication flow for page tabs for how you should be doing this. Note they are not using the state parameter in step 2 and that the state parameter is never mentioned in page tab authentication flow. So even if you wanted to use the state parameter for something other than its intended use, you are out of luck.
Based on your edits, I suggest you check out the documentation on requests. Note that the user clicking on the request will be redirected to your canvas app, not the page tab. "The canvas URL will also contain an additional GET parameter request_ids, which is a comma delimited list delimited list of Request IDs that a user is trying to act upon." So there is no need for you to be trying to do this yourself.
Am I supposed to get the state data back from the parent's page query string?
No, not when authenticating within a canvas/page tab app. The only query string parameter that gets passed to your app in this scenario is the content of the app_data parameter.
But you don’t need the state parameter in this scenario – verifying the signed_request is absolutely sufficient, because it’s signed with your app secret, that only you and Facebook know. So that is enough protection against “manipulated” requests right there already.
See https://developers.facebook.com/docs/authentication/canvas/ resp. https://developers.facebook.com/docs/authentication/pagetab/ for more details. (And see how they do not mention the state parameter at all.)
Edit:
I am storing the user to user request Id in the state parameter. For example A invites B to participate in the app via a facebook request. Once B authorises the app A needs to be rewarded. So I need to know that B came to the app following A's invitation. Therefore I store the requestId in the state param, so once B has authorised the app I can take appropriate action.
That’s a misuse of the state parameter … it’s supposed to achieve something completely different (CSRF protection, as the docs say).
While this might work in your scenario – why are you not using the app_data parameter to transmit this piece of information? That’s the designated way of transferring info to canvas/page tab apps.

Retrieving the action-instance-id from Facebook given the URL

tl;dr: No, there isn't a way.
Calling publish_action using the JS SDK is actually pretty straightforward. However (from the little info I gleaned from reading the documentation), there's no way for me to query facebook to have it return the action instance ID for an object that I have already published... is there?
Example:
User A loads the page, and the page sends an FB.api call to /me/news.reads, which returns an action instance ID.
User A reloads the page, and the page again sends an FB.api call to /me/news.reads, but this time, the Graph API returns:
{
error: {
code: 3501,
message: 'blahblahblah... already associated... blah blah'
type: 'OAuthException'
}
}
Pretty standard stuff, and expected, since I turned off the ability to publish the same URL multiple times.
Now then, is there any way for me to retrieve a previously published action instance ID from the Graph API by passing in the URL, or is it up to me to handle the returned action instance ID (from the original publication attempt) and save it to a database? I was hoping I wouldn't have to do that...
No, there is no way to retrieve instances of published actions other than:
Accessing action by id:
http://graph.facebook.com/ACTION_ID
Accessing all instances published by specific user:
http://graph.facebook.com/USER_ID/NAMESPACE:ACTION (NAMESPACE:ACTION may be replaces by the name of one of built-in actions like news.reads, music.listend, etc.
If you want to access details of published actions connected/referencing specific object you'll need to save that data on your end for later usage.

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).