Session in Laravel does not work inside the Facebook Application Iframe - facebook

I have a facebook app that runs inside an iframe. After the user allow permissions from the app I'm expecting facebook to send me $_REQUEST variables that I use to retrieve user data and it is working well. The problem is after I put the users data in Laravel Session using Session::put() and then Session::save(), on the next request the session is gone. So my app cannot retrieve the Session anymore. I really don't know why during the test all of my browsers work, (Safari, Firefox, Chrome). So I put some error checking to email me everytime there is an Session Error and still I got 30+ emails per day telling that Chrome has no Session, and some users were posting about the App returning Error. I'm really struggling for this problem for days now. I've implemented some fix adding P3P headers and adding favicon on my site, still no avail.
Here is my code:
Route::filter('before', function()
{
// Do stuff before every request to your application...
header('P3P:CP="NOI DEV PSAi COM NAV OTR STP DEM HONK IDC DSP COR ADM DEVi TAIi CAO PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
//safari 3rd party fix cookie fix
if(isset($_SERVER['HTTP_USER_AGENT'])) {
if (! count($_COOKIE) > 0 && strpos($_SERVER['HTTP_USER_AGENT'], 'Safari')) {
if(strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') == false) {
session_start();
$page_url = Config::get('custom.fb_page');
if (isset($_GET["start_session"]))
die(header("Location:" . $page_url));
$sid = session_id();
if (!isset($_GET["sid"]))
{
if(isset($_POST["signed_request"]))
$_SESSION["signed_request"] = $_POST["signed_request"];
die(header("Location:?sid=" . $sid));
}
if (empty($sid) || $_GET["sid"] != $sid)
die('<script>top.window.location="?start_session=true";</script>');
}
}
}
}
}
//and the code that sets Laravel Session
Route::any('tab/(:any?)', function ($res = null)
{
//$response = some_function_to_get_token($_REQUEST['signed_request']);
if($response && isset($response['oauth_token']))
{
Session::put('my_session_for_token', $response['oauth_token']);
Session::save();
$redirect = Redirect::to('my_awesome_page');
}
else
$redirect = Redirect::to('permission');
}
EDIT
I've already tried setting $_SESSION and commenting out Session::put() but it doesn't set my $_SESSION at all. Next thing I tried was uploading a sessionchecker.php script and tried it using the browser.
heres the code for session check:
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
session_start();
if (isset($_SESSION['views']))
$_SESSION['views'] = $_SESSION['views'] + 1;
else
$_SESSION['views'] = 0;
echo '<pre>';
var_dump(session_id()); // I should stay the same
var_dump($_SESSION); // I should start at 0 and increase
echo '</pre>';
and it working as expected, Im suspecting that it has something to do with Redirect::to().
P.S. I've already tried configuring Laravel to use Cookie and File but still the error occurs.

This is not a Laravel problem. This is a classic Safari/Internet explorer 3rd party cookie problem.
Try googling for "safari cookie fix" or check out some of these questions here, for example:
Facebook Iframe App with multiple pages in Safari Session Variables not persisting

Related

The "state" param from the URL and session do not match

In facebook documantion
require('include/facebook/autoload.php'); //SDK directory
$fb = new Facebook\Facebook([
'app_id' => '***********',
'app_secret' => '***********************'
]);
$helper = $fb->getRedirectLoginHelper();
$permissions = ['email', 'public_profile']; // optional
$loginUrl = $helper->getLoginUrl('http://www.meusite.com.br/login-callback.php', $permissions);
When direct it to the url $loginUrl, the return is:
Facebook SDK returned an error: Cross-site request forgery validation failed. The "state" param from the URL and session do not match
I had the same error.
The problem occurred because I did getLoginUrl(...) before getAccessToken()
So rid of getLoginUrl(...) in redirected URL and code should works.
I had the same issue and for me that error was occurring because I did not put session_start(); in my login.php page code before calling getLoginUrl(..) and also at the top of login-callback.php page.
Just put session_start(); in your "login" page and "login-callback" page and it will work surely just like it is working for me now.
There could be 2 reason for this error:
you didn't call session_start(); before getLoginUrl call
You executed getLoginUrl again in login-callback.php, so state value regenerated and mismatched with the redirected value
Possible Fixes : I used the following configuration settings .
Enable WebAuthLogin under the advanced tab . Provide the url in the WebAuthLogin settins as same as that you provide in $loginUrl ;
For example if you use $loginUrl as https://example.com/ use that same in the WebAuthlogin Url
$loginUrl = $helper->getLoginUrl('https://example.com/', $permissions);
This problem occures also in case that you generate 2 or more login links on the same page (e.g. one for login and other for registration - even both point to the same url, they have just different labels).
Facebook SDK creates/updates $_SESSION[FBRLH_state] for each new generated loginURL. So if there are 2 generated URLs (using $helper->getLoginUrl()) then the $_SESSION[FBRLH_state] is 2-times rewritten and valid only for the last generated URL. Previous login URL becomes invalid. It means that it is not possible to generate 2 valid loginURLs. In case that 2 same URLs are generated then return the first one and avoid call of Facebook SDK for generation of second one.
I had the same problem.
The reason for this error is because --->
When "$helper->getLoginUrl" calls, it create a session variable "FB_State", and this is something to FB uses to match the token. Every-time getLoginUrl calls, it create new state. Then after user authorized and redirect back, if you codes cannot detect this event and re-run "$helper->getLoginUrl", then this error will occur.
The solution ->
refine your coding, stop run "$helper->getLoginUrl" again if authorized.
if you already rerun, then set the session variable for the token to NULL if you have, then User can re-authorize again.
when user tries re-authorize, they can remove the authorized APP once or you need to generate new link with "$helper->getReRequestUrl"
Yet, token has be called by "getAccessToken()" before the "$helper->getLoginUrl" or "$helper->getReRequestUrl" runs.
Good Luck!!!!!
Finally, looking into FB code, I discovered that the problem "Cross-site request forgery validation failed. Required param “state” missing" and similars are caused by PHP variable $_SESSION['FBRLH_state'] that for some "strange" reason when FB call the login-callback file.
To solve it I store this variable "FBRLH_state" AFTER the call of function $helper->getLoginUrl(...). Is very important to do only after the call of this function due to is inside this function when the variable $_SESSION['FBRLH_state'] is populated.
Below an example of my code in the login.php:
$uri=$helper->getLoginUrl($uri, $permissions);
foreach ($_SESSION as $k=>$v) {
if(strpos($k, "FBRLH_")!==FALSE) {
if(!setcookie($k, $v)) {
//what??
} else {
$_COOKIE[$k]=$v;
}
}
}
var_dump($_COOKIE);
And in the login-callback.php before calling all FB code:
foreach ($_COOKIE as $k=>$v) {
if(strpos($k, "FBRLH_")!==FALSE) {
$_SESSION[$k]=$v;
}
}
Last, but not least, remember also to include code for PHP session so..
if(!session_id()) {
session_start();
}
...
...
...
...
<?php session_write_close() ?>
I hope this response can help you to save 8-10 hours of work :)
Bye, Alex.
This issue was a bit confusing for me, because I had to change a line at the facebook src file:
src/Facebook/Helpers/FacebookRedirectLoginHelper.php
at the function: "validateCsrf" like this:
if ($result !== 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
And change it into:
if ($result === 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
I don't know if this makes a violation to the facebook SDK security, so I truly opened to any exlanation or recommendation for this answer.
You may also make the following changes at the facebook app manager:
add your site and callback-url into your facebook app account at:
setting->advanced:Valid OAuth redirect URIs
Don't forget to add another url with slash (/) at the end of each url and check all 4 checkboxes at Client OAuth Settings.
I had the same error. Are you using 1 file or 2? I was trying to get by using 1 file but my error was resolved when I split into login.php & fb-callback.php as the documentation recommended. My sessions were being re-written so the state was never saved properly.
Good luck!
Happens when the session in missing a needed variable.
might be caused by several things.
In my case I left the "www" out of the callback URL
You could actually be parsing the data from another domain... for example:
website.com is different from www .website.com
If you're parsing data from http ://website.com/login.php to http://www.website.com/fb-callback.php this would be a cross-domain problem and the error you are receiving would be because of that....
http ://website.com and http ://www.website.com are the same but the script identifies them as different..... hope that gives insight to the problem.

How to solve redirect loop in Impresspages Plugin

I am developing an impresspages plugin where I need a redirection. For such a thing I catch the event ipInitFinished, v.g., creating a code like this:
class Event {
public static function ipInitFinished()
{
if( !(ipIsManagementState()))
{
$site = ipHomeUrl();
$page = ipGetOption('SiteMaintenance.messagePage');
header('location: ' . $site.$page);
exit;
}
}
messagePage is a field of the plugin that store the page name to display on redirection. This page is a normal page created in impresspages environment.
However, when the plugin routine is executed the browser raise an error related to looping or recursive redirect.
How to solve this?
The problem is, that when $site.$page is loading, the same code will be executed again causing a redirect loop. You have to check if the current page is not equal to $site.$page.

Facebook page tab app session across subpages PHP SDK 4

See the full original question further down
Using the latest Facebook PHP SDK 4.4.0, in my main app page I can do the following to get a user id etc.
<?php
FacebookSession::setDefaultApplication(APP_ID, SECRET);
$helper = new FacebookRedirectLoginHelper( PAGE_URL );
$pageHelper = new FacebookPageTabHelper();
$session = $pageHelper->getSession();
echo '<p>You are currently viewing page: '. $pageHelper->getPageId() . '</p>';
// get user_id
echo '<p>User Id: ' . $pageHelper->getUserId() . '</p>';
// **depcrecated** get like status - use for likegates
echo '<p>You have '. ( $pageHelper->isLiked() ? 'LIKED' : 'NOT liked' ) . ' this page</p>';
// get admin status
echo '<p>You are '. ( $pageHelper->isAdmin() ? 'an ADMIN' : 'NOT an ADMIN' ) . '</p>';
?>
This does not work on sub pages of my app ... Why is the session (and amongst other things, the signed request) lost? How can I get them back and how can I get methods such as getUserId() from the the FacebookPageTabHelper to continue to work on sub pages?
full original question
I'm fairly new to Facebook app development and I'm having problems with session management and I just can't seem to be able to wrap my head around it. Of course it doesn't help that the official documentation is almost useless.
My problem is that the page session get lost when moving away from the apps main page to a subpage within the Facebook page tab app iframe.
I use the following PHP code to obtain the session and user id on the main (initial) app page and it works great:
<?php
FacebookSession::setDefaultApplication(APP_ID, SECRET);
$helper = new FacebookRedirectLoginHelper( PAGE_URL );
$pageHelper = new FacebookPageTabHelper();
$session = $pageHelper->getSession();
?>
But it doesn't work on sub pages :( when a user clicks on a menu item (or any other link inside the app/iframe), the session goes bye bye. Which is not ideal as I need the user id of the user to track whether or not that user has completed certain actions. Of course I could send the ID along with every request, but there must be a way to have a persisting session, no?
Is there a way to retrieve the session on a sub page in PHP? If so, how? Or do I have to load additional content using javascript? And how would that work, if I can't keep the session between requests and therefore have no way of identifying which user a request came from? How do others handle this?
What I'd like to avoid is to write my own user session management, which would solve the problem but is simply not in the budget and I was hoping I could work with what Facebook already had on offer. Especially since my app doesn't require user information/permissions of any kind.
Thanks a lot in advance for any info on this topic, greatly appreciated, going in circles here.
Edit to clarify: I thought of just saving the Facebook session in a PHP session cookie, but how would I use that to reconnect with Facebook after changing the page?
I finally managed to solve this problem. I'm not sure whether this is considered the right way or can even be a recommended way of doing this, but it works and since time is of the essence, I don't have much of a choice.
If anybody has any further ideas or suggestions, please comment.
Here's how I did it:
// store the signed request
if(isset($_REQUEST['signed_request'])) {
$_SESSION['signed_request'] = $_REQUEST['signed_request'];
} elseif($_SESSION['signed_request']) {
$_REQUEST['signed_request'] = $_GET['signed_request'] = $_POST['signed_request'] = $_SESSION['signed_request'];
}
// assign the stored signed request to REQUEST, GET and POST vars (the unsavory bit, imo)
$_REQUEST['signed_request'] = $_GET['signed_request'] = $_POST['signed_request'] = $_SESSION['signedRequest'];
FacebookSession::setDefaultApplication(APP_ID, APP_SECRET);
$accessToken = APP_ID . '|' . APP_SECRET;
$this->session = new FacebookSession($accessToken);
$pageHelper = new FacebookPageTabHelper();
$isAdmin = ($this->pageHelper->getPageData('admin')) ? $this->pageHelper->getPageData('admin') : 0;
// get pade id
echo '<p>You are currently viewing page: '. $pageHelper->getPageId() . '</p>';
// get user_id
echo '<p>User Id: ' . $pageHelper->getUserId() . '</p>';
// get admin status
echo '<p>You are '. ( $isAdmin ? 'an ADMIN' : 'NOT an ADMIN' ) . '</p>';

fetch emails with php lib that suports oauth 2

I'm trying to get mail(imap) from google by oauth authorization . I have got the authorization to work, but I can not retrieve the emails. As I understand it should be posible. But Google does not have any api to retrieve mail(?). However, I found the following:
https://developers.google.com/google-apps/gmail/oauth_overview
That says:
Accessing mail using IMAP and sending mail using SMTP is often done
using existing IMAP and SMTP libraries for convenience. As long as
these libraries support the Simple Authentication and Security Layer
(SASL), they should be compatible with the OAuth mechanism supported
by Gmail. In addition to using a library which supports IMAP and SMTP,
developers also will want to use one of the many existing libraries
for handling OAuth
Do anyone know a existing library that i can use and that has some documentation as well. Im using google-api-php-client.
The code
session_start();
ini_set('display_errors',1);
error_reporting(-1);
require_once '../../src/apiClient.php';
$client = new apiClient();
$client->setApplicationName('Mailendar');
$client->setScopes("http://www.google.com/m8/feeds/");
// Documentation: http://code.google.com/apis/gdata/docs/2.0/basics.html
// Visit https://code.google.com/apis/console?api=contacts to generate your
// oauth2_client_id, oauth2_client_secret, and register your oauth2_redirect_uri.
$client->setClientId('secret');
$client->setClientSecret('secret');
$client->setRedirectUri('secret');
$client->setDeveloperKey('secret');
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}
if (isset($_SESSION['token'])) {
echo "token is set";
$client->setAccessToken($_SESSION['token']);
}
if (isset($_REQUEST['logout'])) {
unset($_SESSION['token']);
$client->revokeToken();
}
if ($client->getAccessToken()) {
MAGIC HAPPENS HERE!!!...but is unkown for me ofc
// The access token may have been updated lazily.
$_SESSION['token'] = $client->getAccessToken();
} else {
$auth = $client->createAuthUrl();
}
if (isset($auth)) {
print "<a class=login href='$auth'>Connect Me!</a>";
} else {
print "<a class=logout href='?logout'>Logout</a>";
}
Thanks!
Google doesnt allow you to retrieve mail with oauth 2.0 at the moment.
Now you can fetch mails using OAuth.
Implemented a simple library.
Delete mail function is not yet added. But you can take a look if it satisfies your need.
Try example. https://github.com/vmuthal/VivOAuthIMAP

Facebook Credits Example on App Engine?

Are there any examples of using facebook credits on Google App Engine?
I found this blog post , but it's not complete
http://blog.suinova.com/2011/01/integrating-facebook-credits-api-into.html
I got the sample runwithfriends example working on the App Engine, tried to expand it with Credits, no luck so far.
Also searched for the FB developer forums, got nothing.
Any resources you can point me to?
What's not working:
1) When I click on the "pay with Facebook" button, I get an "Application Error" , without any error code.
-Checked the javascript console
-Checked the fb app settings
-Tried on local server and production server
2) The callback.py isn't complete, because i could not parse the signed request (no code available in py for me to learn from)
3) What I basically did was to add code from Suinova Designs (link above) to the existing Run With Friends app code. Didn't turn out as expected.
my code so far:
//payment_page.html
<html>
<table>
<tr><th>Name</th><th>Price</th><th> </th></tr>
<tr><td>Something to buy</td><td>10 FC</td><td><a href="" onclick="return buyit();">
<img src="http://www.facebook.com/connect/button.php?app_id=215638625132268&feature=payments&type=light_l" />
</a></td></tr>
</table>
// javascript
function buyit(){
FB.ui({
method:'pay',
purchase_type:'item',
order_info:{
item_id:'myitem',
title:'Something to buy',
price:2,
description:'Whatever',
image_url:'http://www.facebook.com/images/gifts/21.png',
product_url:'http://www.facebook.com/images/gifts/21.png'}
},
function(resp){
if(resp.order_id) window.top.location='http://apps.facebook.com/runwithfriends trial'; else alert(resp.error_message);
});
return false;
}
//callback.py
class FacebookPaymentRequest(webapp.RequestHandler):
def post(self):
signed_request = parse_signed_request(self.request.get('signed_request'),conf.FACEBOOK_APP_SECRET)
payload = signed_request['credits'] #credits:{buyer:int,order_id:int,order_info:{},receiver:int}
order_id = payload['order_id']
method = web.request.get('method')
return_data = {'method':method}
if method == 'payments_get_items':
order_info = payload['order_info'] #order_info:{item_id:'',title:'',description:'',price:0,image_url:'',product_url:''}
item = simplejson.loads(order_info) #needs to convert JSON string to dict
#check item,price,etc and reply
return_data['content'] = [item]
elif method == 'payments_status_update':
status = payload['status']
return_data['content'] = {'status':'settled','order_id':order_id}
if status == 'placed':
#just return settled status to Facebook, this may be called twice
order_details = simplejson.loads(payload['order_details'])
#check and log here
elif status == 'settled':
order_details = simplejson.loads(payload['order_details'])
#order_details:{order_id:0,buyer:0,app:0,receiver:0,amount:0,update_time:0,time_placed:0,data:'',items:[{}],status:'placed'}
buyer = order_details['buyer']
item = order_details['items'][0]
#now save this transaction into our database
else:
#may be refunded, canceled, log the activity here
return_data['content']['status'] = status
self.response.out.write(simplejson.dumps(return_data))
Your python code looks fairly normal so I would guess that you are simply having trouble with your authorization. Depending upon how you authorize (a process a fair amount more complicated that the credits system), you are likely being given a signed request that is only partially authorized... meaning you are authorized to access only certain parts of facebook, but generally not authorized to access the active/logged-in user (i.e. me).
You can verify this by determining if you signed_request is a full 80+ characters (as opposed to around 40). Generally I try to authenticate by deciphering the profile (signed_request), if that fails then I try to use a previously stored cookie, then if that fails I try to relogin the user. I determine failure by placing try/except around my calls to get a "me" object through the GraphAPI.