I'm currently building out an application using ZF and want to integrate Facebook Connect - had some questions about architecture.
Functionality:
All logins go through FB connect, there is no other login form. When a user first clicks 'Connect', the application needs to create an entry for them in the database to tie extra data to (reviews, profile, etc).
The header should have a 'Login with FB Connect' button on all pages if the user isn't logged in, and a welcome message/picture/profile completion % bar if he or she is.
Architecture
As far as I can see, there's two components - a JS call that handles the login, redirect, and cookie writing (fb:login-button) and some PHP that reads the cookie. I'm a little confused about what should go where.
The login button is via JS, so I've loaded it in my layout.phtml and handled the cookie check via a View Helper - the helper retrieves the cookie and returns it to the view. That's fine, but I need to handle things like checking if the user exists and adding new users to the database - that doesn't seem like something that belongs in a helper.
I refactored it to include the cookie PHP in the user controller, but I need the cookie in the view, which is rendered by a different controller. I called a view action helper, but that seems to be more for returning partial pages, and I can't get the cookie array variable out of it.
I'm fairly new to MVC, and it seems like I'm missing something fairly obvious - thoughts?
Code is right off the FB developers site:
class Zend_View_Helper_FacebookCookie extends Zend_View_Helper_Abstract
{
public function facebookCookie()
{
$FACEBOOK_APP_ID = 'xxx';
$FACEBOOK_SECRET = 'xxx';
$cookie = $this->getFacebookCookie($FACEBOOK_APP_ID, $FACEBOOK_SECRET);
//Zend_Debug::dump($cookie);
return $cookie;
}
public function getFacebookCookie($app_id, $application_secret)
{
$args = array();
parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
ksort($args);
$payload = '';
foreach ($args as $key => $value) {
if ($key != 'sig') {
$payload .= $key . '=' . $value;
}
}
if (md5($payload . $application_secret) != $args['sig']) {
return null;
}
return $args;
}
}
and in the view:
if ($cookie) { ?>
Your user ID is <?= $cookie['uid'] ?>
<?php } else { ?>
<fb:login-button></fb:login-button>
<?php } ?>
The way I've implemented this in the past is via a single preDispatch plugin -- it checks the status of the user with the Facebook API and then modifies their local state (i.e. logged in / not logged in) and stores that state using Zend_Auth and/or Zend_Session.
If the user is authenticated with Facebook but not yet in your database, then the preDispatch plugin creates the record in your database before setting their local state.
Then, you can query Zend_Auth later in the application to determine whether the user is logged in.
The benefit here is that if you implement other authentication mechanisms (OpenID or others) you can simply create another plugin to do this and you don't have to modify the rest of your the user is ultimately 'locally' authenticated by Zend_Auth).
Related
I have a Progressive Web App built with Angular 4.
My Problem is the Fb login dialog does not close automatically when used from the home screen app. It works perfectly fine when opened in chrome browser but when i use it from installed home screen app the dialog window opens asks for permissions, after all permission is given the dialog goes blank and does not close or redirects back to the app.
It seems like if i change the "display" in manifest.json to "browser" it works but does not work when "display" is in "standalone".
I have searched all over but no success.
Thanks
When your app is installed as a progressive web app, the Facebook login popup and the parent window (your app) can't communicate the way they do in a desktop environment.
You'll face this problem in other scenarios as well, e.g. when your app is launched using in-app browsers like Instagram's or Facebook's.
You can implement a server-based flow as suggested by another answer. You can also implement a custom redirect flow on the client side.
A custom redirect flow works without popups. When your user clicks a "Login" button, he/she is instead redirected to a Facebook login screen. After logging in, the user is directed back to your app using a URL that contains everything you need to verify the success or failure of the authentication.
How to create a custom redirect flow is described in the Facebook SDK documentation, under "Manually Building a Login Flow":
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
The idea is as follows:
You direct your users to the Facebook login dialog (not the popup launched by FB.login()), e.g. when they click a button:
https://www.facebook.com/v2.11/dialog/oauth?
client_id={app-id}
&redirect_uri={redirect-uri}
&response_type=token
&state={state-param}
If the user successfully grants your app the necessary permissions, he/she is redirected back to your app (the location is determined by {redirect-url}):
{redirect-uri}?code=<facebook-code>&state={state}#_=_
Then you app needs to figure out whether the login was successful or not. There are two ways to do this:
A) Read the query string of the current URL (see the code=<code> part -- this can be used to retrieve additional info about your user, or it can be sent to your backend server).
B) Use the Facebook SDK do retrieve the login status using FB.getLoginStatus(yourCallback)
If you're already using the SDK and FB.login(yourCallback), you can add yourCallback to FB.getLoginStatus as well. You can also support both a popup flow and a redirect flow depending on the user's device.
I suggest reading through the documentation entry I posted above for further information and options.
I kind of figured out a way to do this for FB. Used FB PHP SDK to generate a login url and opened the url on a child window and the whole login dialog & permissions thing happens and it redirects to my redirect_url where I pass the necessary data to parent window using window.opener.postMessage.
My redirect_url page is setup like this:
<?php
if ( ! session_id()) {
session_start();
}
require_once "vendor/autoload.php";
$_SESSION['FBRLH_state'] = $_GET['state'];
$fb = new Facebook\Facebook([
'app_id' => 'FBID', // Replace {app-id} with your app id
'app_secret' => 'FBSECRET',
'default_graph_version' => 'v2.2',
]);
$helper = $fb->getRedirectLoginHelper();
try {
$accessToken = $helper->getAccessToken();
} catch (Facebook\Exceptions\FacebookResponseException $e) {
echo 'Graph returned an error: ' . $e->getMessage();
exit;
} catch (Facebook\Exceptions\FacebookSDKException $e) {
echo 'Facebook SDK returned an error: ' . $e->getMessage();
exit;
}
if ( ! isset($accessToken)) {
if ($helper->getError()) {
header('HTTP/1.0 401 Unauthorized');
echo "Error: " . $helper->getError() . "\n";
echo "Error Code: " . $helper->getErrorCode() . "\n";
echo "Error Reason: " . $helper->getErrorReason() . "\n";
echo "Error Description: " . $helper->getErrorDescription() . "\n";
} else {
header('HTTP/1.0 400 Bad Request');
echo 'Bad request';
}
exit;
}
$oAuth2Client = $fb->getOAuth2Client();
$tokenMetadata = $oAuth2Client->debugToken($accessToken);
$tokenMetadata->validateAppId('FBID'); // Replace {app-id} with your app id
$tokenMetadata->validateExpiration();
if ( ! $accessToken->isLongLived()) {
try {
$accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
} catch (Facebook\Exceptions\FacebookSDKException $e) {
echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
exit;
}}
$_SESSION['fb_access_token'] = (string)$accessToken;
$tk = $_SESSION['fb_access_token'];
?>
<?= $tk;?>
<script type="text/javascript">
window.opener.postMessage({token: '<?= $tk;?>'}, '*');
window.close();
</script>
In the parent window I have an eventListener which is listening to the postMessage using window.addEventListener("message", receiveMessage, false);and the receiveMessage function handles all the data as required. So any web app using standalone mode should be able to get data from child window.
Window postmessage can be used to pass data between cross-origins. More details on postMessage are here: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
for my facebook quiz app I need to collect the facebookID of
each participating user. My app is in an iframe of another App.
The "facebook-files" I put in the third party folder.
In my controller I invoke it like so:
$this->ci->load->file(APPPATH.'/third_party/facebook.php');
// Connect to Facebook API
$this->facebook = new Facebook(array('appId' => $this->config->item('appkey'), 'secret' => $this->config->item('appsecret')));
It seems not possible to get the facebookID from a participant.
If try the this:
try {
$user_id = $this->facebook->api('/me');
//$signed_request = $_REQUEST["signed_request"];
//$user_id = $signed_request['user_id'];
//print_r( $user_id) ;
} catch ( FacebookApiException $e ) {
error_log( $e );
$user_id = 100;
}
the $user_id is 100.
Can you get me start how to retrieve the right facebookid ?
What would be the right way? Authentication process ?
Thanks in advance!
Ansgar
to get the Facebook ID with PHP you need to use the method $fb->getUser() : https://developers.facebook.com/docs/reference/php/facebook-getUser/
Although, for this to work, the user need to connect into your app. This can be achieve in multiple way, but if you only do it with PHP you can use the getLoginUrl method to get the URL where the user will be asked to accept to login with your app (https://developers.facebook.com/docs/reference/php/facebook-getLoginUrl/). This is also achievable with JS SDK (which is my personal preferred way)
All documentation on Login Dialog can be found here: https://developers.facebook.com/docs/opengraph/authentication/
The key concept here is that the user is anonymous until you ask them to connect in your app.
Is there a way to prevent users from installing my facebook application to their pages, using the link below?
https://www.facebook.com/add.php?api_key=API_KEY&pages
Note: I do not want to disable my application, I just want to disable new installations.
The quickest way to achieve this is to parse the signed_request and compare the page information to a list of page ids you have authorised to use the app.
A very simple implementation would be along the lines of
<?php
$app_secret = 'APPSECRET';
$signed_request = parse_signed_request($_REQUEST['signed_request'], $app_secret);
$page_whitelist = array(PAGEID1, PAGEID2);
if (in_array($signed_request['page']['id'], $page_whitelist)) {
// do stuff
} else {
// output some error message
}
https://gist.github.com/4157347
I know there are many Q&A on here about this but I can't seem to make sense of them because they don't show full code examples or are for doing something different than what I'm trying to do.
I have a simple facebook welcome-test tab. Not a canvas app. I want to be able to get the users id but have to make it prompt them for access to that info before it will show up in the signed request.
So what I'm looking for is a code example for a simple welcome tab. I don't need a login page or anything like that since this will be in the fan content page so they will have to be logged in already. I just want that dialog box to popup asking them for permission so I can get the userid.
Thanks
Edit:
I wanted to add that I just figured out my problem but it's not really an answer to this question. I figured out how to get the user id without asking the user for permission to access their profile. It was as easy as adding this code to the page and it works in a tab and canvas app the same way. getUser() by its self returns 0 but since the user is already logged in to be seeing this content, when getUser() returns 0, then the code simply gets the user id from the API method. Now that I finally figured this out it seems simple.
$session = $facebook->getUser();
if (!$session) {
$url = $facebook->getLoginUrl(array(
'canvas' => 1,
'fbconnect' => 0
));
echo "<script type='text/javascript'>top.location.href = '$url';</script>";
} else {
try {
$uid = $facebook->getUser();
$me = $facebook->api('/me');
$updated = date("l, F j, Y", strtotime($me['updated_time']));
echo "Hello " . $me['name'] . "<br />";
echo "You last updated your profile on " . $updated;
} catch (FacebookApiException $e) {
echo "Error:" . print_r($e, true);
}
}
I'd still like to know if there is a way to make the extended permissions dialog show for a tab app so it will be good to see solutions for php sdk.
you need to prompt them for allowing your application to pull data on the user
this can be done using the JS SDK's function FB.login(). you can have more details in the facebook documentation : http://developers.facebook.com/docs/reference/javascript/FB.login/
notice that the popup can be opened only on mouseclick or keyup events - otherwise popup blockers will block it.
you will also have to set a domain and site url to the application in the developers site, and init the facebook js library (the loading paragraph in - http://developers.facebook.com/docs/reference/javascript/)
<div onclick="login();">click here to login</div>
<script type="text/javascript">
function afterLogin(){
alert("user logged");
}
function login(){
FB.getLoginStatus(function(response){
if(response.status=="connected")
{
afterLogin();
} else {
FB.login(function(response){
if(response.status=="connected")
{
afterLogin();
}
});
}
});
}
</script>
I don't think facebook allows this... Facebook does not want a user's tab to be able to detect who is looking at it, and possibly displaying information to a user's friends that isn't displayed to the user.
I can be wrong :)
I have a website and within it, Social Engine 4.1.4 is just a sub-module. I am using the login system of Social Engine in my website. When a user login and then comes back to the site homepage, I want to show his login status. I mean, that if the user is logged in SE4, then I should greet him with his name. How can I do the same.
My site is not using Zend Framework. Since the session data is stored in the table engine4_core_session, I was thinking of a way to decode the serialized data column in some way by getting the specific user row through the *session_id*. I'm not getting the way to decode the data.
Hi you get de name in socialengine 4 :
in the controller you get this:
public function indexAction()
{
$viewer = Engine_Api::_()->user()->getViewer();
$fields = Engine_Api::_()->fields()->getFieldsValuesByAlias($viewer);
$this->view->name = $fields["first_name"] ." ". $fields["last_name"];
$viewer->getTitle();
}
in your view print this:
<h1><?php echo $this->name; ?></h1>